diff options
| author | 2023-06-10 22:42:54 -0400 | |
|---|---|---|
| committer | 2023-06-14 16:34:14 -0400 | |
| commit | de9100ea81adec8c008b9bb9376541d2410ef4e8 (patch) | |
| tree | 5b52ed8094c79f264b62c87950aa48a625e68794 /src/android | |
| parent | Merge pull request #10726 from t895/emulation-nav-component (diff) | |
| download | yuzu-de9100ea81adec8c008b9bb9376541d2410ef4e8.tar.gz yuzu-de9100ea81adec8c008b9bb9376541d2410ef4e8.tar.xz yuzu-de9100ea81adec8c008b9bb9376541d2410ef4e8.zip | |
android: Add Picture in Picture / Orientation
Diffstat (limited to 'src/android')
15 files changed, 336 insertions, 66 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index b474ddb0b..e31ad69e2 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -54,6 +54,8 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 54 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | 54 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |
| 55 | android:theme="@style/Theme.Yuzu.Main" | 55 | android:theme="@style/Theme.Yuzu.Main" |
| 56 | android:screenOrientation="userLandscape" | 56 | android:screenOrientation="userLandscape" |
| 57 | android:supportsPictureInPicture="true" | ||
| 58 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" | ||
| 57 | android:exported="true"> | 59 | android:exported="true"> |
| 58 | 60 | ||
| 59 | <intent-filter> | 61 | <intent-filter> |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index caf660348..e2eab3105 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -4,14 +4,23 @@ | |||
| 4 | package org.yuzu.yuzu_emu.activities | 4 | package org.yuzu.yuzu_emu.activities |
| 5 | 5 | ||
| 6 | import android.app.Activity | 6 | import android.app.Activity |
| 7 | import android.app.PendingIntent | ||
| 8 | import android.app.PictureInPictureParams | ||
| 9 | import android.app.RemoteAction | ||
| 10 | import android.content.BroadcastReceiver | ||
| 7 | import android.content.Context | 11 | import android.content.Context |
| 8 | import android.content.Intent | 12 | import android.content.Intent |
| 13 | import android.content.IntentFilter | ||
| 14 | import android.content.res.Configuration | ||
| 9 | import android.graphics.Rect | 15 | import android.graphics.Rect |
| 16 | import android.graphics.drawable.Icon | ||
| 10 | import android.hardware.Sensor | 17 | import android.hardware.Sensor |
| 11 | import android.hardware.SensorEvent | 18 | import android.hardware.SensorEvent |
| 12 | import android.hardware.SensorEventListener | 19 | import android.hardware.SensorEventListener |
| 13 | import android.hardware.SensorManager | 20 | import android.hardware.SensorManager |
| 21 | import android.os.Build | ||
| 14 | import android.os.Bundle | 22 | import android.os.Bundle |
| 23 | import android.util.Rational | ||
| 15 | import android.view.InputDevice | 24 | import android.view.InputDevice |
| 16 | import android.view.KeyEvent | 25 | import android.view.KeyEvent |
| 17 | import android.view.MotionEvent | 26 | import android.view.MotionEvent |
| @@ -27,6 +36,8 @@ import androidx.navigation.fragment.NavHostFragment | |||
| 27 | import org.yuzu.yuzu_emu.NativeLibrary | 36 | import org.yuzu.yuzu_emu.NativeLibrary |
| 28 | import org.yuzu.yuzu_emu.R | 37 | import org.yuzu.yuzu_emu.R |
| 29 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | 38 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding |
| 39 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | ||
| 40 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | ||
| 30 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | 41 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel |
| 31 | import org.yuzu.yuzu_emu.model.Game | 42 | import org.yuzu.yuzu_emu.model.Game |
| 32 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 43 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| @@ -50,6 +61,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 50 | private var motionTimestamp: Long = 0 | 61 | private var motionTimestamp: Long = 0 |
| 51 | private var flipMotionOrientation: Boolean = false | 62 | private var flipMotionOrientation: Boolean = false |
| 52 | 63 | ||
| 64 | private val actionPause = "ACTION_EMULATOR_PAUSE" | ||
| 65 | private val actionPlay = "ACTION_EMULATOR_PLAY" | ||
| 66 | |||
| 53 | private val settingsViewModel: SettingsViewModel by viewModels() | 67 | private val settingsViewModel: SettingsViewModel by viewModels() |
| 54 | 68 | ||
| 55 | override fun onDestroy() { | 69 | override fun onDestroy() { |
| @@ -120,6 +134,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 120 | super.onResume() | 134 | super.onResume() |
| 121 | nfcReader.startScanning() | 135 | nfcReader.startScanning() |
| 122 | startMotionSensorListener() | 136 | startMotionSensorListener() |
| 137 | |||
| 138 | buildPictureInPictureParams() | ||
| 123 | } | 139 | } |
| 124 | 140 | ||
| 125 | override fun onPause() { | 141 | override fun onPause() { |
| @@ -128,6 +144,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 128 | stopMotionSensorListener() | 144 | stopMotionSensorListener() |
| 129 | } | 145 | } |
| 130 | 146 | ||
| 147 | override fun onUserLeaveHint() { | ||
| 148 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { | ||
| 149 | if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) { | ||
| 150 | val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() | ||
| 151 | .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() | ||
| 152 | enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 131 | override fun onNewIntent(intent: Intent) { | 157 | override fun onNewIntent(intent: Intent) { |
| 132 | super.onNewIntent(intent) | 158 | super.onNewIntent(intent) |
| 133 | setIntent(intent) | 159 | setIntent(intent) |
| @@ -230,6 +256,79 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 230 | } | 256 | } |
| 231 | } | 257 | } |
| 232 | 258 | ||
| 259 | private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder() : PictureInPictureParams.Builder { | ||
| 260 | val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) { | ||
| 261 | 0 -> Rational(16, 9) | ||
| 262 | 1 -> Rational(4, 3) | ||
| 263 | 2 -> Rational(21, 9) | ||
| 264 | 3 -> Rational(16, 10) | ||
| 265 | else -> null | ||
| 266 | } | ||
| 267 | return this.apply { aspectRatio?.let { setAspectRatio(it) } } | ||
| 268 | } | ||
| 269 | |||
| 270 | private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder() : PictureInPictureParams.Builder { | ||
| 271 | val pictureInPictureActions : MutableList<RemoteAction> = mutableListOf() | ||
| 272 | val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE | ||
| 273 | |||
| 274 | val isEmulationPaused = emulationFragment?.isEmulationStatePaused() ?: false | ||
| 275 | if (isEmulationPaused) { | ||
| 276 | val playIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_play) | ||
| 277 | val playPendingIntent = PendingIntent.getBroadcast( | ||
| 278 | this@EmulationActivity, R.drawable.ic_pip_play, Intent(actionPlay), pendingFlags | ||
| 279 | ) | ||
| 280 | val playRemoteAction = RemoteAction(playIcon, getString(R.string.play), getString(R.string.play), playPendingIntent) | ||
| 281 | pictureInPictureActions.add(playRemoteAction) | ||
| 282 | } else { | ||
| 283 | val pauseIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_pause) | ||
| 284 | val pausePendingIntent = PendingIntent.getBroadcast( | ||
| 285 | this@EmulationActivity, R.drawable.ic_pip_pause, Intent(actionPause), pendingFlags | ||
| 286 | ) | ||
| 287 | val pauseRemoteAction = RemoteAction(pauseIcon, getString(R.string.pause), getString(R.string.pause), pausePendingIntent) | ||
| 288 | pictureInPictureActions.add(pauseRemoteAction) | ||
| 289 | } | ||
| 290 | |||
| 291 | return this.apply { setActions(pictureInPictureActions) } | ||
| 292 | } | ||
| 293 | |||
| 294 | fun buildPictureInPictureParams() { | ||
| 295 | val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() | ||
| 296 | .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() | ||
| 297 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | ||
| 298 | pictureInPictureParamsBuilder.setAutoEnterEnabled(BooleanSetting.PICTURE_IN_PICTURE.boolean) | ||
| 299 | } | ||
| 300 | setPictureInPictureParams(pictureInPictureParamsBuilder.build()) | ||
| 301 | } | ||
| 302 | |||
| 303 | private var pictureInPictureReceiver = object : BroadcastReceiver() { | ||
| 304 | override fun onReceive(context : Context?, intent : Intent) { | ||
| 305 | if (intent.action == actionPlay) { | ||
| 306 | emulationFragment?.onPictureInPicturePlay() | ||
| 307 | } else if (intent.action == actionPause) { | ||
| 308 | emulationFragment?.onPictureInPicturePause() | ||
| 309 | } | ||
| 310 | buildPictureInPictureParams() | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { | ||
| 315 | super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) | ||
| 316 | if (isInPictureInPictureMode) { | ||
| 317 | IntentFilter().apply { | ||
| 318 | addAction(actionPause) | ||
| 319 | addAction(actionPlay) | ||
| 320 | }.also { | ||
| 321 | registerReceiver(pictureInPictureReceiver, it) | ||
| 322 | } | ||
| 323 | emulationFragment?.onPictureInPictureEnter() | ||
| 324 | } else { | ||
| 325 | try { | ||
| 326 | unregisterReceiver(pictureInPictureReceiver) | ||
| 327 | } catch (ignored : Exception) { } | ||
| 328 | emulationFragment?.onPictureInPictureLeave() | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 233 | private fun startMotionSensorListener() { | 332 | private fun startMotionSensorListener() { |
| 234 | val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager | 333 | val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager |
| 235 | val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) | 334 | val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 3dfd66779..63b4df273 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt | |||
| @@ -8,6 +8,7 @@ enum class BooleanSetting( | |||
| 8 | override val section: String, | 8 | override val section: String, |
| 9 | override val defaultValue: Boolean | 9 | override val defaultValue: Boolean |
| 10 | ) : AbstractBooleanSetting { | 10 | ) : AbstractBooleanSetting { |
| 11 | PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), | ||
| 11 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); | 12 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); |
| 12 | 13 | ||
| 13 | override var boolean: Boolean = defaultValue | 14 | override var boolean: Boolean = defaultValue |
| @@ -27,6 +28,7 @@ enum class BooleanSetting( | |||
| 27 | 28 | ||
| 28 | companion object { | 29 | companion object { |
| 29 | private val NOT_RUNTIME_EDITABLE = listOf( | 30 | private val NOT_RUNTIME_EDITABLE = listOf( |
| 31 | PICTURE_IN_PICTURE, | ||
| 30 | USE_CUSTOM_RTC | 32 | USE_CUSTOM_RTC |
| 31 | ) | 33 | ) |
| 32 | 34 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index fa84f94f5..4427a7d9d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt | |||
| @@ -93,6 +93,11 @@ enum class IntSetting( | |||
| 93 | Settings.SECTION_RENDERER, | 93 | Settings.SECTION_RENDERER, |
| 94 | 0 | 94 | 0 |
| 95 | ), | 95 | ), |
| 96 | RENDERER_SCREEN_LAYOUT( | ||
| 97 | "screen_layout", | ||
| 98 | Settings.SECTION_RENDERER, | ||
| 99 | Settings.LayoutOption_MobileLandscape | ||
| 100 | ), | ||
| 96 | RENDERER_ASPECT_RATIO( | 101 | RENDERER_ASPECT_RATIO( |
| 97 | "aspect_ratio", | 102 | "aspect_ratio", |
| 98 | Settings.SECTION_RENDERER, | 103 | Settings.SECTION_RENDERER, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 8df20b928..4f753955b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt | |||
| @@ -133,7 +133,6 @@ class Settings { | |||
| 133 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" | 133 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" |
| 134 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" | 134 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" |
| 135 | const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" | 135 | const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" |
| 136 | const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout" | ||
| 137 | const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" | 136 | const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" |
| 138 | const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" | 137 | const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" |
| 139 | 138 | ||
| @@ -144,6 +143,14 @@ class Settings { | |||
| 144 | 143 | ||
| 145 | private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() | 144 | private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() |
| 146 | 145 | ||
| 146 | // These must match what is defined in src/core/settings.h | ||
| 147 | const val LayoutOption_Default = 0 | ||
| 148 | const val LayoutOption_SingleScreen = 1 | ||
| 149 | const val LayoutOption_LargeScreen = 2 | ||
| 150 | const val LayoutOption_SideScreen = 3 | ||
| 151 | const val LayoutOption_MobilePortrait = 4 | ||
| 152 | const val LayoutOption_MobileLandscape = 5 | ||
| 153 | |||
| 147 | init { | 154 | init { |
| 148 | configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] = | 155 | configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] = |
| 149 | listOf( | 156 | listOf( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 72e2cce2a..3853845ce 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt | |||
| @@ -16,6 +16,7 @@ import androidx.core.view.WindowCompat | |||
| 16 | import androidx.core.view.WindowInsetsCompat | 16 | import androidx.core.view.WindowInsetsCompat |
| 17 | import android.view.ViewGroup.MarginLayoutParams | 17 | import android.view.ViewGroup.MarginLayoutParams |
| 18 | import androidx.activity.OnBackPressedCallback | 18 | import androidx.activity.OnBackPressedCallback |
| 19 | import androidx.activity.result.ActivityResultLauncher | ||
| 19 | import androidx.core.view.updatePadding | 20 | import androidx.core.view.updatePadding |
| 20 | import com.google.android.material.color.MaterialColors | 21 | import com.google.android.material.color.MaterialColors |
| 21 | import org.yuzu.yuzu_emu.NativeLibrary | 22 | import org.yuzu.yuzu_emu.NativeLibrary |
| @@ -239,5 +240,12 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { | |||
| 239 | settings.putExtra(ARG_GAME_ID, gameId) | 240 | settings.putExtra(ARG_GAME_ID, gameId) |
| 240 | context.startActivity(settings) | 241 | context.startActivity(settings) |
| 241 | } | 242 | } |
| 243 | |||
| 244 | fun launch(context: Context, launcher: ActivityResultLauncher<Intent>, menuTag: String?, gameId: String?) { | ||
| 245 | val settings = Intent(context, SettingsActivity::class.java) | ||
| 246 | settings.putExtra(ARG_MENU_TAG, menuTag) | ||
| 247 | settings.putExtra(ARG_GAME_ID, gameId) | ||
| 248 | launcher.launch(settings) | ||
| 249 | } | ||
| 242 | } | 250 | } |
| 243 | } | 251 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 1ceaa6fb4..b611389a1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt | |||
| @@ -166,6 +166,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 166 | IntSetting.CPU_ACCURACY.defaultValue | 166 | IntSetting.CPU_ACCURACY.defaultValue |
| 167 | ) | 167 | ) |
| 168 | ) | 168 | ) |
| 169 | add( | ||
| 170 | SwitchSetting( | ||
| 171 | BooleanSetting.PICTURE_IN_PICTURE, | ||
| 172 | R.string.picture_in_picture, | ||
| 173 | R.string.picture_in_picture_description, | ||
| 174 | BooleanSetting.PICTURE_IN_PICTURE.key, | ||
| 175 | BooleanSetting.PICTURE_IN_PICTURE.defaultValue | ||
| 176 | ) | ||
| 177 | ) | ||
| 169 | } | 178 | } |
| 170 | } | 179 | } |
| 171 | 180 | ||
| @@ -285,6 +294,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 285 | ) | 294 | ) |
| 286 | add( | 295 | add( |
| 287 | SingleChoiceSetting( | 296 | SingleChoiceSetting( |
| 297 | IntSetting.RENDERER_SCREEN_LAYOUT, | ||
| 298 | R.string.renderer_screen_layout, | ||
| 299 | 0, | ||
| 300 | R.array.rendererScreenLayoutNames, | ||
| 301 | R.array.rendererScreenLayoutValues, | ||
| 302 | IntSetting.RENDERER_SCREEN_LAYOUT.key, | ||
| 303 | IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue | ||
| 304 | ) | ||
| 305 | ) | ||
| 306 | add( | ||
| 307 | SingleChoiceSetting( | ||
| 288 | IntSetting.RENDERER_ASPECT_RATIO, | 308 | IntSetting.RENDERER_ASPECT_RATIO, |
| 289 | R.string.renderer_aspect_ratio, | 309 | R.string.renderer_aspect_ratio, |
| 290 | 0, | 310 | 0, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 02bfcdb1e..6ea5c90f3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -7,6 +7,7 @@ import android.annotation.SuppressLint | |||
| 7 | import android.app.AlertDialog | 7 | import android.app.AlertDialog |
| 8 | import android.content.Context | 8 | import android.content.Context |
| 9 | import android.content.DialogInterface | 9 | import android.content.DialogInterface |
| 10 | import android.content.Intent | ||
| 10 | import android.content.SharedPreferences | 11 | import android.content.SharedPreferences |
| 11 | import android.content.pm.ActivityInfo | 12 | import android.content.pm.ActivityInfo |
| 12 | import android.content.res.Resources | 13 | import android.content.res.Resources |
| @@ -19,11 +20,14 @@ import android.util.TypedValue | |||
| 19 | import android.view.* | 20 | import android.view.* |
| 20 | import android.widget.TextView | 21 | import android.widget.TextView |
| 21 | import androidx.activity.OnBackPressedCallback | 22 | import androidx.activity.OnBackPressedCallback |
| 23 | import androidx.activity.result.ActivityResultLauncher | ||
| 24 | import androidx.activity.result.contract.ActivityResultContracts | ||
| 22 | import androidx.appcompat.widget.PopupMenu | 25 | import androidx.appcompat.widget.PopupMenu |
| 23 | import androidx.core.content.res.ResourcesCompat | 26 | import androidx.core.content.res.ResourcesCompat |
| 24 | import androidx.core.graphics.Insets | 27 | import androidx.core.graphics.Insets |
| 25 | import androidx.core.view.ViewCompat | 28 | import androidx.core.view.ViewCompat |
| 26 | import androidx.core.view.WindowInsetsCompat | 29 | import androidx.core.view.WindowInsetsCompat |
| 30 | import androidx.core.view.isVisible | ||
| 27 | import androidx.core.view.updatePadding | 31 | import androidx.core.view.updatePadding |
| 28 | import androidx.fragment.app.Fragment | 32 | import androidx.fragment.app.Fragment |
| 29 | import androidx.lifecycle.Lifecycle | 33 | import androidx.lifecycle.Lifecycle |
| @@ -61,11 +65,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 61 | 65 | ||
| 62 | val args by navArgs<EmulationFragmentArgs>() | 66 | val args by navArgs<EmulationFragmentArgs>() |
| 63 | 67 | ||
| 68 | private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent> | ||
| 69 | |||
| 64 | override fun onAttach(context: Context) { | 70 | override fun onAttach(context: Context) { |
| 65 | super.onAttach(context) | 71 | super.onAttach(context) |
| 66 | if (context is EmulationActivity) { | 72 | if (context is EmulationActivity) { |
| 67 | emulationActivity = context | 73 | emulationActivity = context |
| 68 | NativeLibrary.setEmulationActivity(context) | 74 | NativeLibrary.setEmulationActivity(context) |
| 75 | |||
| 76 | onReturnFromSettings = context.activityResultRegistry.register( | ||
| 77 | "SettingsResult", ActivityResultContracts.StartActivityForResult() | ||
| 78 | ) { | ||
| 79 | binding.surfaceEmulation.setAspectRatio( | ||
| 80 | when (IntSetting.RENDERER_ASPECT_RATIO.int) { | ||
| 81 | 0 -> Rational(16, 9) | ||
| 82 | 1 -> Rational(4, 3) | ||
| 83 | 2 -> Rational(21, 9) | ||
| 84 | 3 -> Rational(16, 10) | ||
| 85 | 4 -> null // Stretch | ||
| 86 | else -> Rational(16, 9) | ||
| 87 | } | ||
| 88 | ) | ||
| 89 | emulationActivity?.buildPictureInPictureParams() | ||
| 90 | updateScreenLayout() | ||
| 91 | } | ||
| 69 | } else { | 92 | } else { |
| 70 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") | 93 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") |
| 71 | } | 94 | } |
| @@ -129,7 +152,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 129 | } | 152 | } |
| 130 | 153 | ||
| 131 | R.id.menu_settings -> { | 154 | R.id.menu_settings -> { |
| 132 | SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") | 155 | SettingsActivity.launch( |
| 156 | requireContext(), onReturnFromSettings, SettingsFile.FILE_NAME_CONFIG, "" | ||
| 157 | ) | ||
| 133 | true | 158 | true |
| 134 | } | 159 | } |
| 135 | 160 | ||
| @@ -162,7 +187,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 162 | lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | 187 | lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { |
| 163 | WindowInfoTracker.getOrCreate(requireContext()) | 188 | WindowInfoTracker.getOrCreate(requireContext()) |
| 164 | .windowLayoutInfo(requireActivity()) | 189 | .windowLayoutInfo(requireActivity()) |
| 165 | .collect { updateCurrentLayout(requireActivity() as EmulationActivity, it) } | 190 | .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } |
| 166 | } | 191 | } |
| 167 | } | 192 | } |
| 168 | } | 193 | } |
| @@ -204,6 +229,37 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 204 | super.onDetach() | 229 | super.onDetach() |
| 205 | } | 230 | } |
| 206 | 231 | ||
| 232 | fun isEmulationStatePaused() : Boolean { | ||
| 233 | return this::emulationState.isInitialized && emulationState.isPaused | ||
| 234 | } | ||
| 235 | |||
| 236 | fun onPictureInPictureEnter() { | ||
| 237 | if (binding.drawerLayout.isOpen) { | ||
| 238 | binding.drawerLayout.close() | ||
| 239 | } | ||
| 240 | if (EmulationMenuSettings.showOverlay) { | ||
| 241 | binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | fun onPictureInPicturePause() { | ||
| 246 | if (!emulationState.isPaused) { | ||
| 247 | emulationState.pause() | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | fun onPictureInPicturePlay() { | ||
| 252 | if (emulationState.isPaused) { | ||
| 253 | emulationState.run(false) | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | fun onPictureInPictureLeave() { | ||
| 258 | if (EmulationMenuSettings.showOverlay) { | ||
| 259 | binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 207 | private fun refreshInputOverlay() { | 263 | private fun refreshInputOverlay() { |
| 208 | binding.surfaceInputOverlay.refreshControls() | 264 | binding.surfaceInputOverlay.refreshControls() |
| 209 | } | 265 | } |
| @@ -243,15 +299,33 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 243 | } | 299 | } |
| 244 | } | 300 | } |
| 245 | 301 | ||
| 302 | @SuppressLint("SourceLockedOrientationActivity") | ||
| 303 | private fun updateScreenLayout() { | ||
| 304 | emulationActivity?.let { | ||
| 305 | when (IntSetting.RENDERER_SCREEN_LAYOUT.int) { | ||
| 306 | Settings.LayoutOption_MobileLandscape -> { | ||
| 307 | it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE | ||
| 308 | } | ||
| 309 | Settings.LayoutOption_MobilePortrait -> { | ||
| 310 | it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT | ||
| 311 | } | ||
| 312 | Settings.LayoutOption_Default -> { | ||
| 313 | it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED | ||
| 314 | } | ||
| 315 | else -> { it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE } | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 246 | private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt() | 320 | private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt() |
| 247 | 321 | ||
| 248 | fun updateCurrentLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) { | 322 | fun updateFoldableLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) { |
| 249 | val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let { | 323 | val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let { |
| 250 | if (it.isSeparating) { | 324 | if (it.isSeparating) { |
| 251 | emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED | 325 | emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED |
| 252 | if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { | 326 | if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { |
| 253 | binding.surfaceEmulation.layoutParams.height = it.bounds.top | 327 | binding.emulationContainer.layoutParams.height = it.bounds.top |
| 254 | binding.inGameMenu.layoutParams.height = it.bounds.bottom | 328 | // Prevent touch regions from being displayed in the hinge |
| 255 | binding.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx | 329 | binding.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx |
| 256 | binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx) | 330 | binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx) |
| 257 | } | 331 | } |
| @@ -259,14 +333,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 259 | it.isSeparating | 333 | it.isSeparating |
| 260 | } ?: false | 334 | } ?: false |
| 261 | if (!isFolding) { | 335 | if (!isFolding) { |
| 262 | binding.surfaceEmulation.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 336 | binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 263 | binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | ||
| 264 | binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 337 | binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 265 | binding.overlayContainer.updatePadding(0, 0, 0, 0) | 338 | binding.overlayContainer.updatePadding(0, 0, 0, 0) |
| 266 | emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE | 339 | updateScreenLayout() |
| 267 | } | 340 | } |
| 268 | binding.surfaceInputOverlay.requestLayout() | 341 | binding.emulationContainer.requestLayout() |
| 269 | binding.inGameMenu.requestLayout() | ||
| 270 | binding.overlayContainer.requestLayout() | 342 | binding.overlayContainer.requestLayout() |
| 271 | } | 343 | } |
| 272 | 344 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index aa424c768..724929a04 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.overlay | 4 | package org.yuzu.yuzu_emu.overlay |
| 5 | 5 | ||
| 6 | import android.annotation.SuppressLint | ||
| 6 | import android.app.Activity | 7 | import android.app.Activity |
| 7 | import android.content.Context | 8 | import android.content.Context |
| 8 | import android.content.SharedPreferences | 9 | import android.content.SharedPreferences |
| @@ -15,12 +16,14 @@ import android.graphics.drawable.Drawable | |||
| 15 | import android.graphics.drawable.VectorDrawable | 16 | import android.graphics.drawable.VectorDrawable |
| 16 | import android.os.Build | 17 | import android.os.Build |
| 17 | import android.util.AttributeSet | 18 | import android.util.AttributeSet |
| 19 | import android.util.Rational | ||
| 18 | import android.view.HapticFeedbackConstants | 20 | import android.view.HapticFeedbackConstants |
| 19 | import android.view.MotionEvent | 21 | import android.view.MotionEvent |
| 20 | import android.view.SurfaceView | 22 | import android.view.SurfaceView |
| 21 | import android.view.View | 23 | import android.view.View |
| 22 | import android.view.View.OnTouchListener | 24 | import android.view.View.OnTouchListener |
| 23 | import android.view.WindowInsets | 25 | import android.view.WindowInsets |
| 26 | import android.view.WindowManager | ||
| 24 | import androidx.core.content.ContextCompat | 27 | import androidx.core.content.ContextCompat |
| 25 | import androidx.preference.PreferenceManager | 28 | import androidx.preference.PreferenceManager |
| 26 | import androidx.window.layout.WindowMetricsCalculator | 29 | import androidx.window.layout.WindowMetricsCalculator |
| @@ -33,6 +36,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings | |||
| 33 | import org.yuzu.yuzu_emu.utils.EmulationMenuSettings | 36 | import org.yuzu.yuzu_emu.utils.EmulationMenuSettings |
| 34 | import kotlin.math.max | 37 | import kotlin.math.max |
| 35 | import kotlin.math.min | 38 | import kotlin.math.min |
| 39 | import kotlin.math.roundToInt | ||
| 36 | 40 | ||
| 37 | /** | 41 | /** |
| 38 | * Draws the interactive input overlay on top of the | 42 | * Draws the interactive input overlay on top of the |
| @@ -73,6 +77,25 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 73 | requestFocus() | 77 | requestFocus() |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 80 | @SuppressLint("DrawAllocation") | ||
| 81 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | ||
| 82 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) | ||
| 83 | val width = MeasureSpec.getSize(widthMeasureSpec) | ||
| 84 | val height = MeasureSpec.getSize(heightMeasureSpec) | ||
| 85 | if (height > width) { | ||
| 86 | val aspectRatio = with (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager) { | ||
| 87 | val metrics = maximumWindowMetrics.bounds | ||
| 88 | Rational(metrics.height(), metrics.width()).toFloat() | ||
| 89 | } | ||
| 90 | val newWidth: Int = width | ||
| 91 | val newHeight: Int = (width / aspectRatio).roundToInt() | ||
| 92 | setMeasuredDimension(newWidth, newHeight) | ||
| 93 | invalidate() | ||
| 94 | } else { | ||
| 95 | setMeasuredDimension(width, height) | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 76 | override fun draw(canvas: Canvas) { | 99 | override fun draw(canvas: Canvas) { |
| 77 | super.draw(canvas) | 100 | super.draw(canvas) |
| 78 | for (button in overlayButtons) { | 101 | for (button in overlayButtons) { |
| @@ -754,9 +777,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 754 | */ | 777 | */ |
| 755 | private fun getSafeScreenSize(context: Context): Pair<Point, Point> { | 778 | private fun getSafeScreenSize(context: Context): Pair<Point, Point> { |
| 756 | // Get screen size | 779 | // Get screen size |
| 757 | val windowMetrics = | 780 | val windowMetrics = WindowMetricsCalculator.getOrCreate() |
| 758 | WindowMetricsCalculator.getOrCreate() | 781 | .computeCurrentWindowMetrics(context as Activity) |
| 759 | .computeCurrentWindowMetrics(context as Activity) | ||
| 760 | var maxY = windowMetrics.bounds.height().toFloat() | 782 | var maxY = windowMetrics.bounds.height().toFloat() |
| 761 | var maxX = windowMetrics.bounds.width().toFloat() | 783 | var maxX = windowMetrics.bounds.width().toFloat() |
| 762 | var minY = 0 | 784 | var minY = 0 |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt index e1e7a59d7..7e8f058c1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt | |||
| @@ -11,14 +11,6 @@ object EmulationMenuSettings { | |||
| 11 | private val preferences = | 11 | private val preferences = |
| 12 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 12 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 13 | 13 | ||
| 14 | // These must match what is defined in src/core/settings.h | ||
| 15 | const val LayoutOption_Default = 0 | ||
| 16 | const val LayoutOption_SingleScreen = 1 | ||
| 17 | const val LayoutOption_LargeScreen = 2 | ||
| 18 | const val LayoutOption_SideScreen = 3 | ||
| 19 | const val LayoutOption_MobilePortrait = 4 | ||
| 20 | const val LayoutOption_MobileLandscape = 5 | ||
| 21 | |||
| 22 | var joystickRelCenter: Boolean | 14 | var joystickRelCenter: Boolean |
| 23 | get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true) | 15 | get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true) |
| 24 | set(value) { | 16 | set(value) { |
| @@ -41,16 +33,6 @@ object EmulationMenuSettings { | |||
| 41 | .apply() | 33 | .apply() |
| 42 | } | 34 | } |
| 43 | 35 | ||
| 44 | var landscapeScreenLayout: Int | ||
| 45 | get() = preferences.getInt( | ||
| 46 | Settings.PREF_MENU_SETTINGS_LANDSCAPE, | ||
| 47 | LayoutOption_MobileLandscape | ||
| 48 | ) | ||
| 49 | set(value) { | ||
| 50 | preferences.edit() | ||
| 51 | .putInt(Settings.PREF_MENU_SETTINGS_LANDSCAPE, value) | ||
| 52 | .apply() | ||
| 53 | } | ||
| 54 | var showFps: Boolean | 36 | var showFps: Boolean |
| 55 | get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false) | 37 | get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false) |
| 56 | set(value) { | 38 | set(value) { |
diff --git a/src/android/app/src/main/res/drawable/ic_pip_pause.xml b/src/android/app/src/main/res/drawable/ic_pip_pause.xml new file mode 100644 index 000000000..4a7d4ea03 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_pause.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportHeight="24" | ||
| 5 | android:viewportWidth="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="@android:color/white" | ||
| 8 | android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_pip_play.xml b/src/android/app/src/main/res/drawable/ic_pip_play.xml new file mode 100644 index 000000000..2303a4623 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_play.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportHeight="24" | ||
| 5 | android:viewportWidth="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="@android:color/white" | ||
| 8 | android:pathData="M8,5v14l11,-7z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 09b789b6b..ccd0f4c50 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml | |||
| @@ -12,14 +12,21 @@ | |||
| 12 | android:layout_width="match_parent" | 12 | android:layout_width="match_parent" |
| 13 | android:layout_height="match_parent"> | 13 | android:layout_height="match_parent"> |
| 14 | 14 | ||
| 15 | <!-- This is what everything is rendered to during emulation --> | 15 | <FrameLayout |
| 16 | <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView | 16 | android:id="@+id/emulation_container" |
| 17 | android:id="@+id/surface_emulation" | ||
| 18 | android:layout_width="match_parent" | 17 | android:layout_width="match_parent" |
| 19 | android:layout_height="match_parent" | 18 | android:layout_height="match_parent"> |
| 20 | android:layout_gravity="center" | 19 | |
| 21 | android:focusable="false" | 20 | <!-- This is what everything is rendered to during emulation --> |
| 22 | android:focusableInTouchMode="false" /> | 21 | <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView |
| 22 | android:id="@+id/surface_emulation" | ||
| 23 | android:layout_width="match_parent" | ||
| 24 | android:layout_height="match_parent" | ||
| 25 | android:layout_gravity="center" | ||
| 26 | android:focusable="false" | ||
| 27 | android:focusableInTouchMode="false" /> | ||
| 28 | |||
| 29 | </FrameLayout> | ||
| 23 | 30 | ||
| 24 | <FrameLayout | 31 | <FrameLayout |
| 25 | android:id="@+id/overlay_container" | 32 | android:id="@+id/overlay_container" |
| @@ -27,34 +34,36 @@ | |||
| 27 | android:layout_height="match_parent" | 34 | android:layout_height="match_parent" |
| 28 | android:layout_gravity="bottom"> | 35 | android:layout_gravity="bottom"> |
| 29 | 36 | ||
| 30 | <!-- This is the onscreen input overlay --> | 37 | <!-- This is the onscreen input overlay --> |
| 31 | <org.yuzu.yuzu_emu.overlay.InputOverlay | 38 | <org.yuzu.yuzu_emu.overlay.InputOverlay |
| 32 | android:id="@+id/surface_input_overlay" | 39 | android:id="@+id/surface_input_overlay" |
| 33 | android:layout_width="match_parent" | 40 | android:layout_width="match_parent" |
| 34 | android:layout_height="match_parent" | 41 | android:layout_height="match_parent" |
| 35 | android:focusable="true" | 42 | android:layout_gravity="bottom" |
| 36 | android:focusableInTouchMode="true" /> | 43 | android:focusable="true" |
| 44 | android:focusableInTouchMode="true" /> | ||
| 45 | |||
| 46 | <TextView | ||
| 47 | android:id="@+id/show_fps_text" | ||
| 48 | android:layout_width="wrap_content" | ||
| 49 | android:layout_height="wrap_content" | ||
| 50 | android:layout_gravity="left" | ||
| 51 | android:clickable="false" | ||
| 52 | android:focusable="false" | ||
| 53 | android:shadowColor="@android:color/black" | ||
| 54 | android:textColor="@android:color/white" | ||
| 55 | android:textSize="12sp" | ||
| 56 | tools:ignore="RtlHardcoded" /> | ||
| 37 | 57 | ||
| 38 | <TextView | 58 | <Button |
| 39 | android:id="@+id/show_fps_text" | 59 | style="@style/Widget.Material3.Button.ElevatedButton" |
| 40 | android:layout_width="wrap_content" | 60 | android:id="@+id/done_control_config" |
| 41 | android:layout_height="wrap_content" | 61 | android:layout_width="wrap_content" |
| 42 | android:layout_gravity="left" | 62 | android:layout_height="wrap_content" |
| 43 | android:clickable="false" | 63 | android:layout_gravity="center" |
| 44 | android:focusable="false" | 64 | android:text="@string/emulation_done" |
| 45 | android:shadowColor="@android:color/black" | 65 | android:visibility="gone" /> |
| 46 | android:textColor="@android:color/white" | ||
| 47 | android:textSize="12sp" | ||
| 48 | tools:ignore="RtlHardcoded" /> | ||
| 49 | 66 | ||
| 50 | <Button | ||
| 51 | style="@style/Widget.Material3.Button.ElevatedButton" | ||
| 52 | android:id="@+id/done_control_config" | ||
| 53 | android:layout_width="wrap_content" | ||
| 54 | android:layout_height="wrap_content" | ||
| 55 | android:layout_gravity="center" | ||
| 56 | android:text="@string/emulation_done" | ||
| 57 | android:visibility="gone" /> | ||
| 58 | </FrameLayout> | 67 | </FrameLayout> |
| 59 | 68 | ||
| 60 | </androidx.coordinatorlayout.widget.CoordinatorLayout> | 69 | </androidx.coordinatorlayout.widget.CoordinatorLayout> |
| @@ -63,7 +72,7 @@ | |||
| 63 | android:id="@+id/in_game_menu" | 72 | android:id="@+id/in_game_menu" |
| 64 | android:layout_width="wrap_content" | 73 | android:layout_width="wrap_content" |
| 65 | android:layout_height="match_parent" | 74 | android:layout_height="match_parent" |
| 66 | android:layout_gravity="start|bottom" | 75 | android:layout_gravity="start" |
| 67 | app:headerLayout="@layout/header_in_game" | 76 | app:headerLayout="@layout/header_in_game" |
| 68 | app:menu="@menu/menu_in_game" /> | 77 | app:menu="@menu/menu_in_game" /> |
| 69 | 78 | ||
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index ea20cb17c..7f7b1938c 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -119,6 +119,18 @@ | |||
| 119 | <item>3</item> | 119 | <item>3</item> |
| 120 | </integer-array> | 120 | </integer-array> |
| 121 | 121 | ||
| 122 | <string-array name="rendererScreenLayoutNames"> | ||
| 123 | <item>@string/screen_layout_landscape</item> | ||
| 124 | <item>@string/screen_layout_portrait</item> | ||
| 125 | <item>@string/screen_layout_auto</item> | ||
| 126 | </string-array> | ||
| 127 | |||
| 128 | <integer-array name="rendererScreenLayoutValues"> | ||
| 129 | <item>5</item> | ||
| 130 | <item>4</item> | ||
| 131 | <item>0</item> | ||
| 132 | </integer-array> | ||
| 133 | |||
| 122 | <string-array name="rendererAspectRatioNames"> | 134 | <string-array name="rendererAspectRatioNames"> |
| 123 | <item>@string/ratio_default</item> | 135 | <item>@string/ratio_default</item> |
| 124 | <item>@string/ratio_force_four_three</item> | 136 | <item>@string/ratio_force_four_three</item> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index c236811fa..b5bc249d4 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -162,6 +162,7 @@ | |||
| 162 | <string name="renderer_accuracy">Accuracy level</string> | 162 | <string name="renderer_accuracy">Accuracy level</string> |
| 163 | <string name="renderer_resolution">Resolution (Handheld/Docked)</string> | 163 | <string name="renderer_resolution">Resolution (Handheld/Docked)</string> |
| 164 | <string name="renderer_vsync">VSync mode</string> | 164 | <string name="renderer_vsync">VSync mode</string> |
| 165 | <string name="renderer_screen_layout">Orientation</string> | ||
| 165 | <string name="renderer_aspect_ratio">Aspect ratio</string> | 166 | <string name="renderer_aspect_ratio">Aspect ratio</string> |
| 166 | <string name="renderer_scaling_filter">Window adapting filter</string> | 167 | <string name="renderer_scaling_filter">Window adapting filter</string> |
| 167 | <string name="renderer_anti_aliasing">Anti-aliasing method</string> | 168 | <string name="renderer_anti_aliasing">Anti-aliasing method</string> |
| @@ -326,6 +327,11 @@ | |||
| 326 | <string name="anti_aliasing_fxaa">FXAA</string> | 327 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 327 | <string name="anti_aliasing_smaa">SMAA</string> | 328 | <string name="anti_aliasing_smaa">SMAA</string> |
| 328 | 329 | ||
| 330 | <!-- Screen Layouts --> | ||
| 331 | <string name="screen_layout_landscape">Landscape</string> | ||
| 332 | <string name="screen_layout_portrait">Portrait</string> | ||
| 333 | <string name="screen_layout_auto">Auto</string> | ||
| 334 | |||
| 329 | <!-- Aspect Ratios --> | 335 | <!-- Aspect Ratios --> |
| 330 | <string name="ratio_default">Default (16:9)</string> | 336 | <string name="ratio_default">Default (16:9)</string> |
| 331 | <string name="ratio_force_four_three">Force 4:3</string> | 337 | <string name="ratio_force_four_three">Force 4:3</string> |
| @@ -364,6 +370,12 @@ | |||
| 364 | <string name="use_black_backgrounds">Black backgrounds</string> | 370 | <string name="use_black_backgrounds">Black backgrounds</string> |
| 365 | <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> | 371 | <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> |
| 366 | 372 | ||
| 373 | <!-- Picture-In-Picture --> | ||
| 374 | <string name="picture_in_picture">Picture in Picture</string> | ||
| 375 | <string name="picture_in_picture_description">Minimize window when placed in the background</string> | ||
| 376 | <string name="pause">Pause</string> | ||
| 377 | <string name="play">Play</string> | ||
| 378 | |||
| 367 | <!-- Licenses screen strings --> | 379 | <!-- Licenses screen strings --> |
| 368 | <string name="licenses">Licenses</string> | 380 | <string name="licenses">Licenses</string> |
| 369 | <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> | 381 | <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> |