From de9100ea81adec8c008b9bb9376541d2410ef4e8 Mon Sep 17 00:00:00 2001 From: Abandoned Cart Date: Sat, 10 Jun 2023 22:42:54 -0400 Subject: [PATCH 01/10] android: Add Picture in Picture / Orientation --- src/android/app/src/main/AndroidManifest.xml | 2 + .../yuzu_emu/activities/EmulationActivity.kt | 99 +++++++++++++++++++ .../features/settings/model/BooleanSetting.kt | 2 + .../features/settings/model/IntSetting.kt | 5 + .../features/settings/model/Settings.kt | 9 +- .../features/settings/ui/SettingsActivity.kt | 8 ++ .../settings/ui/SettingsFragmentPresenter.kt | 20 ++++ .../yuzu_emu/fragments/EmulationFragment.kt | 92 +++++++++++++++-- .../org/yuzu/yuzu_emu/overlay/InputOverlay.kt | 28 +++++- .../yuzu_emu/utils/EmulationMenuSettings.kt | 18 ---- .../src/main/res/drawable/ic_pip_pause.xml | 9 ++ .../app/src/main/res/drawable/ic_pip_play.xml | 9 ++ .../main/res/layout/fragment_emulation.xml | 77 ++++++++------- .../app/src/main/res/values/arrays.xml | 12 +++ .../app/src/main/res/values/strings.xml | 12 +++ 15 files changed, 336 insertions(+), 66 deletions(-) create mode 100644 src/android/app/src/main/res/drawable/ic_pip_pause.xml create mode 100644 src/android/app/src/main/res/drawable/ic_pip_play.xml 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 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" android:theme="@style/Theme.Yuzu.Main" android:screenOrientation="userLandscape" + android:supportsPictureInPicture="true" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" android:exported="true"> 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 @@ 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.os.Build import android.os.Bundle +import android.util.Rational import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent @@ -27,6 +36,8 @@ import androidx.navigation.fragment.NavHostFragment import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding +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.SettingsViewModel import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper @@ -50,6 +61,9 @@ 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 val settingsViewModel: SettingsViewModel by viewModels() override fun onDestroy() { @@ -120,6 +134,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() + + buildPictureInPictureParams() } override fun onPause() { @@ -128,6 +144,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) @@ -230,6 +256,79 @@ 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 PictureInPictureParams.Builder.getPictureInPictureActionsBuilder() : PictureInPictureParams.Builder { + val pictureInPictureActions : MutableList = 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) } + } + + fun buildPictureInPictureParams() { + 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()) + } + + 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() + } + buildPictureInPictureParams() + } + } + + 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) 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( 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 ) 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( Settings.SECTION_RENDERER, 0 ), + RENDERER_SCREEN_LAYOUT( + "screen_layout", + Settings.SECTION_RENDERER, + Settings.LayoutOption_MobileLandscape + ), RENDERER_ASPECT_RATIO( "aspect_ratio", 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 { 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> = 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( 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 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, 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) + } } } 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) 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, 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 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.Resources @@ -19,11 +20,14 @@ 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.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.lifecycle.Lifecycle @@ -61,11 +65,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val args by navArgs() + private lateinit var onReturnFromSettings: ActivityResultLauncher + override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { emulationActivity = context NativeLibrary.setEmulationActivity(context) + + onReturnFromSettings = context.activityResultRegistry.register( + "SettingsResult", ActivityResultContracts.StartActivityForResult() + ) { + binding.surfaceEmulation.setAspectRatio( + when (IntSetting.RENDERER_ASPECT_RATIO.int) { + 0 -> Rational(16, 9) + 1 -> Rational(4, 3) + 2 -> Rational(21, 9) + 3 -> Rational(16, 10) + 4 -> null // Stretch + else -> Rational(16, 9) + } + ) + emulationActivity?.buildPictureInPictureParams() + updateScreenLayout() + } } else { throw IllegalStateException("EmulationFragment must have EmulationActivity parent") } @@ -129,7 +152,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 } @@ -162,7 +187,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(requireContext()) .windowLayoutInfo(requireActivity()) - .collect { updateCurrentLayout(requireActivity() as EmulationActivity, it) } + .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } } } } @@ -204,6 +229,37 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { super.onDetach() } + fun isEmulationStatePaused() : Boolean { + return this::emulationState.isInitialized && emulationState.isPaused + } + + fun onPictureInPictureEnter() { + 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 } + } + } + private fun refreshInputOverlay() { binding.surfaceInputOverlay.refreshControls() } @@ -243,15 +299,33 @@ 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 } + } + } + } + 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 if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { - binding.surfaceEmulation.layoutParams.height = it.bounds.top - binding.inGameMenu.layoutParams.height = it.bounds.bottom + binding.emulationContainer.layoutParams.height = it.bounds.top + // Prevent touch regions from being displayed in the hinge binding.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx) } @@ -259,14 +333,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { it.isSeparating } ?: false if (!isFolding) { - binding.surfaceEmulation.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT - binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.emulationContainer.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.inGameMenu.requestLayout() + binding.emulationContainer.requestLayout() binding.overlayContainer.requestLayout() } 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 @@ package org.yuzu.yuzu_emu.overlay +import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.SharedPreferences @@ -15,12 +16,14 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.VectorDrawable import android.os.Build import android.util.AttributeSet +import android.util.Rational import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.SurfaceView import android.view.View import android.view.View.OnTouchListener import android.view.WindowInsets +import android.view.WindowManager import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager import androidx.window.layout.WindowMetricsCalculator @@ -33,6 +36,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.utils.EmulationMenuSettings import kotlin.math.max import kotlin.math.min +import kotlin.math.roundToInt /** * Draws the interactive input overlay on top of the @@ -73,6 +77,25 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context requestFocus() } + @SuppressLint("DrawAllocation") + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val width = MeasureSpec.getSize(widthMeasureSpec) + val height = MeasureSpec.getSize(heightMeasureSpec) + if (height > width) { + val aspectRatio = with (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager) { + val metrics = maximumWindowMetrics.bounds + Rational(metrics.height(), metrics.width()).toFloat() + } + val newWidth: Int = width + val newHeight: Int = (width / aspectRatio).roundToInt() + setMeasuredDimension(newWidth, newHeight) + invalidate() + } else { + setMeasuredDimension(width, height) + } + } + override fun draw(canvas: Canvas) { super.draw(canvas) for (button in overlayButtons) { @@ -754,9 +777,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context */ private fun getSafeScreenSize(context: Context): Pair { // Get screen size - val windowMetrics = - WindowMetricsCalculator.getOrCreate() - .computeCurrentWindowMetrics(context as Activity) + val windowMetrics = WindowMetricsCalculator.getOrCreate() + .computeCurrentWindowMetrics(context as Activity) var maxY = windowMetrics.bounds.height().toFloat() var maxX = windowMetrics.bounds.width().toFloat() 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 { 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) { 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 @@ + + + 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 @@ + + + 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 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + android:layout_height="match_parent"> + + + + + - - + + - + + +