summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts30
-rw-r--r--src/android/app/src/main/AndroidManifest.xml2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt41
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt122
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt89
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt212
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt327
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt40
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt4
-rw-r--r--src/android/app/src/main/jni/native.cpp13
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_pause.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_play.xml9
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml82
-rw-r--r--src/android/app/src/main/res/values/arrays.xml12
-rw-r--r--src/android/app/src/main/res/values/integers.xml64
-rw-r--r--src/android/app/src/main/res/values/strings.xml14
-rw-r--r--src/common/fs/fs.cpp27
-rw-r--r--src/common/fs/fs_android.h5
-rw-r--r--src/core/file_sys/vfs_real.cpp187
-rw-r--r--src/core/file_sys/vfs_real.h30
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp2
-rw-r--r--src/input_common/drivers/virtual_amiibo.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp15
73 files changed, 1309 insertions, 540 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index a637db78a..7ae538cf9 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -2,7 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import android.annotation.SuppressLint
+import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
+import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
plugins {
id("com.android.application")
@@ -10,6 +12,7 @@ plugins {
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21"
id("androidx.navigation.safeargs.kotlin")
+ id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
}
/**
@@ -44,16 +47,6 @@ android {
jniLibs.useLegacyPackaging = true
}
- lint {
- // This is important as it will run lint but not abort on error
- // Lint has some overly obnoxious "errors" that should really be warnings
- abortOnError = false
-
- //Uncomment disable lines for test builds...
- //disable 'MissingTranslation'bin
- //disable 'ExtraTranslation'
- }
-
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
@@ -167,6 +160,23 @@ android {
}
}
+tasks.getByPath("preBuild").dependsOn("ktlintCheck")
+
+ktlint {
+ version.set("0.47.0")
+ android.set(true)
+ ignoreFailures.set(false)
+ disabledRules.set(
+ setOf(
+ "no-wildcard-imports",
+ "package-name"
+ )
+ )
+ reporters {
+ reporter(ReporterType.CHECKSTYLE)
+ }
+}
+
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
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">
<intent-filter>
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 4be9ade14..f860cdd4b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -14,16 +14,18 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
+import org.yuzu.yuzu_emu.utils.FileUtil.exists
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
+import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.Log.error
import org.yuzu.yuzu_emu.utils.Log.verbose
import org.yuzu.yuzu_emu.utils.Log.warning
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
-import java.lang.ref.WeakReference
/**
* Class which contains methods that interact
@@ -74,7 +76,9 @@ object NativeLibrary {
fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
- } else openContentUri(appContext, path, openmode)
+ } else {
+ openContentUri(appContext, path, openmode)
+ }
}
@Keep
@@ -82,7 +86,29 @@ object NativeLibrary {
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
- } else getFileSize(appContext, path)
+ } else {
+ getFileSize(appContext, path)
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ fun exists(path: String?): Boolean {
+ return if (isNativePath(path!!)) {
+ YuzuApplication.documentsTree!!.exists(path)
+ } else {
+ exists(appContext, path)
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ fun isDirectory(path: String?): Boolean {
+ return if (isNativePath(path!!)) {
+ YuzuApplication.documentsTree!!.isDirectory(path)
+ } else {
+ isDirectory(appContext, path)
+ }
}
/**
@@ -283,6 +309,11 @@ object NativeLibrary {
external fun isRunning(): Boolean
/**
+ * Returns true if emulation is paused.
+ */
+ external fun isPaused(): Boolean
+
+ /**
* Returns the performance stats for the current game
*/
external fun getPerfStats(): DoubleArray
@@ -431,7 +462,9 @@ object NativeLibrary {
Html.FROM_HTML_MODE_LEGACY
)
)
- .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
+ emulationActivity.finish()
+ }
.setOnDismissListener { emulationActivity.finish() }
emulationActivity.runOnUiThread {
val alert = builder.create()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 4c947b786..04ab6a220 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -7,12 +7,12 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
+import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
-import java.io.File
-fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
+fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
private fun createNotificationChannels() {
@@ -21,7 +21,9 @@ class YuzuApplication : Application() {
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
- emulationChannel.description = getString(R.string.emulation_notification_channel_description)
+ emulationChannel.description = getString(
+ R.string.emulation_notification_channel_description
+ )
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
@@ -48,7 +50,7 @@ class YuzuApplication : Application() {
GpuDriverHelper.initializeDriverParameters(applicationContext)
NativeLibrary.logDeviceInfo()
- createNotificationChannels();
+ createNotificationChannels()
}
companion object {
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..f0a6753a9 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
@@ -24,9 +33,12 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
+import kotlin.math.roundToInt
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
@@ -34,7 +46,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
-import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
@@ -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,100 @@ 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 // Best fit
+ }
+ return this.apply { aspectRatio?.let { setAspectRatio(it) } }
+ }
+
+ private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder():
+ PictureInPictureParams.Builder {
+ val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf()
+ val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+
+ if (NativeLibrary.isPaused()) {
+ 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) {
+ if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation()
+ } else if (intent.action == actionPause) {
+ if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
+ }
+ buildPictureInPictureParams()
+ }
+ }
+
+ override fun onPictureInPictureModeChanged(
+ isInPictureInPictureMode: Boolean,
+ newConfig: Configuration
+ ) {
+ super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
+ if (isInPictureInPictureMode) {
+ IntentFilter().apply {
+ addAction(actionPause)
+ addAction(actionPlay)
+ }.also {
+ registerReceiver(pictureInPictureReceiver, it)
+ }
+ } else {
+ try {
+ unregisterReceiver(pictureInPictureReceiver)
+ } catch (ignored: Exception) {
+ }
+ }
+ }
+
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/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 83d08841b..e91277d35 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -28,10 +28,9 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
-import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.model.Game
-import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.model.GamesViewModel
class GameAdapter(private val activity: AppCompatActivity) :
@@ -60,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
- val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true
+ val gameExists = DocumentFile.fromSingleUri(
+ YuzuApplication.appContext,
+ Uri.parse(holder.game.path)
+ )?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b719dd539..d3df3bc81 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
)
when (option.titleId) {
- R.string.get_early_access -> binding.optionLayout.background =
- ContextCompat.getDrawable(
- binding.optionCard.context,
- R.drawable.premium_background
- )
+ R.string.get_early_access ->
+ binding.optionLayout.background =
+ ContextCompat.getDrawable(
+ binding.optionCard.context,
+ R.drawable.premium_background
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
index 82a6712b6..e058067c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
@@ -12,10 +12,10 @@ import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.annotation.Keep
import androidx.core.view.ViewCompat
+import java.io.Serializable
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
-import java.io.Serializable
@Keep
object SoftwareKeyboard {
@@ -40,19 +40,22 @@ object SoftwareKeyboard {
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
val handler = Handler(Looper.myLooper()!!)
val delayMs = 500
- handler.postDelayed(object : Runnable {
- override fun run() {
- val insets = ViewCompat.getRootWindowInsets(overlayView)
- val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
- if (isKeyboardVisible) {
- handler.postDelayed(this, delayMs.toLong())
- return
- }
+ handler.postDelayed(
+ object : Runnable {
+ override fun run() {
+ val insets = ViewCompat.getRootWindowInsets(overlayView)
+ val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
+ if (isKeyboardVisible) {
+ handler.postDelayed(this, delayMs.toLong())
+ return
+ }
- // No longer visible, submit the result.
- NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
- }
- }, delayMs.toLong())
+ // No longer visible, submit the result.
+ NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
+ }
+ },
+ delayMs.toLong()
+ )
}
@JvmStatic
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index 3b1559c80..a18efef19 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -20,7 +20,10 @@ object DiskShaderCacheProgress {
emulationActivity.getString(R.string.loading),
emulationActivity.getString(R.string.preparing_shaders)
)
- fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
+ fragment.show(
+ emulationActivity.supportFragmentManager,
+ ShaderProgressDialogFragment.TAG
+ )
}
synchronized(finishLock) { finishLock.wait() }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
index 2c68c9ac3..8a8e0a6e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
@@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() {
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
alertDialog.setMessage(msg)
}
- synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
+ synchronized(DiskShaderCacheProgress.finishLock) {
+ DiskShaderCacheProgress.finishLock.notifyAll()
+ }
}
override fun onDestroyView() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
index 4c3a9ca80..f3be156b5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
@@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap
+import java.io.*
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.getPublicFilesDir
-import java.io.*
class DocumentProvider : DocumentsProvider() {
private val baseDirectory: File
@@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() {
DocumentsContract.Document.COLUMN_SIZE
)
- const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user"
+ const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user"
const val ROOT_ID: String = "root"
}
@@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() {
private fun getFile(documentId: String): File {
if (documentId.startsWith(ROOT_ID)) {
val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
- if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found")
+ if (!file.exists()) {
+ throw FileNotFoundException(
+ "${file.absolutePath} ($documentId) not found"
+ )
+ }
return file
} else {
throw FileNotFoundException("'$documentId' is not in any known root")
@@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Root.COLUMN_SUMMARY, null)
add(
DocumentsContract.Root.COLUMN_FLAGS,
- DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
+ DocumentsContract.Root.FLAG_SUPPORTS_CREATE or
+ DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
)
add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
@@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() {
try {
if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
- if (!newFile.mkdir())
+ if (!newFile.mkdir()) {
throw IOException("Failed to create directory")
+ }
} else {
- if (!newFile.createNewFile())
+ if (!newFile.createNewFile()) {
throw IOException("Failed to create file")
+ }
}
} catch (e: IOException) {
throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
@@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() {
override fun deleteDocument(documentId: String?) {
val file = getFile(documentId!!)
- if (!file.delete())
+ if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
+ }
}
override fun removeDocument(documentId: String, parentDocumentId: String?) {
@@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() {
val file = getFile(documentId)
if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
- if (!file.delete())
+ if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
+ }
} else {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
}
override fun renameDocument(documentId: String?, displayName: String?): String {
- if (displayName == null)
- throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
+ if (displayName == null) {
+ throw FileNotFoundException(
+ "Couldn't rename document '$documentId' as the new name is null"
+ )
+ }
val sourceFile = getFile(documentId!!)
val sourceParentFile = sourceFile.parentFile
- ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent")
+ ?: throw FileNotFoundException(
+ "Couldn't rename document '$documentId' as it has no parent"
+ )
val destFile = sourceParentFile.resolve(displayName)
try {
- if (!sourceFile.renameTo(destFile))
- throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'")
+ if (!sourceFile.renameTo(destFile)) {
+ throw FileNotFoundException(
+ "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'"
+ )
+ }
} catch (e: Exception) {
- throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}")
+ throw FileNotFoundException(
+ "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " +
+ "${e.message}"
+ )
}
return getDocumentId(destFile)
}
private fun copyDocument(
- sourceDocumentId: String, sourceParentDocumentId: String,
+ sourceDocumentId: String,
+ sourceParentDocumentId: String,
targetParentDocumentId: String?
): String {
- if (!isChildDocument(sourceParentDocumentId, sourceDocumentId))
- throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'")
+ if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) {
+ throw FileNotFoundException(
+ "Couldn't copy document '$sourceDocumentId' as its parent is not " +
+ "'$sourceParentDocumentId'"
+ )
+ }
return copyDocument(sourceDocumentId, targetParentDocumentId)
}
@@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() {
val newFile = parent.resolveWithoutConflict(oldFile.name)
try {
- if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true)))
+ if (!(
+ newFile.createNewFile() && newFile.setWritable(true) &&
+ newFile.setReadable(true)
+ )
+ ) {
throw IOException("Couldn't create new file")
+ }
FileInputStream(oldFile).use { inStream ->
FileOutputStream(newFile).use { outStream ->
@@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() {
}
override fun moveDocument(
- sourceDocumentId: String, sourceParentDocumentId: String?,
+ sourceDocumentId: String,
+ sourceParentDocumentId: String?,
targetParentDocumentId: String?
): String {
try {
val newDocumentId = copyDocument(
- sourceDocumentId, sourceParentDocumentId!!,
+ sourceDocumentId,
+ sourceParentDocumentId!!,
targetParentDocumentId
)
removeDocument(sourceDocumentId, sourceParentDocumentId)
@@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
add(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
- if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name
+ if (localFile == baseDirectory) {
+ context!!.getString(R.string.app_name)
+ } else {
+ localFile.name
+ }
)
add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
add(DocumentsContract.Document.COLUMN_FLAGS, flags)
- if (localFile == baseDirectory)
+ if (localFile == baseDirectory) {
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
+ }
}
return cursor
}
private fun getTypeForFile(file: File): Any {
- return if (file.isDirectory)
+ return if (file.isDirectory) {
DocumentsContract.Document.MIME_TYPE_DIR
- else
+ } else {
getTypeForName(file.name)
+ }
}
private fun getTypeForName(name: String): Any {
@@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() {
if (lastDot >= 0) {
val extension = name.substring(lastDot + 1)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
- if (mime != null)
+ if (mime != null) {
return mime
+ }
}
return "application/octect-stream"
}
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..88afb2223 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
@@ -4,11 +4,11 @@
package org.yuzu.yuzu_emu.features.settings.model
import android.text.TextUtils
+import java.util.*
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
-import java.util.*
class Settings {
private var gameId: String? = null
@@ -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,10 @@ class Settings {
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
+ const val LayoutOption_Unspecified = 0
+ 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/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 9eac9904e..7306ec458 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
-import org.yuzu.yuzu_emu.features.settings.model.IntSetting
class SingleChoiceSetting(
setting: AbstractIntSetting?,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 842648ce4..92d0167ae 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,13 +3,11 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
-import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.Log
-import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
@@ -19,7 +17,7 @@ class SliderSetting(
val max: Int,
val units: String,
val key: String? = null,
- val defaultValue: Int? = null,
+ val defaultValue: Int? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 9e9b00d10..bad34fd88 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
-import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class StringSingleChoiceSetting(
val key: String? = null,
@@ -22,7 +21,9 @@ class StringSingleChoiceSetting(
if (valuesId == null) return null
return if (index >= 0 && index < valuesId.size) {
valuesId[index]
- } else ""
+ } else {
+ ""
+ }
}
val selectedValue: String
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index a3ef59c2f..8a9d13a92 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,8 +3,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-
class SubmenuSetting(
titleId: Int,
descriptionId: Int,
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..a5af5a7ae 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
@@ -8,17 +8,18 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import android.view.ViewGroup.MarginLayoutParams
-import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.google.android.material.color.MaterialColors
-import org.yuzu.yuzu_emu.NativeLibrary
+import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -29,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.*
-import java.io.IOException
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this)
@@ -59,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
setSupportActionBar(binding.toolbarSettings)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
- if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
+ if (InsetsHelper.getSystemGestureType(applicationContext) !=
+ InsetsHelper.GESTURE_NAVIGATION
+ ) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -75,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() = navigateBack()
- })
+ }
+ )
setInsets()
}
@@ -148,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private fun areSystemAnimationsEnabled(): Boolean {
val duration = android.provider.Settings.Global.getFloat(
contentResolver,
- android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
+ android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
+ 1f
)
val transition = android.provider.Settings.Global.getFloat(
contentResolver,
- android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
+ android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
+ 1f
)
return duration != 0f && transition != 0f
}
@@ -207,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.frameContent
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.updatePadding(
@@ -239,5 +246,17 @@ 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)
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
index 4361d95fb..93e677b21 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.text.TextUtils
+import java.io.File
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
-import java.io.File
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
val settings: Settings get() = activityView.settings
@@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
private fun prepareDirectoriesIfNeeded() {
val configFile =
- File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
+ File(
+ "${DirectoryInitialization.userDirectory}/config/" +
+ "${SettingsFile.FILE_NAME_CONFIG}.ini"
+ )
if (!configFile.exists()) {
- Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
+ Log.error(
+ "${DirectoryInitialization.userDirectory}/config/" +
+ "${SettingsFile.FILE_NAME_CONFIG}.ini"
+ )
Log.error("yuzu config file could not be found!")
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 1eb4899fc..eac6a134b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -13,7 +13,6 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.setFragmentResultListener
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 867147950..70a74c4dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
settingsAdapter = SettingsAdapter(this, requireActivity())
- val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
+ val dividerDecoration = MaterialDividerItemDecoration(
+ requireContext(),
+ LinearLayoutManager.VERTICAL
+ )
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply {
adapter = settingsAdapter
@@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.listSettings
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets
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..c8c85dd7a 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
@@ -7,7 +7,6 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import androidx.preference.PreferenceManager
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -166,6 +165,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
+ )
+ )
}
}
@@ -227,7 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
sl.apply {
-
add(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
@@ -285,6 +292,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
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,
R.string.renderer_aspect_ratio,
0,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 04c045e77..7955532ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -4,15 +4,15 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
-import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index b163bd6ca..54f531795 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.CompoundButton
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index e29bca11d..20a0636df 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,6 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.utils
+import java.io.*
+import java.util.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.utils.BiMap
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
-import java.io.*
-import java.util.*
/**
* Contains static methods for interacting with .ini files in which settings are stored.
@@ -137,9 +137,12 @@ object SettingsFile {
for (settingKey in sortedKeySet) {
val setting = settings[settingKey]
NativeLibrary.setUserSetting(
- gameId, mapSectionNameFromIni(
+ gameId,
+ mapSectionNameFromIni(
section.name
- ), setting!!.key, setting.valueAsString
+ ),
+ setting!!.key,
+ setting.valueAsString
)
}
}
@@ -148,13 +151,17 @@ object SettingsFile {
private fun mapSectionNameFromIni(generalSectionName: String): String? {
return if (sectionsMap.getForward(generalSectionName) != null) {
sectionsMap.getForward(generalSectionName)
- } else generalSectionName
+ } else {
+ generalSectionName
+ }
}
private fun mapSectionNameToIni(generalSectionName: String): String {
return if (sectionsMap.getBackward(generalSectionName) != null) {
sectionsMap.getBackward(generalSectionName).toString()
- } else generalSectionName
+ } else {
+ generalSectionName
+ }
}
fun getSettingsFile(fileName: String): File {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index c92e2755c..2ff827c6b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -66,7 +66,11 @@ class AboutFragment : Fragment() {
true
}
- binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) }
+ binding.buttonContributors.setOnClickListener {
+ openLink(
+ getString(R.string.contributors_link)
+ )
+ }
binding.buttonLicenses.setOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
@@ -101,7 +105,9 @@ class AboutFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
index d8bbc1ce4..dbc16da4a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
@@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() {
parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
}
- binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) }
+ binding.getEarlyAccessButton.setOnClickListener {
+ openLink(
+ getString(R.string.play_store_link)
+ )
+ }
setInsets()
}
@@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
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..4643418c1 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,24 +7,26 @@ 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
+import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Rational
-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.updatePadding
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
@@ -48,6 +50,7 @@ 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
+import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.utils.*
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -61,11 +64,28 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val args by navArgs<EmulationFragmentArgs>()
+ private var isInFoldableLayout = false
+
+ private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
+
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
+
+ lifecycleScope.launch(Dispatchers.Main) {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ WindowInfoTracker.getOrCreate(context)
+ .windowLayoutInfo(context)
+ .collect { updateFoldableLayout(context, it) }
+ }
+ }
+
+ onReturnFromSettings = context.activityResultRegistry.register(
+ "SettingsResult",
+ ActivityResultContracts.StartActivityForResult()
+ ) { updateScreenLayout() }
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -129,7 +149,12 @@ 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
}
@@ -154,15 +179,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireActivity(),
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
- if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
+ if (binding.drawerLayout.isOpen) {
+ binding.drawerLayout.close()
+ } else {
+ binding.drawerLayout.open()
+ }
}
- })
+ }
+ )
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity())
- .collect { updateCurrentLayout(requireActivity() as EmulationActivity, it) }
+ .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
+ }
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ if (emulationActivity?.isInPictureInPictureMode == true) {
+ if (binding.drawerLayout.isOpen) {
+ binding.drawerLayout.close()
+ }
+ if (EmulationMenuSettings.showOverlay) {
+ binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false }
+ }
+ } else {
+ if (EmulationMenuSettings.showOverlay) {
+ binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true }
+ }
+ if (!isInFoldableLayout) {
+ if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT
+ } else {
+ binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE
+ }
+ }
+ if (!binding.surfaceInputOverlay.isInEditMode) {
+ refreshInputOverlay()
}
}
}
@@ -173,16 +229,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
DirectoryInitialization.start(requireContext())
}
- 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)
- }
- )
+ updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
@@ -243,31 +290,72 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
- private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt()
-
- fun updateCurrentLayout(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.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx
- binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx)
- }
+ @SuppressLint("SourceLockedOrientationActivity")
+ private fun updateOrientation() {
+ emulationActivity?.let {
+ it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
+ Settings.LayoutOption_MobileLandscape ->
+ ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+ Settings.LayoutOption_MobilePortrait ->
+ ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+ Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
- it.isSeparating
- } ?: false
+ }
+ }
+
+ private fun updateScreenLayout() {
+ 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()
+ updateOrientation()
+ }
+
+ private 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) {
+ // Restrict emulation and overlays to the top of the screen
+ binding.emulationContainer.layoutParams.height = it.bounds.top
+ binding.overlayContainer.layoutParams.height = it.bounds.top
+ // Restrict input and menu drawer to the bottom of the screen
+ binding.inputContainer.layoutParams.height = it.bounds.bottom
+ binding.inGameMenu.layoutParams.height = it.bounds.bottom
+
+ isInFoldableLayout = true
+ binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
+ refreshInputOverlay()
+ }
+ }
+ 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.inputContainer.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
+ binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ isInFoldableLayout = false
+ updateOrientation()
+ onConfigurationChanged(resources.configuration)
}
- binding.surfaceInputOverlay.requestLayout()
- binding.inGameMenu.requestLayout()
+ binding.emulationContainer.requestLayout()
+ binding.inputContainer.requestLayout()
binding.overlayContainer.requestLayout()
+ binding.inGameMenu.requestLayout()
}
override fun surfaceCreated(holder: SurfaceHolder) {
@@ -397,7 +485,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.show()
}
+ @SuppressLint("SourceLockedOrientationActivity")
private fun startConfiguringControls() {
+ // Lock the current orientation to prevent editing inconsistencies
+ if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
+ emulationActivity?.let {
+ it.requestedOrientation =
+ if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+ } else {
+ ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+ }
+ }
+ }
binding.doneControlConfig.visibility = View.VISIBLE
binding.surfaceInputOverlay.setIsInEditMode(true)
}
@@ -405,6 +505,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun stopConfiguringControls() {
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
+ // Unlock the orientation if it was locked for editing
+ if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
+ emulationActivity?.let {
+ it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ }
+ }
}
@SuppressLint("SetTextI18n")
@@ -414,18 +520,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
inputScaleSlider.apply {
valueTo = 150F
value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
- addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
- inputScaleValue.text = "${value.toInt()}%"
- setControlScale(value.toInt())
- })
+ addOnChangeListener(
+ Slider.OnChangeListener { _, value, _ ->
+ inputScaleValue.text = "${value.toInt()}%"
+ setControlScale(value.toInt())
+ }
+ )
}
inputOpacitySlider.apply {
valueTo = 100F
value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
- addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
- inputOpacityValue.text = "${value.toInt()}%"
- setControlOpacity(value.toInt())
- })
+ addOnChangeListener(
+ Slider.OnChangeListener { _, value, _ ->
+ inputOpacityValue.text = "${value.toInt()}%"
+ setControlOpacity(value.toInt())
+ }
+ )
}
inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
@@ -457,7 +567,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.inGameMenu
+ ) { v: View, windowInsets: WindowInsetsCompat ->
val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
var left = 0
var right = 0
@@ -577,8 +689,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
state = State.PAUSED
}
- State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
- else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
+ State.PAUSED -> Log.warning(
+ "[EmulationFragment] Surface cleared while emulation paused."
+ )
+ else -> Log.warning(
+ "[EmulationFragment] Surface cleared while emulation stopped."
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 536163eb6..6f8adbba5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -103,7 +103,9 @@ class HomeSettingsFragment : Fragment() {
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
- ) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
+ ) {
+ mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
+ },
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
@@ -225,7 +227,11 @@ class HomeSettingsFragment : Fragment() {
val intent = Intent(action)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
- intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ intent.addFlags(
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
+ Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
return intent
}
@@ -307,7 +313,9 @@ class HomeSettingsFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
index 36e63bb9e..e1495ee8c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
@@ -15,6 +15,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import java.io.BufferedOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.io.FilenameFilter
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -24,14 +32,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.getPublicFilesDir
import org.yuzu.yuzu_emu.utils.FileUtil
-import java.io.BufferedOutputStream
-import java.io.File
-import java.io.FileOutputStream
-import java.io.FilenameFilter
-import java.time.LocalDateTime
-import java.time.format.DateTimeFormatter
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
class ImportExportSavesFragment : DialogFragment() {
private val context = YuzuApplication.appContext
@@ -98,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() {
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
- LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
@@ -106,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() {
saveFolder.walkTopDown().forEach { file ->
val zipFileName =
file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
- if (zipFileName == "")
+ if (zipFileName == "") {
return@forEach
+ }
val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
zos.putNextEntry(entry)
- if (file.isFile)
+ if (file.isFile) {
file.inputStream().use { fis -> fis.copyTo(zos) }
+ }
}
}
lastZipCreated = outputZipFile
@@ -137,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() {
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
- context, DocumentsContract.buildDocumentUri(
+ context,
+ DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index c7880d8cc..739b26f99 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -14,7 +14,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
-
class IndeterminateProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index 59141e823..b6e9129f7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -113,7 +113,9 @@ class LicensesFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index adbe3696b..dd6c895fd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
+import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter
@@ -29,8 +30,6 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
-import org.yuzu.yuzu_emu.utils.Log
-import java.util.Locale
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -130,15 +129,15 @@ class SearchFragment : Fragment() {
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
- FileUtil.hasExtension(it.path, "xci")
- || FileUtil.hasExtension(it.path, "nsp")
+ FileUtil.hasExtension(it.path, "xci") ||
+ FileUtil.hasExtension(it.path, "nsp")
}
else -> baseList
}
- if (binding.searchText.text.toString().isEmpty()
- && binding.chipGroup.checkedChipId != View.NO_ID
+ if (binding.searchText.text.toString().isEmpty() &&
+ binding.chipGroup.checkedChipId != View.NO_ID
) {
gamesViewModel.setSearchedGames(filteredList)
return
@@ -173,14 +172,16 @@ class SearchFragment : Fragment() {
private fun focusSearch() {
if (_binding != null) {
binding.searchText.requestFocus()
- val imm =
- requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
+ val imm = requireActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
}
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 258773380..6c4ddaf6b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -25,6 +25,7 @@ import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
+import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.SetupAdapter
@@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.GameHelper
-import java.io.File
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@@ -82,7 +82,8 @@ class SetupFragment : Fragment() {
requireActivity().finish()
}
}
- })
+ }
+ )
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
@@ -148,14 +149,20 @@ class SetupFragment : Fragment() {
R.drawable.ic_add,
true,
R.string.add_games,
- { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
+ {
+ mainActivity.getGamesDirectory.launch(
+ Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
+ )
+ },
true,
R.string.add_games_warning,
R.string.add_games_warning_description,
R.string.add_games_warning_help,
{
val preferences =
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ PreferenceManager.getDefaultSharedPreferences(
+ YuzuApplication.appContext
+ )
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
}
)
@@ -260,7 +267,9 @@ class SetupFragment : Fragment() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
- if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+ if (!it &&
+ !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
+ ) {
PermissionDeniedDialogFragment().show(
childFragmentManager,
PermissionDeniedDialogFragment.TAG
@@ -315,7 +324,9 @@ class SetupFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.setPadding(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
index be5e4c86c..bdd6ea628 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
@@ -44,7 +44,9 @@ class AutofitGridLayoutManager(
override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) {
val width = width
val height = height
- if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
+ if (columnWidth > 0 && width > 0 && height > 0 &&
+ (isColumnWidthChanged || lastWidth != width || lastHeight != height)
+ ) {
val totalSpace: Int = if (orientation == VERTICAL) {
width - paddingRight - paddingLeft
} else {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index 35d8000c5..6a048e39f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -4,9 +4,9 @@
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
+import java.util.HashSet
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
-import java.util.HashSet
@Parcelize
@Serializable
@@ -23,8 +23,9 @@ class Game(
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
override fun equals(other: Any?): Boolean {
- if (other !is Game)
+ if (other !is Game) {
return false
+ }
return hashCode() == other.hashCode()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index d9b301210..1fe42f922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
+import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -20,7 +21,6 @@ import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
-import java.util.Locale
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
@@ -99,8 +99,9 @@ class GamesViewModel : ViewModel() {
}
fun reloadGames(directoryChanged: Boolean) {
- if (isReloading.value == true)
+ if (isReloading.value == true) {
return
+ }
_isReloading.postValue(true)
viewModelScope.launch {
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..6251ec783 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
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
-import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Point
@@ -24,6 +23,8 @@ import android.view.WindowInsets
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.window.layout.WindowMetricsCalculator
+import kotlin.math.max
+import kotlin.math.min
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
import org.yuzu.yuzu_emu.NativeLibrary.StickType
@@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
-import kotlin.math.max
-import kotlin.math.min
/**
* Draws the interactive input overlay on top of the
* [SurfaceView] that is rendering emulation.
*/
-class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs),
+class InputOverlay(context: Context, attrs: AttributeSet?) :
+ SurfaceView(context, attrs),
OnTouchListener {
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
@@ -51,12 +51,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
private lateinit var windowInsets: WindowInsets
+ var orientation = LANDSCAPE
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
windowInsets = rootWindowInsets
- if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) {
+ if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
defaultOverlay()
}
@@ -93,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
var shouldUpdateView = false
val playerIndex =
- if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
+ if (NativeLibrary.isHandheldOnly()) {
+ NativeLibrary.ConsoleDevice
+ } else {
+ NativeLibrary.Player1Device
+ }
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
@@ -156,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
shouldUpdateView = true
}
- if (shouldUpdateView)
+ if (shouldUpdateView) {
invalidate()
+ }
if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
return true
@@ -233,10 +240,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
val fingerPositionX = event.getX(pointerIndex).toInt()
val fingerPositionY = event.getY(pointerIndex).toInt()
- // TODO: Provide support for portrait layout
- //val orientation =
- // if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
-
for (button in overlayButtons) {
// Determine the button state to apply based on the MotionEvent action flag.
when (event.action and MotionEvent.ACTION_MASK) {
@@ -245,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If no button is being moved now, remember the currently touched button to move.
if (buttonBeingConfigured == null &&
button.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ fingerPositionX,
+ fingerPositionY
+ )
) {
buttonBeingConfigured = button
buttonBeingConfigured!!.onConfigureTouch(event)
@@ -266,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
buttonBeingConfigured!!.buttonId,
buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(),
- ""
+ orientation
)
buttonBeingConfigured = null
}
@@ -299,7 +302,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
dpadBeingConfigured!!.upId,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
- ""
+ orientation
)
dpadBeingConfigured = null
}
@@ -311,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
joystick.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ fingerPositionX,
+ fingerPositionY
+ )
) {
joystickBeingConfigured = joystick
joystickBeingConfigured!!.onConfigureTouch(event)
@@ -330,7 +333,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
joystickBeingConfigured!!.buttonId,
joystickBeingConfigured!!.bounds.centerX(),
joystickBeingConfigured!!.bounds.centerY(),
- ""
+ orientation
)
joystickBeingConfigured = null
}
@@ -533,8 +536,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
overlayButtons.clear()
overlayDpads.clear()
overlayJoysticks.clear()
- val orientation =
- if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
// Add all the enabled overlay items back to the HashSet.
if (EmulationMenuSettings.showOverlay) {
@@ -548,8 +549,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
val min = windowSize.first
val max = windowSize.second
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
- .putFloat("$sharedPrefsId$orientation-X", (x - min.x).toFloat() / max.x)
- .putFloat("$sharedPrefsId$orientation-Y", (y - min.y).toFloat() / max.y)
+ .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x)
+ .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y)
.apply()
}
@@ -558,145 +559,250 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
}
private fun defaultOverlay() {
- if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) {
- defaultOverlayLandscape()
+ if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
+ defaultOverlayByLayout(orientation)
}
resetButtonPlacement()
preferences.edit()
- .putBoolean(Settings.PREF_OVERLAY_INIT, true)
+ .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true)
.apply()
}
fun resetButtonPlacement() {
- defaultOverlayLandscape()
+ defaultOverlayByLayout(orientation)
refreshControls()
}
- private fun defaultOverlayLandscape() {
+ private val landscapeResources = arrayOf(
+ R.integer.SWITCH_BUTTON_A_X,
+ R.integer.SWITCH_BUTTON_A_Y,
+ R.integer.SWITCH_BUTTON_B_X,
+ R.integer.SWITCH_BUTTON_B_Y,
+ R.integer.SWITCH_BUTTON_X_X,
+ R.integer.SWITCH_BUTTON_X_Y,
+ R.integer.SWITCH_BUTTON_Y_X,
+ R.integer.SWITCH_BUTTON_Y_Y,
+ R.integer.SWITCH_TRIGGER_ZL_X,
+ R.integer.SWITCH_TRIGGER_ZL_Y,
+ R.integer.SWITCH_TRIGGER_ZR_X,
+ R.integer.SWITCH_TRIGGER_ZR_Y,
+ R.integer.SWITCH_BUTTON_DPAD_X,
+ R.integer.SWITCH_BUTTON_DPAD_Y,
+ R.integer.SWITCH_TRIGGER_L_X,
+ R.integer.SWITCH_TRIGGER_L_Y,
+ R.integer.SWITCH_TRIGGER_R_X,
+ R.integer.SWITCH_TRIGGER_R_Y,
+ R.integer.SWITCH_BUTTON_PLUS_X,
+ R.integer.SWITCH_BUTTON_PLUS_Y,
+ R.integer.SWITCH_BUTTON_MINUS_X,
+ R.integer.SWITCH_BUTTON_MINUS_Y,
+ R.integer.SWITCH_BUTTON_HOME_X,
+ R.integer.SWITCH_BUTTON_HOME_Y,
+ R.integer.SWITCH_BUTTON_CAPTURE_X,
+ R.integer.SWITCH_BUTTON_CAPTURE_Y,
+ R.integer.SWITCH_STICK_R_X,
+ R.integer.SWITCH_STICK_R_Y,
+ R.integer.SWITCH_STICK_L_X,
+ R.integer.SWITCH_STICK_L_Y
+ )
+
+ private val portraitResources = arrayOf(
+ R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
+ R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
+ R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
+ R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
+ R.integer.SWITCH_STICK_R_X_PORTRAIT,
+ R.integer.SWITCH_STICK_R_Y_PORTRAIT,
+ R.integer.SWITCH_STICK_L_X_PORTRAIT,
+ R.integer.SWITCH_STICK_L_Y_PORTRAIT
+ )
+
+ private val foldableResources = arrayOf(
+ R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
+ R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
+ R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
+ R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
+ R.integer.SWITCH_STICK_R_X_FOLDABLE,
+ R.integer.SWITCH_STICK_R_Y_FOLDABLE,
+ R.integer.SWITCH_STICK_L_X_FOLDABLE,
+ R.integer.SWITCH_STICK_L_Y_FOLDABLE
+ )
+
+ private fun getResourceValue(orientation: String, position: Int): Float {
+ return when (orientation) {
+ PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
+ FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
+ else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
+ }
+ }
+
+ private fun defaultOverlayByLayout(orientation: String) {
// Each value represents the position of the button in relation to the screen size without insets.
preferences.edit()
.putFloat(
- ButtonType.BUTTON_A.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_A_X).toFloat() / 1000
+ ButtonType.BUTTON_A.toString() + "-X$orientation",
+ getResourceValue(orientation, 0)
)
.putFloat(
- ButtonType.BUTTON_A.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_A_Y).toFloat() / 1000
+ ButtonType.BUTTON_A.toString() + "-Y$orientation",
+ getResourceValue(orientation, 1)
)
.putFloat(
- ButtonType.BUTTON_B.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_B_X).toFloat() / 1000
+ ButtonType.BUTTON_B.toString() + "-X$orientation",
+ getResourceValue(orientation, 2)
)
.putFloat(
- ButtonType.BUTTON_B.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_B_Y).toFloat() / 1000
+ ButtonType.BUTTON_B.toString() + "-Y$orientation",
+ getResourceValue(orientation, 3)
)
.putFloat(
- ButtonType.BUTTON_X.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_X_X).toFloat() / 1000
+ ButtonType.BUTTON_X.toString() + "-X$orientation",
+ getResourceValue(orientation, 4)
)
.putFloat(
- ButtonType.BUTTON_X.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_X_Y).toFloat() / 1000
+ ButtonType.BUTTON_X.toString() + "-Y$orientation",
+ getResourceValue(orientation, 5)
)
.putFloat(
- ButtonType.BUTTON_Y.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_Y_X).toFloat() / 1000
+ ButtonType.BUTTON_Y.toString() + "-X$orientation",
+ getResourceValue(orientation, 6)
)
.putFloat(
- ButtonType.BUTTON_Y.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_Y_Y).toFloat() / 1000
+ ButtonType.BUTTON_Y.toString() + "-Y$orientation",
+ getResourceValue(orientation, 7)
)
.putFloat(
- ButtonType.TRIGGER_ZL.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_X).toFloat() / 1000
+ ButtonType.TRIGGER_ZL.toString() + "-X$orientation",
+ getResourceValue(orientation, 8)
)
.putFloat(
- ButtonType.TRIGGER_ZL.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y).toFloat() / 1000
+ ButtonType.TRIGGER_ZL.toString() + "-Y$orientation",
+ getResourceValue(orientation, 9)
)
.putFloat(
- ButtonType.TRIGGER_ZR.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_X).toFloat() / 1000
+ ButtonType.TRIGGER_ZR.toString() + "-X$orientation",
+ getResourceValue(orientation, 10)
)
.putFloat(
- ButtonType.TRIGGER_ZR.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y).toFloat() / 1000
+ ButtonType.TRIGGER_ZR.toString() + "-Y$orientation",
+ getResourceValue(orientation, 11)
)
.putFloat(
- ButtonType.DPAD_UP.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_X).toFloat() / 1000
+ ButtonType.DPAD_UP.toString() + "-X$orientation",
+ getResourceValue(orientation, 12)
)
.putFloat(
- ButtonType.DPAD_UP.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y).toFloat() / 1000
+ ButtonType.DPAD_UP.toString() + "-Y$orientation",
+ getResourceValue(orientation, 13)
)
.putFloat(
- ButtonType.TRIGGER_L.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_TRIGGER_L_X).toFloat() / 1000
+ ButtonType.TRIGGER_L.toString() + "-X$orientation",
+ getResourceValue(orientation, 14)
)
.putFloat(
- ButtonType.TRIGGER_L.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_TRIGGER_L_Y).toFloat() / 1000
+ ButtonType.TRIGGER_L.toString() + "-Y$orientation",
+ getResourceValue(orientation, 15)
)
.putFloat(
- ButtonType.TRIGGER_R.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_TRIGGER_R_X).toFloat() / 1000
+ ButtonType.TRIGGER_R.toString() + "-X$orientation",
+ getResourceValue(orientation, 16)
)
.putFloat(
- ButtonType.TRIGGER_R.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_TRIGGER_R_Y).toFloat() / 1000
+ ButtonType.TRIGGER_R.toString() + "-Y$orientation",
+ getResourceValue(orientation, 17)
)
.putFloat(
- ButtonType.BUTTON_PLUS.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_X).toFloat() / 1000
+ ButtonType.BUTTON_PLUS.toString() + "-X$orientation",
+ getResourceValue(orientation, 18)
)
.putFloat(
- ButtonType.BUTTON_PLUS.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y).toFloat() / 1000
+ ButtonType.BUTTON_PLUS.toString() + "-Y$orientation",
+ getResourceValue(orientation, 19)
)
.putFloat(
- ButtonType.BUTTON_MINUS.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_X).toFloat() / 1000
+ ButtonType.BUTTON_MINUS.toString() + "-X$orientation",
+ getResourceValue(orientation, 20)
)
.putFloat(
- ButtonType.BUTTON_MINUS.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y).toFloat() / 1000
+ ButtonType.BUTTON_MINUS.toString() + "-Y$orientation",
+ getResourceValue(orientation, 21)
)
.putFloat(
- ButtonType.BUTTON_HOME.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_HOME_X).toFloat() / 1000
+ ButtonType.BUTTON_HOME.toString() + "-X$orientation",
+ getResourceValue(orientation, 22)
)
.putFloat(
- ButtonType.BUTTON_HOME.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_HOME_Y).toFloat() / 1000
+ ButtonType.BUTTON_HOME.toString() + "-Y$orientation",
+ getResourceValue(orientation, 23)
)
.putFloat(
- ButtonType.BUTTON_CAPTURE.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X)
- .toFloat() / 1000
+ ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation",
+ getResourceValue(orientation, 24)
)
.putFloat(
- ButtonType.BUTTON_CAPTURE.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y)
- .toFloat() / 1000
+ ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation",
+ getResourceValue(orientation, 25)
)
.putFloat(
- ButtonType.STICK_R.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_STICK_R_X).toFloat() / 1000
+ ButtonType.STICK_R.toString() + "-X$orientation",
+ getResourceValue(orientation, 26)
)
.putFloat(
- ButtonType.STICK_R.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_STICK_R_Y).toFloat() / 1000
+ ButtonType.STICK_R.toString() + "-Y$orientation",
+ getResourceValue(orientation, 27)
)
.putFloat(
- ButtonType.STICK_L.toString() + "-X",
- resources.getInteger(R.integer.SWITCH_STICK_L_X).toFloat() / 1000
+ ButtonType.STICK_L.toString() + "-X$orientation",
+ getResourceValue(orientation, 28)
)
.putFloat(
- ButtonType.STICK_L.toString() + "-Y",
- resources.getInteger(R.integer.SWITCH_STICK_L_Y).toFloat() / 1000
+ ButtonType.STICK_L.toString() + "-Y$orientation",
+ getResourceValue(orientation, 29)
)
.apply()
}
@@ -709,13 +815,17 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
private val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ const val LANDSCAPE = ""
+ const val PORTRAIT = "_Portrait"
+ const val FOLDABLE = "_Foldable"
+
/**
* Resizes a [Bitmap] by a given scale factor
*
* @param context Context for getting the vector drawable
* @param drawableId The ID of the drawable to scale.
* @param scale The scale factor for the bitmap.
- * @return The scaled [Bitmap]
+ * @return The scaled [Bitmap]
*/
private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap {
val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable
@@ -749,14 +859,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
* Gets the safe screen size for drawing the overlay
*
* @param context Context for getting the window metrics
- * @return A pair of points, the first being the top left corner of the safe area,
+ * @return A pair of points, the first being the top left corner of the safe area,
* the second being the bottom right corner of the safe area
*/
private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
// 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
@@ -768,10 +877,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null) {
- if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
- insets.boundingRectTop.bottom.toFloat() else maxY
- if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
- insets.boundingRectRight.left.toFloat() else maxX
+ if (insets.boundingRectTop.bottom != 0 &&
+ insets.boundingRectTop.bottom > maxY / 2
+ ) {
+ maxY = insets.boundingRectTop.bottom.toFloat()
+ }
+ if (insets.boundingRectRight.left != 0 &&
+ insets.boundingRectRight.left > maxX / 2
+ ) {
+ maxX = insets.boundingRectRight.left.toFloat()
+ }
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
@@ -878,8 +993,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
- val xKey = "$buttonId$orientation-X"
- val yKey = "$buttonId$orientation-Y"
+ val xKey = "$buttonId-X$orientation"
+ val yKey = "$buttonId-Y$orientation"
val drawableXPercent = sPrefs.getFloat(xKey, 0f)
val drawableYPercent = sPrefs.getFloat(yKey, 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
@@ -959,8 +1074,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu.
- val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-X", 0f)
- val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-Y", 0f)
+ val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f)
+ val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt()
val width = overlayDrawable.width
@@ -1026,8 +1141,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
- val drawableXPercent = sPrefs.getFloat("$button$orientation-X", 0f)
- val drawableYPercent = sPrefs.getFloat("$button$orientation-Y", 0f)
+ val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f)
+ val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt()
val outerScale = 1.66f
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
index 43d664d21..8aef6f5a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -133,7 +133,10 @@ class InputOverlayDrawableDpad(
downButtonState = axisY > VIRT_AXIS_DEADZONE
leftButtonState = axisX < -VIRT_AXIS_DEADZONE
rightButtonState = axisX > VIRT_AXIS_DEADZONE
- return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState
+ return oldUpState != upButtonState ||
+ oldDownState != downButtonState ||
+ oldLeftState != leftButtonState ||
+ oldRightState != rightButtonState
}
return false
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index f1d32192a..fb48f584d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -9,12 +9,12 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
/**
* Custom [BitmapDrawable] that is capable
@@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick(
private fun setInnerBounds() {
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
- if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
- virtBounds.centerX() + virtBounds.width() / 2
- if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
- virtBounds.centerX() - virtBounds.width() / 2
- if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
- virtBounds.centerY() + virtBounds.height() / 2
- if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
- virtBounds.centerY() - virtBounds.height() / 2
+ if (x > virtBounds.centerX() + virtBounds.width() / 2) {
+ x =
+ virtBounds.centerX() + virtBounds.width() / 2
+ }
+ if (x < virtBounds.centerX() - virtBounds.width() / 2) {
+ x =
+ virtBounds.centerX() - virtBounds.width() / 2
+ }
+ if (y > virtBounds.centerY() + virtBounds.height() / 2) {
+ y =
+ virtBounds.centerY() + virtBounds.height() / 2
+ }
+ if (y < virtBounds.centerY() - virtBounds.height() / 2) {
+ y =
+ virtBounds.centerY() - virtBounds.height() / 2
+ }
val width = pressedStateInnerBitmap.bounds.width() / 2
val height = pressedStateInnerBitmap.bounds.height() / 2
defaultStateInnerBitmap.setBounds(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 97eef40d2..b0156dca5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -99,7 +99,9 @@ class GamesFragment : Fragment() {
}
shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
if (shouldSwapData) {
- (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
+ (binding.gridGames.adapter as GameAdapter).submitList(
+ gamesViewModel.games.value!!
+ )
gamesViewModel.setShouldSwapData(false)
}
}
@@ -128,7 +130,9 @@ class GamesFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 041d16f3a..cc1d87f1b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -26,6 +26,9 @@ import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
+import java.io.File
+import java.io.FilenameFilter
+import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -43,9 +46,6 @@ import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.*
-import java.io.File
-import java.io.FilenameFilter
-import java.io.IOException
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@@ -86,7 +86,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
ThemeHelper.SYSTEM_BAR_ALPHA
)
)
- if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
+ if (InsetsHelper.getSystemGestureType(applicationContext) !=
+ InsetsHelper.GESTURE_NAVIGATION
+ ) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -172,7 +174,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding.navigationView.height.toFloat() * 2
translationY(0f)
} else {
- if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (ViewCompat.getLayoutDirection(binding.navigationView) ==
+ ViewCompat.LAYOUT_DIRECTION_LTR
+ ) {
binding.navigationView.translationX =
binding.navigationView.width.toFloat() * -2
translationX(0f)
@@ -189,7 +193,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (smallLayout) {
translationY(binding.navigationView.height.toFloat() * 2)
} else {
- if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (ViewCompat.getLayoutDirection(binding.navigationView) ==
+ ViewCompat.LAYOUT_DIRECTION_LTR
+ ) {
translationX(binding.navigationView.width.toFloat() * -2)
} else {
translationX(binding.navigationView.width.toFloat() * 2)
@@ -234,7 +240,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams
mlpStatusShade.height = insets.top
@@ -256,8 +264,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
contentResolver.takePersistableUriPermission(
result,
@@ -281,8 +290,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
if (!FileUtil.hasExtension(result, "keys")) {
MessageDialogFragment.newInstance(
@@ -324,8 +334,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
val inputZip = contentResolver.openInputStream(result)
if (inputZip == null) {
@@ -376,8 +387,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getAmiiboKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
if (!FileUtil.hasExtension(result, "bin")) {
MessageDialogFragment.newInstance(
@@ -418,8 +430,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -470,8 +483,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val installGameUpdate =
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
- if (it == null)
+ if (it == null) {
return@registerForActivityResult
+ }
IndeterminateProgressDialogFragment.newInstance(
this@MainActivity,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
index 791cea904..eeefcdf20 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
@@ -19,7 +19,9 @@ class ControllerMappingHelper {
// The two analog triggers generate analog motion events as well as a keycode.
// We always prefer to use the analog values, so throw away the button press
keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
- } else false
+ } else {
+ false
+ }
}
/**
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 36c479e6c..2ee63697e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -4,8 +4,8 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
-import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
+import org.yuzu.yuzu_emu.NativeLibrary
object DirectoryInitialization {
private var userPath: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
index cc8ea6b9d..cf226ad94 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
@@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.File
import java.util.*
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree {
private var root: DocumentsNode? = null
@@ -29,13 +29,20 @@ class DocumentsTree {
val node = resolvePath(filepath)
return if (node == null || node.isDirectory) {
0
- } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
+ } else {
+ FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
+ }
}
fun exists(filepath: String): Boolean {
return resolvePath(filepath) != null
}
+ fun isDirectory(filepath: String): Boolean {
+ val node = resolvePath(filepath)
+ return node != null && node.isDirectory
+ }
+
private fun resolvePath(filepath: String): DocumentsNode? {
val tokens = StringTokenizer(filepath, File.separator, false)
var iterator = root
@@ -106,7 +113,9 @@ class DocumentsTree {
fun isNativePath(path: String): Boolean {
return if (path.isNotEmpty()) {
path[0] == '/'
- } else false
+ } else {
+ false
+ }
}
}
}
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/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 492b1ad91..9f3bbe56f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -9,8 +9,6 @@ import android.net.Uri
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import androidx.documentfile.provider.DocumentFile
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
@@ -19,6 +17,8 @@ import java.io.InputStream
import java.net.URLDecoder
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.MinimalDocumentFile
object FileUtil {
const val PATH_TREE = "tree"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
index dc9b7c744..086d17606 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
@@ -54,7 +54,7 @@ class ForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
- return START_NOT_STICKY;
+ return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 42b207618..ee9f3e570 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
+import java.util.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
-import java.util.*
object GameHelper {
const val KEY_GAME_PATH = "game_path"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index 528011d7f..dad159481 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipInputStream
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
index 70bdb4097..a4e64070a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
@@ -3,12 +3,12 @@
package org.yuzu.yuzu_emu.utils
-import org.json.JSONException
-import org.json.JSONObject
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
+import org.json.JSONException
+import org.json.JSONObject
class GpuDriverMetadata(metadataFilePath: String) {
var name: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index 24e999b29..e963dfbc1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils
import android.view.KeyEvent
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary
import kotlin.math.sqrt
+import org.yuzu.yuzu_emu.NativeLibrary
class InputHandler {
fun initialize() {
@@ -68,7 +68,11 @@ class InputHandler {
6 -> NativeLibrary.Player6Device
7 -> NativeLibrary.Player7Device
8 -> NativeLibrary.Player8Device
- else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
+ else -> if (NativeLibrary.isHandheldOnly()) {
+ NativeLibrary.ConsoleDevice
+ } else {
+ NativeLibrary.Player1Device
+ }
}
}
@@ -107,7 +111,11 @@ class InputHandler {
}
private fun getAxisToButton(axis: Float): Int {
- return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED
+ return if (axis > 0.5f) {
+ NativeLibrary.ButtonState.PRESSED
+ } else {
+ NativeLibrary.ButtonState.RELEASED
+ }
}
private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
@@ -287,7 +295,6 @@ class InputHandler {
}
}
-
private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
// Joycon support is half dead. Right joystick doesn't work
val playerNumber = getPlayerNumber(event.device.controllerNumber)
@@ -355,6 +362,4 @@ class InputHandler {
)
}
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
index 19c53c481..595f0d284 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
@@ -4,9 +4,7 @@
package org.yuzu.yuzu_emu.utils
import android.annotation.SuppressLint
-import android.app.Activity
import android.content.Context
-import android.graphics.Rect
object InsetsHelper {
const val THREE_BUTTON_NAVIGATION = 0
@@ -20,12 +18,8 @@ object InsetsHelper {
resources.getIdentifier("config_navBarInteractionMode", "integer", "android")
return if (resourceId != 0) {
resources.getInteger(resourceId)
- } else 0
- }
-
- fun getBottomPaddingRequired(activity: Activity): Int {
- val visibleFrame = Rect()
- activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
- return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
+ } else {
+ 0
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 344dd8a0a..68ed66565 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
@@ -13,8 +13,8 @@ import android.nfc.tech.NfcA
import android.os.Build
import android.os.Handler
import android.os.Looper
-import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
+import org.yuzu.yuzu_emu.NativeLibrary
class NfcReader(private val activity: Activity) {
private var nfcAdapter: NfcAdapter? = null
@@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) {
pendingIntent = PendingIntent.getActivity(
activity,
- 0, Intent(activity, activity.javaClass),
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ 0,
+ Intent(activity, activity.javaClass),
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- else PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
@@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) {
fun onNewIntent(intent: Intent) {
val action = intent.action
- if (NfcAdapter.ACTION_TAG_DISCOVERED != action
- && NfcAdapter.ACTION_TECH_DISCOVERED != action
- && NfcAdapter.ACTION_NDEF_DISCOVERED != action
+ if (NfcAdapter.ACTION_TAG_DISCOVERED != action &&
+ NfcAdapter.ACTION_TECH_DISCOVERED != action &&
+ NfcAdapter.ACTION_NDEF_DISCOVERED != action
) {
return
}
@@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) {
}
private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
- val bufferSize = amiibo.maxTransceiveLength;
+ val bufferSize = amiibo.maxTransceiveLength
val tagSize = 0x21C
val pageSize = 4
val lastPage = tagSize / pageSize - 1
@@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) {
val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
} catch (e: IOException) {
- return null;
+ return null
}
}
return tagData
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
index 87ee7f2e6..00e58faec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
@@ -11,30 +11,34 @@ import java.io.Serializable
object SerializableHelper {
inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializable(key, T::class.java)
- else
+ } else {
getSerializable(key) as? T
+ }
}
inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializableExtra(key, T::class.java)
- else
+ } else {
getSerializableExtra(key) as? T
+ }
}
inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
- else
+ } else {
getParcelable(key) as? T
+ }
}
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
- else
+ } else {
getParcelableExtra(key) as? T
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index e55767c0f..f312e24cf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -3,21 +3,19 @@
package org.yuzu.yuzu_emu.utils
-import android.app.Activity
import android.content.res.Configuration
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
-import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
+import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
-import kotlin.math.roundToInt
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
@@ -36,8 +34,8 @@ object ThemeHelper {
// Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds.
- if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
- && isNightMode(activity)
+ if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
+ isNightMode(activity)
) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
}
@@ -46,8 +44,10 @@ object ThemeHelper {
@ColorInt
fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int {
return Color.argb(
- (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color),
- Color.green(color), Color.blue(color)
+ (alphaFactor * Color.alpha(color)).roundToInt(),
+ Color.red(color),
+ Color.green(color),
+ Color.blue(color)
)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
index d89a89983..685ccaa76 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
@@ -38,8 +38,8 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
newWidth = width
newHeight = (width / aspectRatio).roundToInt()
}
- val left = (width - newWidth) / 2;
- val top = (height - newHeight) / 2;
+ val left = (width - newWidth) / 2
+ val top = (height - newHeight) / 2
setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
} else {
setLeftTopRightBottom(0, 0, width, height)
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 4091c23d1..f9617202b 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -202,6 +202,11 @@ public:
return m_is_running;
}
+ bool IsPaused() const {
+ std::scoped_lock lock(m_mutex);
+ return m_is_running && m_is_paused;
+ }
+
const Core::PerfStatsResults& PerfStats() const {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats;
@@ -287,11 +292,13 @@ public:
void PauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Pause();
+ m_is_paused = true;
}
void UnPauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Run();
+ m_is_paused = false;
}
void HaltEmulation() {
@@ -473,6 +480,7 @@ private:
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
bool m_is_running{};
+ bool m_is_paused{};
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
@@ -583,6 +591,11 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz) {
+ return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
+}
+
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
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 @@
+<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>
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 @@
+<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>
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..e54a10e8f 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -12,49 +12,65 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- This is what everything is rendered to during emulation -->
- <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView
- android:id="@+id/surface_emulation"
+ <FrameLayout
+ android:id="@+id/emulation_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="false"
- android:focusableInTouchMode="false" />
+ android:layout_height="match_parent">
+
+ <!-- This is what everything is rendered to during emulation -->
+ <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView
+ 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" />
+
+ </FrameLayout>
<FrameLayout
- android:id="@+id/overlay_container"
+ android:id="@+id/input_container"
android:layout_width="match_parent"
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"
+ <!-- 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="center"
+ android:focusable="true"
+ android:focusableInTouchMode="true" />
+
+ <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>
+
+ <FrameLayout
+ android:id="@+id/overlay_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:focusableInTouchMode="true" />
+ android:layout_height="match_parent">
- <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" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
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 @@
<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>
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index bc614b81d..2e93b408c 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -34,4 +34,68 @@
<integer name="SWITCH_BUTTON_DPAD_X">260</integer>
<integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
+ <!-- Default SWITCH portrait layout -->
+ <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
+ <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer>
+ <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer>
+ <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer>
+ <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer>
+ <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer>
+ <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer>
+ <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer>
+ <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer>
+ <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer>
+ <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer>
+ <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer>
+ <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer>
+ <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer>
+ <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer>
+ <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer>
+ <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer>
+ <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer>
+ <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer>
+ <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer>
+ <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer>
+ <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer>
+ <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer>
+ <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer>
+ <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer>
+ <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer>
+ <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer>
+ <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
+ <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
+ <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
+
+ <!-- Default SWITCH foldable layout -->
+ <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
+ <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer>
+ <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer>
+ <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer>
+ <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer>
+ <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer>
+ <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer>
+ <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer>
+ <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer>
+ <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer>
+ <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer>
+ <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer>
+ <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer>
+ <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer>
+ <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer>
+ <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer>
+ <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer>
+ <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer>
+ <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer>
+ <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer>
+ <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer>
+ <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer>
+ <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer>
+ <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer>
+ <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer>
+ <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer>
+ <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer>
+ <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
+ <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
+ <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
+
</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index c236811fa..2f2059d42 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<!-- General application strings -->
<string name="app_name" translatable="false">yuzu</string>
@@ -162,6 +162,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>
@@ -326,6 +327,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>
@@ -364,6 +370,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>
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index e1716c62d..6d66c926d 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -3,6 +3,9 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
+#ifdef ANDROID
+#include "common/fs/fs_android.h"
+#endif
#include "common/fs/path_util.h"
#include "common/logging/log.h"
@@ -525,15 +528,39 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
// Generic Filesystem Operations
bool Exists(const fs::path& path) {
+#ifdef ANDROID
+ if (Android::IsContentUri(path)) {
+ return Android::Exists(path);
+ } else {
+ return fs::exists(path);
+ }
+#else
return fs::exists(path);
+#endif
}
bool IsFile(const fs::path& path) {
+#ifdef ANDROID
+ if (Android::IsContentUri(path)) {
+ return !Android::IsDirectory(path);
+ } else {
+ return fs::is_regular_file(path);
+ }
+#else
return fs::is_regular_file(path);
+#endif
}
bool IsDir(const fs::path& path) {
+#ifdef ANDROID
+ if (Android::IsContentUri(path)) {
+ return Android::IsDirectory(path);
+ } else {
+ return fs::is_directory(path);
+ }
+#else
return fs::is_directory(path);
+#endif
}
fs::path GetCurrentDir() {
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
index bb8a52648..b441c2a12 100644
--- a/src/common/fs/fs_android.h
+++ b/src/common/fs/fs_android.h
@@ -12,7 +12,10 @@
"openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
- V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J")
+ V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
+ V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
+ "(Ljava/lang/String;)Z") \
+ V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
namespace Common::FS::Android {
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index cc0076238..7a15d8438 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -25,6 +25,8 @@ namespace FS = Common::FS;
namespace {
+constexpr size_t MaxOpenFiles = 512;
+
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
switch (mode) {
case Mode::Read:
@@ -73,28 +75,30 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
- if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
- const auto& weak = weak_iter->second;
-
- if (!weak.expired()) {
- return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
+ if (auto it = cache.find(path); it != cache.end()) {
+ if (auto file = it->second.lock(); file) {
+ return file;
}
}
- auto backing = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
-
- if (!backing) {
+ if (!FS::Exists(path) || !FS::IsFile(path)) {
return nullptr;
}
- cache.insert_or_assign(path, std::move(backing));
+ auto reference = std::make_unique<FileReference>();
+ this->InsertReferenceIntoList(*reference);
- // Cannot use make_shared as RealVfsFile constructor is private
- return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
+ auto file =
+ std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, std::move(reference), path, perms));
+ cache[path] = file;
+
+ return file;
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ cache.erase(path);
+
// Current usages of CreateFile expect to delete the contents of an existing file.
if (FS::IsFile(path)) {
FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile};
@@ -123,51 +127,22 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
- const auto cached_file_iter = cache.find(old_path);
-
- if (cached_file_iter != cache.cend()) {
- auto file = cached_file_iter->second.lock();
-
- if (!cached_file_iter->second.expired()) {
- file->Close();
- }
-
- if (!FS::RenameFile(old_path, new_path)) {
- return nullptr;
- }
-
- cache.erase(old_path);
- file->Open(new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile);
- if (file->IsOpen()) {
- cache.insert_or_assign(new_path, std::move(file));
- } else {
- LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
- }
- } else {
- ASSERT(false);
+ cache.erase(old_path);
+ cache.erase(new_path);
+ if (!FS::RenameFile(old_path, new_path)) {
return nullptr;
}
-
return OpenFile(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
- const auto cached_iter = cache.find(path);
-
- if (cached_iter != cache.cend()) {
- if (!cached_iter->second.expired()) {
- cached_iter->second.lock()->Close();
- }
- cache.erase(path);
- }
-
+ cache.erase(path);
return FS::RemoveFile(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
- // Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
@@ -176,7 +151,6 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
if (!FS::CreateDirs(path)) {
return nullptr;
}
- // Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
@@ -194,73 +168,102 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
if (!FS::RenameDir(old_path, new_path)) {
return nullptr;
}
+ return OpenDirectory(new_path, Mode::ReadWrite);
+}
- for (auto& kv : cache) {
- // If the path in the cache doesn't start with old_path, then bail on this file.
- if (kv.first.rfind(old_path, 0) != 0) {
- continue;
- }
+bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
+ const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ return FS::RemoveDirRecursively(path);
+}
- const auto file_old_path =
- FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
- auto file_new_path = FS::SanitizePath(new_path + '/' + kv.first.substr(old_path.size()),
- FS::DirectorySeparator::PlatformDefault);
- const auto& cached = cache[file_old_path];
+void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms,
+ FileReference& reference) {
+ // Temporarily remove from list.
+ this->RemoveReferenceFromList(reference);
- if (cached.expired()) {
- continue;
- }
+ // Restore file if needed.
+ if (!reference.file) {
+ this->EvictSingleReference();
- auto file = cached.lock();
- cache.erase(file_old_path);
- file->Open(file_new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile);
- if (file->IsOpen()) {
- cache.insert_or_assign(std::move(file_new_path), std::move(file));
- } else {
- LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
+ reference.file =
+ FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
+ if (reference.file) {
+ num_open_files++;
}
}
- return OpenDirectory(new_path, Mode::ReadWrite);
+ // Reinsert into list.
+ this->InsertReferenceIntoList(reference);
}
-bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
- const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
+ // Remove from list.
+ this->RemoveReferenceFromList(*reference);
- for (auto& kv : cache) {
- // If the path in the cache doesn't start with path, then bail on this file.
- if (kv.first.rfind(path, 0) != 0) {
- continue;
- }
+ // Close the file.
+ if (reference->file) {
+ reference->file.reset();
+ num_open_files--;
+ }
+}
- const auto& entry = cache[kv.first];
- if (!entry.expired()) {
- entry.lock()->Close();
- }
+void RealVfsFilesystem::EvictSingleReference() {
+ if (num_open_files < MaxOpenFiles || open_references.empty()) {
+ return;
+ }
+
+ // Get and remove from list.
+ auto& reference = open_references.back();
+ this->RemoveReferenceFromList(reference);
- cache.erase(kv.first);
+ // Close the file.
+ if (reference.file) {
+ reference.file.reset();
+ num_open_files--;
}
- return FS::RemoveDirRecursively(path);
+ // Reinsert into closed list.
+ this->InsertReferenceIntoList(reference);
+}
+
+void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) {
+ if (reference.file) {
+ open_references.push_front(reference);
+ } else {
+ closed_references.push_front(reference);
+ }
+}
+
+void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) {
+ if (reference.file) {
+ open_references.erase(open_references.iterator_to(reference));
+ } else {
+ closed_references.erase(closed_references.iterator_to(reference));
+ }
}
-RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
+RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
const std::string& path_, Mode perms_)
- : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
- path_components(FS::SplitPathComponents(path_)), perms(perms_) {}
+ : base(base_), reference(std::move(reference_)), path(path_),
+ parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
+ perms(perms_) {}
-RealVfsFile::~RealVfsFile() = default;
+RealVfsFile::~RealVfsFile() {
+ base.DropReference(std::move(reference));
+}
std::string RealVfsFile::GetName() const {
return path_components.back();
}
std::size_t RealVfsFile::GetSize() const {
- return backing->GetSize();
+ base.RefreshReference(path, perms, *reference);
+ return reference->file ? reference->file->GetSize() : 0;
}
bool RealVfsFile::Resize(std::size_t new_size) {
- return backing->SetSize(new_size);
+ base.RefreshReference(path, perms, *reference);
+ return reference->file ? reference->file->SetSize(new_size) : false;
}
VirtualDir RealVfsFile::GetContainingDirectory() const {
@@ -276,27 +279,25 @@ bool RealVfsFile::IsReadable() const {
}
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
- if (!backing->Seek(static_cast<s64>(offset))) {
+ base.RefreshReference(path, perms, *reference);
+ if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
return 0;
}
- return backing->ReadSpan(std::span{data, length});
+ return reference->file->ReadSpan(std::span{data, length});
}
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
- if (!backing->Seek(static_cast<s64>(offset))) {
+ base.RefreshReference(path, perms, *reference);
+ if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
return 0;
}
- return backing->WriteSpan(std::span{data, length});
+ return reference->file->WriteSpan(std::span{data, length});
}
bool RealVfsFile::Rename(std::string_view name) {
return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr;
}
-void RealVfsFile::Close() {
- backing->Close();
-}
-
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
// constexpr' because there is a compile error in the branch not used.
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index b92c84316..d8c900e33 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -3,8 +3,9 @@
#pragma once
+#include <map>
#include <string_view>
-#include <boost/container/flat_map.hpp>
+#include "common/intrusive_list.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
@@ -14,6 +15,11 @@ class IOFile;
namespace FileSys {
+struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {
+ std::shared_ptr<Common::FS::IOFile> file{};
+};
+
+class RealVfsFile;
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
@@ -35,7 +41,21 @@ public:
bool DeleteDirectory(std::string_view path) override;
private:
- boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache;
+ using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
+ std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
+ ReferenceListType open_references;
+ ReferenceListType closed_references;
+ size_t num_open_files{};
+
+private:
+ friend class RealVfsFile;
+ void RefreshReference(const std::string& path, Mode perms, FileReference& reference);
+ void DropReference(std::unique_ptr<FileReference>&& reference);
+ void EvictSingleReference();
+
+private:
+ void InsertReferenceIntoList(FileReference& reference);
+ void RemoveReferenceFromList(FileReference& reference);
};
// An implementation of VfsFile that represents a file on the user's computer.
@@ -57,13 +77,11 @@ public:
bool Rename(std::string_view name) override;
private:
- RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
+ RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, Mode perms = Mode::Read);
- void Close();
-
RealVfsFilesystem& base;
- std::shared_ptr<Common::FS::IOFile> backing;
+ std::unique_ptr<FileReference> reference;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index f8bafe553..6435b8af8 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -82,6 +82,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
switch (nfc_file.GetSize()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
+ case AmiiboSizeWithSignature:
data.resize(AmiiboSize);
if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) {
return Info::NotAnAmiibo;
@@ -109,6 +110,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
switch (data.size_bytes()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
+ case AmiiboSizeWithSignature:
nfc_data.resize(AmiiboSize);
break;
case MifareSize:
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 34e97cd91..09ca09e68 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -57,6 +57,7 @@ public:
private:
static constexpr std::size_t AmiiboSize = 0x21C;
static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
+ static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20;
static constexpr std::size_t MifareSize = 0x400;
std::string file_path{};
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 5734f51e5..18e040a1b 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -358,6 +358,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.support_snorm_render_buffer = true,
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
+ .support_conditional_barrier = device.SupportsConditionalBarriers(),
};
if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index a46f9beed..3d2e9a16a 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -344,6 +344,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
const bool is_s8gen2 = device_id == 0x43050a01;
+ const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
@@ -391,7 +392,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
CollectPhysicalMemoryInfo();
CollectToolingInfo();
-#ifdef ANDROID
if (is_qualcomm || is_turnip) {
LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
@@ -411,7 +411,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
extensions.push_descriptor = false;
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
-#ifdef ARCHITECTURE_arm64
+#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
// Patch the driver to enable BCn textures.
const auto major = (properties.properties.driverVersion >> 24) << 2;
const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU;
@@ -431,18 +431,23 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
} else {
LOG_WARNING(Render_Vulkan, "Adreno driver can't be patched to enable BCn textures");
}
-#endif // ARCHITECTURE_arm64
+#endif
}
- const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if (is_arm) {
must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
+
+ LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
+ features.extended_dynamic_state2.extendedDynamicState2 = false;
+ features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
+ features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
+ extensions.extended_dynamic_state2 = false;
+ loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
-#endif // ANDROID
if (is_nvidia) {
const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;