summaryrefslogtreecommitdiffstats
path: root/src/android/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt42
-rw-r--r--src/android/app/src/main/jni/game_metadata.cpp12
-rw-r--r--src/android/app/src/main/jni/native.cpp42
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_about.xml5
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml155
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml1
-rw-r--r--src/android/app/src/main/res/layout/card_driver_option.xml1
-rw-r--r--src/android/app/src/main/res/layout/card_folder.xml6
-rw-r--r--src/android/app/src/main/res/layout/fragment_about.xml5
-rw-r--r--src/android/app/src/main/res/layout/fragment_addons.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_applet_launcher.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_driver_manager.xml2
-rw-r--r--src/android/app/src/main/res/layout/fragment_early_access.xml5
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml8
-rw-r--r--src/android/app/src/main/res/layout/fragment_folders.xml3
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_info.xml3
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_properties.xml5
-rw-r--r--src/android/app/src/main/res/layout/fragment_games.xml1
-rw-r--r--src/android/app/src/main/res/layout/fragment_home_settings.xml3
-rw-r--r--src/android/app/src/main/res/layout/fragment_installables.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_licenses.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_settings.xml2
-rw-r--r--src/android/app/src/main/res/layout/list_item_addon.xml2
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml1
-rw-r--r--src/android/app/src/main/res/values/strings.xml3
38 files changed, 421 insertions, 72 deletions
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 c408485c6..55abba093 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
@@ -303,6 +303,11 @@ object NativeLibrary {
*/
external fun getCpuBackend(): String
+ /**
+ * Returns the current GPU Driver.
+ */
+ external fun getGpuDriver(): String
+
external fun applySettings()
external fun logSettings()
@@ -615,6 +620,11 @@ object NativeLibrary {
external fun clearFilesystemProvider()
/**
+ * Checks if all necessary keys are present for decryption
+ */
+ external fun areKeysPresent(): Boolean
+
+ /**
* Button type for use in onTouchEvent
*/
object ButtonType {
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 9b08f008d..26cddecf4 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
@@ -193,6 +193,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchKeyEvent(event)
}
+ if (emulationViewModel.drawerOpen.value) {
+ return super.dispatchKeyEvent(event)
+ }
+
return InputHandler.dispatchKeyEvent(event)
}
@@ -203,6 +207,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchGenericMotionEvent(event)
}
+ if (emulationViewModel.drawerOpen.value) {
+ return super.dispatchGenericMotionEvent(event)
+ }
+
// Don't attempt to do anything if we are disconnecting a device.
if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
return true
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
index f006f9e3d..0ab1b46c3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
@@ -14,15 +14,20 @@ import androidx.recyclerview.widget.RecyclerView
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
* code used in every [RecyclerView].
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
+ * @param exact Decides whether each item will be compared by reference or by their contents
*/
-abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
- ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
+abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
+ exact: Boolean = true
+) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
- private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
+ private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() {
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
- return oldItem === newItem
+ if (exact) {
+ return oldItem === newItem
+ }
+ return oldItem == newItem
}
@SuppressLint("DiffUtilEquals")
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 b4f4d950f..85c8249e6 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
@@ -30,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
- AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
+ AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return GameViewHolder(it) }
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 5b5f800c1..5ab38ffda 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
@@ -77,7 +77,7 @@ class AboutFragment : Fragment() {
}
binding.textVersionName.text = BuildConfig.VERSION_NAME
- binding.textVersionName.setOnClickListener {
+ binding.buttonVersionName.setOnClickListener {
val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
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 2a97ae14d..22da1d0e5 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
@@ -141,7 +141,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
- emulationState = EmulationState(game.path)
+ emulationState = EmulationState(game.path) {
+ return@EmulationState driverViewModel.isInteractionAllowed.value
+ }
}
/**
@@ -183,10 +185,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onDrawerOpened(drawerView: View) {
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
+ binding.inGameMenu.requestFocus()
+ emulationViewModel.setDrawerOpen(true)
}
override fun onDrawerClosed(drawerView: View) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
+ emulationViewModel.setDrawerOpen(false)
}
override fun onDrawerStateChanged(newState: Int) {
@@ -238,6 +243,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireContext().theme
)
}
+ binding.inGameMenu.requestFocus()
true
}
@@ -246,6 +252,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
null,
Settings.MenuTag.SECTION_ROOT
)
+ binding.inGameMenu.requestFocus()
binding.root.findNavController().navigate(action)
true
}
@@ -255,6 +262,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
args.game,
Settings.MenuTag.SECTION_ROOT
)
+ binding.inGameMenu.requestFocus()
binding.root.findNavController().navigate(action)
true
}
@@ -286,6 +294,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
)
}
}
+ binding.inGameMenu.requestFocus()
NativeConfig.saveGlobalConfig()
true
}
@@ -294,7 +303,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.stop()
emulationViewModel.setIsEmulationStopping(true)
binding.drawerLayout.close()
- binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ binding.inGameMenu.requestFocus()
true
}
@@ -311,12 +320,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (!NativeLibrary.isRunning()) {
return
}
-
- if (binding.drawerLayout.isOpen) {
- binding.drawerLayout.close()
- } else {
- binding.drawerLayout.open()
- }
+ emulationViewModel.setDrawerOpen(!binding.drawerLayout.isOpen)
}
}
)
@@ -371,6 +375,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ driverViewModel.isInteractionAllowed.collect {
+ if (it) {
+ startEmulation()
+ }
+ }
+ }
+ }
+ launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStarted.collectLatest {
if (it) {
@@ -399,10 +412,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- driverViewModel.isInteractionAllowed.collect {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.drawerOpen.collect {
if (it) {
- onEmulationStart()
+ binding.drawerLayout.open()
+ binding.inGameMenu.requestFocus()
+ } else {
+ binding.drawerLayout.close()
}
}
}
@@ -410,7 +426,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
- private fun onEmulationStart() {
+ private fun startEmulation() {
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
@@ -485,12 +501,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val FRAMETIME = 2
val SPEED = 3
perfStatsUpdater = {
- if (emulationViewModel.emulationStarted.value) {
+ if (emulationViewModel.emulationStarted.value &&
+ !emulationViewModel.isEmulationStopping.value
+ ) {
val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend()
+ val gpuDriver = NativeLibrary.getGpuDriver()
if (_binding != null) {
binding.showFpsText.text =
- String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
+ String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
}
@@ -807,7 +826,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
- private class EmulationState(private val gamePath: String) {
+ private class EmulationState(
+ private val gamePath: String,
+ private val emulationCanStart: () -> Boolean
+ ) {
private var state: State
private var surface: Surface? = null
@@ -901,6 +923,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
State.PAUSED -> Log.warning(
"[EmulationFragment] Surface cleared while emulation paused."
)
+
else -> Log.warning(
"[EmulationFragment] Surface cleared while emulation stopped."
)
@@ -910,6 +933,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun runWithValidSurface() {
NativeLibrary.surfaceChanged(surface)
+ if (!emulationCanStart.invoke()) {
+ return
+ }
+
when (state) {
State.STOPPED -> {
val emulationThread = Thread({
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 aefae2938..1f3578b22 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
@@ -153,7 +153,13 @@ class HomeSettingsFragment : Fragment() {
cancellable = true
) { progressCallback, _ ->
val result = NativeLibrary.verifyInstalledContents(progressCallback)
- return@newInstance if (result.isEmpty()) {
+ return@newInstance if (progressCallback.invoke(100, 100)) {
+ // Invoke the progress callback to check if the process was cancelled
+ MessageDialogFragment.newInstance(
+ titleId = R.string.verify_no_result,
+ descriptionId = R.string.verify_no_result_description
+ )
+ } else if (result.isEmpty()) {
MessageDialogFragment.newInstance(
titleId = R.string.verify_success,
descriptionId = R.string.operation_completed_successfully
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
index 620d8db7c..22b084b9a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
@@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() {
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
val helpLinkId = requireArguments().getInt(HELP_LINK)
+ val dismissible = requireArguments().getBoolean(DISMISSIBLE)
+ val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
val builder = MaterialAlertDialogBuilder(requireContext())
+ if (clearPositiveAction) {
+ messageDialogViewModel.positiveAction = null
+ }
+
if (messageDialogViewModel.positiveAction == null) {
builder.setPositiveButton(R.string.close, null)
} else {
@@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() {
}
}
+ isCancelable = dismissible
+
return builder.show()
}
@@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() {
private const val DESCRIPTION_ID = "DescriptionId"
private const val DESCRIPTION_STRING = "DescriptionString"
private const val HELP_LINK = "Link"
+ private const val DISMISSIBLE = "Dismissible"
+ private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
fun newInstance(
activity: FragmentActivity? = null,
@@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() {
descriptionId: Int = 0,
descriptionString: String = "",
helpLinkId: Int = 0,
+ dismissible: Boolean = true,
positiveAction: (() -> Unit)? = null
): MessageDialogFragment {
+ var clearPositiveAction = false
+ if (activity != null) {
+ ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
+ clear()
+ this.positiveAction = positiveAction
+ }
+ } else {
+ clearPositiveAction = true
+ }
+
val dialog = MessageDialogFragment()
- val bundle = Bundle()
- bundle.apply {
+ val bundle = Bundle().apply {
putInt(TITLE_ID, titleId)
putString(TITLE_STRING, titleString)
putInt(DESCRIPTION_ID, descriptionId)
putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId)
- }
- if (activity != null) {
- ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
- clear()
- this.positiveAction = positiveAction
- }
+ putBoolean(DISMISSIBLE, dismissible)
+ putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
}
dialog.arguments = bundle
return dialog
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 064342cdd..ebf41a639 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
@@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
import kotlinx.coroutines.launch
+import org.yuzu.yuzu_emu.NativeLibrary
import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
R.string.install_prod_keys_warning_help,
{
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
- if (file.exists()) {
+ if (file.exists() && NativeLibrary.areKeysPresent()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
@@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
- if (mainActivity.processKey(result)) {
+ mainActivity.processKey(result)
+ if (NativeLibrary.areKeysPresent()) {
keyCallback.onStepCompleted()
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
index 15ae3a42b..5ed754c96 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
@@ -144,6 +144,7 @@ class DriverViewModel : ViewModel() {
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
+ setDriverReady()
return
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
index f34870c2d..b66f47fe7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
class EmulationViewModel : ViewModel() {
val emulationStarted: StateFlow<Boolean> get() = _emulationStarted
@@ -23,6 +24,9 @@ class EmulationViewModel : ViewModel() {
val shaderMessage: StateFlow<String> get() = _shaderMessage
private val _shaderMessage = MutableStateFlow("")
+ private val _drawerOpen = MutableStateFlow(false)
+ val drawerOpen = _drawerOpen.asStateFlow()
+
fun setEmulationStarted(started: Boolean) {
_emulationStarted.value = started
}
@@ -49,6 +53,10 @@ class EmulationViewModel : ViewModel() {
setTotalShaders(max)
}
+ fun setDrawerOpen(value: Boolean) {
+ _drawerOpen.value = value
+ }
+
fun clear() {
setEmulationStarted(false)
setIsEmulationStopping(false)
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 c8a4a2d17..6859b7780 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
@@ -70,11 +70,19 @@ class Game(
}
override fun equals(other: Any?): Boolean {
- if (other !is Game) {
- return false
- }
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Game
+
+ if (title != other.title) return false
+ if (path != other.path) return false
+ if (programId != other.programId) return false
+ if (developer != other.developer) return false
+ if (version != other.version) return false
+ if (isHomebrew != other.isHomebrew) return false
- return hashCode() == other.hashCode()
+ return true
}
override fun hashCode(): Int {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 513ac2fc5..cfc777b81 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
private val _reloadPropertiesList = MutableStateFlow(false)
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
+ private val _checkKeys = MutableStateFlow(false)
+ val checkKeys = _checkKeys.asStateFlow()
+
var navigatedToSetup = false
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
fun reloadPropertiesList(reload: Boolean) {
_reloadPropertiesList.value = reload
}
+
+ fun setCheckKeys(value: Boolean) {
+ _checkKeys.value = value
+ }
}
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 c2cc29961..b3967d294 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
@@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override var themeId: Int = 0
+ private val CHECKED_DECRYPTION = "CheckedDecryption"
+ private var checkedDecryption = false
+
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
+ if (savedInstanceState != null) {
+ checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
+ }
+ if (!checkedDecryption) {
+ val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+ .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
+ if (!firstTimeSetup) {
+ checkKeys()
+ }
+ checkedDecryption = true
+ }
+
WindowCompat.setDecorFitsSystemWindows(window, false)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
@@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
}
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ homeViewModel.checkKeys.collect {
+ if (it) {
+ checkKeys()
+ homeViewModel.setCheckKeys(false)
+ }
+ }
+ }
+ }
}
// Dismiss previous notifications (should not happen unless a crash occurred)
@@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
setInsets()
}
+ private fun checkKeys() {
+ if (!NativeLibrary.areKeysPresent()) {
+ MessageDialogFragment.newInstance(
+ titleId = R.string.keys_missing,
+ descriptionId = R.string.keys_missing_description,
+ helpLinkId = R.string.keys_missing_help
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
+ }
+
fun finishSetup(navController: NavController) {
navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
@@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
+ homeViewModel.setCheckKeys(true)
gamesViewModel.reloadGames(true)
return true
} else {
@@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true)
+ homeViewModel.setCheckKeys(true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp
index 78f604c70..8f0da1413 100644
--- a/src/android/app/src/main/jni/game_metadata.cpp
+++ b/src/android/app/src/main/jni/game_metadata.cpp
@@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <core/core.h>
-#include <core/file_sys/mode.h>
-#include <core/file_sys/patch_manager.h>
-#include <core/loader/nro.h>
-#include <jni.h>
+#include "core/core.h"
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
+#include "core/loader/nro.h"
+#include "jni.h"
#include "jni/android_common/android_common.h"
#include "native.h"
@@ -79,7 +79,7 @@ extern "C" {
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
jstring jpath) {
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
- GetJString(env, jpath), FileSys::Mode::Read);
+ GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) {
return false;
}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 963f57380..3fd9a500c 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -35,9 +35,10 @@
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/submission_package.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
@@ -154,7 +155,7 @@ void EmulationSession::SurfaceChanged() {
}
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
- const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
+ const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
@@ -247,6 +248,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
+ OnEmulationStarted();
return Core::SystemResultStatus::Success;
}
@@ -463,8 +465,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
};
return static_cast<int>(
- ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
- EmulationSession::GetInstance().System().GetFilesystem().get(),
+ ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
+ *EmulationSession::GetInstance().System().GetFilesystem(),
GetJString(env, j_file), callback));
}
@@ -474,8 +476,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
- EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
- FileSys::Mode::Read));
+ EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
+ updatePath, FileSys::OpenMode::Read));
for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
@@ -674,6 +676,11 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
return ToJString(env, "JIT");
}
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
+ return ToJString(env,
+ EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
+}
+
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
EmulationSession::GetInstance().System().ApplySettings();
}
@@ -713,7 +720,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
jobject instance) {
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
- Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
+ Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
static_cast<std::size_t>(0));
@@ -819,7 +826,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
- ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
+ ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
@@ -829,8 +836,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
program_id, GetJString(env, jname));
}
-jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
- jobject jcallback) {
+jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
+ jobject jobj,
+ jobject jcallback) {
auto jlambdaClass = env->GetObjectClass(jcallback);
auto jlambdaInvokeMethod = env->GetMethodID(
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@@ -842,7 +850,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en
auto& session = EmulationSession::GetInstance();
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
- &session.System(), session.GetContentProvider(), callback);
+ session.System(), *session.GetContentProvider(), callback);
jobjectArray jresult =
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
for (size_t i = 0; i < result.size(); ++i) {
@@ -863,7 +871,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
};
auto& session = EmulationSession::GetInstance();
return static_cast<jint>(
- ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
+ ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
@@ -882,7 +890,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
- FileSys::Mode::Read);
+ FileSys::OpenMode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
@@ -912,4 +920,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
+ auto& system = EmulationSession::GetInstance().System();
+ system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
+ return ContentManager::AreKeysPresent();
+}
+
} // extern "C"
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
index 655e49219..a5eba6474 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
@@ -11,12 +11,14 @@
android:id="@+id/appbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back"
app:title="@string/about" />
@@ -28,6 +30,7 @@
android:layout_height="match_parent"
android:fadeScrollbars="false"
android:scrollbars="vertical"
+ android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml
new file mode 100644
index 000000000..90d95dbb7
--- /dev/null
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/coordinator_about"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?attr/colorSurface">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:id="@+id/appbar_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
+
+ <com.google.android.material.appbar.MaterialToolbar
+ android:id="@+id/toolbar_info"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
+ app:navigationIcon="@drawable/ic_back" />
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scroll_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:defaultFocusHighlightEnabled="false"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+ <LinearLayout
+ android:id="@+id/content_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingHorizontal="16dp"
+ android:baselineAligned="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="3"
+ android:gravity="top|center_horizontal"
+ android:paddingHorizontal="16dp">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/button_copy"
+ style="@style/Widget.Material3.Button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/copy_details" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/button_verify_integrity"
+ style="@style/Widget.Material3.Button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="@string/verify_integrity" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/path"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/path_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:editable="false"
+ android:importantForAutofill="no"
+ android:inputType="none"
+ android:minHeight="48dp"
+ android:textAlignment="viewStart"
+ tools:text="1.0.0" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/program_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/program_id_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:editable="false"
+ android:importantForAutofill="no"
+ android:inputType="none"
+ android:minHeight="48dp"
+ android:textAlignment="viewStart"
+ tools:text="1.0.0" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/developer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/developer_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:editable="false"
+ android:importantForAutofill="no"
+ android:inputType="none"
+ android:minHeight="48dp"
+ android:textAlignment="viewStart"
+ tools:text="1.0.0" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/version"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/version_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:editable="false"
+ android:importantForAutofill="no"
+ android:inputType="none"
+ android:minHeight="48dp"
+ android:textAlignment="viewStart"
+ tools:text="1.0.0" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </androidx.core.widget.NestedScrollView>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml
index 551f255c0..7cdef569f 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml
@@ -14,6 +14,7 @@
android:clipToPadding="false"
android:fadeScrollbars="false"
android:scrollbars="vertical"
+ android:defaultFocusHighlightEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/icon_layout"
app:layout_constraintTop_toTopOf="parent">
diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml
index 1dd9a6d7d..bda524f0f 100644
--- a/src/android/app/src/main/res/layout/card_driver_option.xml
+++ b/src/android/app/src/main/res/layout/card_driver_option.xml
@@ -23,6 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:focusable="false"
android:clickable="false"
android:checked="false" />
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml
index 4e0c04b6b..ed4a7ca8f 100644
--- a/src/android/app/src/main/res/layout/card_folder.xml
+++ b/src/android/app/src/main/res/layout/card_folder.xml
@@ -6,16 +6,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
- android:layout_marginVertical="12dp"
- android:focusable="true">
+ android:layout_marginVertical="12dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
- android:layout_gravity="center_vertical"
- android:animateLayoutChanges="true">
+ android:layout_gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/path"
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml
index 38090fa50..7f32e139a 100644
--- a/src/android/app/src/main/res/layout/fragment_about.xml
+++ b/src/android/app/src/main/res/layout/fragment_about.xml
@@ -11,12 +11,14 @@
android:id="@+id/appbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:title="@string/about"
app:navigationIcon="@drawable/ic_back" />
@@ -28,6 +30,7 @@
android:layout_height="match_parent"
android:scrollbars="vertical"
android:fadeScrollbars="false"
+ android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
diff --git a/src/android/app/src/main/res/layout/fragment_addons.xml b/src/android/app/src/main/res/layout/fragment_addons.xml
index a25e82766..b029b4209 100644
--- a/src/android/app/src/main/res/layout/fragment_addons.xml
+++ b/src/android/app/src/main/res/layout/fragment_addons.xml
@@ -11,6 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@@ -19,6 +20,7 @@
android:id="@+id/toolbar_addons"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout>
@@ -28,6 +30,8 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
+ android:defaultFocusHighlightEnabled="false"
+ android:nextFocusDown="@id/button_install"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml
index fe8fae40f..95e6d6a6b 100644
--- a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml
+++ b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml
@@ -10,12 +10,14 @@
android:id="@+id/appbar_applets"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_applets"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back"
app:title="@string/applets" />
diff --git a/src/android/app/src/main/res/layout/fragment_driver_manager.xml b/src/android/app/src/main/res/layout/fragment_driver_manager.xml
index 6cea2d164..56d8e6bb8 100644
--- a/src/android/app/src/main/res/layout/fragment_driver_manager.xml
+++ b/src/android/app/src/main/res/layout/fragment_driver_manager.xml
@@ -15,12 +15,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false"
app:liftOnScrollTargetViewId="@id/list_drivers">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_drivers"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back"
app:title="@string/gpu_driver_manager" />
diff --git a/src/android/app/src/main/res/layout/fragment_early_access.xml b/src/android/app/src/main/res/layout/fragment_early_access.xml
index 644b4dd45..12e233afc 100644
--- a/src/android/app/src/main/res/layout/fragment_early_access.xml
+++ b/src/android/app/src/main/res/layout/fragment_early_access.xml
@@ -11,12 +11,14 @@
android:id="@+id/appbar_ea"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back"
app:title="@string/early_access" />
@@ -30,6 +32,7 @@
android:paddingBottom="20dp"
android:scrollbars="vertical"
android:fadeScrollbars="false"
+ android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
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 5252adf54..c01117d14 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
+ android:defaultFocusHighlightEnabled="false"
tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"
tools:openDrawer="start">
@@ -24,7 +25,8 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
- android:focusableInTouchMode="false" />
+ android:focusableInTouchMode="false"
+ android:defaultFocusHighlightEnabled="false" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/loading_indicator"
@@ -32,7 +34,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:focusable="false"
+ android:defaultFocusHighlightEnabled="false"
android:clickable="false">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -118,6 +120,7 @@
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
+ android:defaultFocusHighlightEnabled="false"
android:visibility="invisible" />
<Button
@@ -160,6 +163,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
+ android:focusedByDefault="true"
app:headerLayout="@layout/header_in_game"
app:menu="@menu/menu_in_game"
tools:visibility="gone" />
diff --git a/src/android/app/src/main/res/layout/fragment_folders.xml b/src/android/app/src/main/res/layout/fragment_folders.xml
index 74f2f3754..b5c7676d8 100644
--- a/src/android/app/src/main/res/layout/fragment_folders.xml
+++ b/src/android/app/src/main/res/layout/fragment_folders.xml
@@ -15,12 +15,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false"
app:liftOnScrollTargetViewId="@id/list_folders">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_folders"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back"
app:title="@string/game_folders" />
@@ -31,6 +33,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
+ android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_game_info.xml b/src/android/app/src/main/res/layout/fragment_game_info.xml
index 53af15787..f29e7e376 100644
--- a/src/android/app/src/main/res/layout/fragment_game_info.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_info.xml
@@ -11,12 +11,14 @@
android:id="@+id/appbar_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:touchscreenBlocksFocus="false"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_info"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout>
@@ -25,6 +27,7 @@
android:id="@+id/scroll_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml
index cadd0bc4a..436ebd79d 100644
--- a/src/android/app/src/main/res/layout/fragment_game_properties.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml
@@ -12,7 +12,8 @@
android:layout_height="match_parent"
android:scrollbars="vertical"
android:fadeScrollbars="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:defaultFocusHighlightEnabled="false">
<LinearLayout
android:id="@+id/layout_all"
@@ -86,7 +87,7 @@
android:id="@+id/list_properties"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:listitem="@layout/card_simple_outlined" />
+ android:defaultFocusHighlightEnabled="false" />
</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_games.xml b/src/android/app/src/main/res/layout/fragment_games.xml
index a0568668a..cc280b1ff 100644
--- a/src/android/app/src/main/res/layout/fragment_games.xml
+++ b/src/android/app/src/main/res/layout/fragment_games.xml
@@ -27,6 +27,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
+ android:defaultFocusHighlightEnabled="false"
tools:listitem="@layout/card_game" />
</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml
index d84093ba3..c179f9341 100644
--- a/src/android/app/src/main/res/layout/fragment_home_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml
@@ -7,7 +7,8 @@
android:background="?attr/colorSurface"
android:scrollbars="vertical"
android:fadeScrollbars="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:defaultFocusHighlightEnabled="false">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/linear_layout_settings"
diff --git a/src/android/app/src/main/res/layout/fragment_installables.xml b/src/android/app/src/main/res/layout/fragment_installables.xml
index 3a4df81a6..47ef3869f 100644
--- a/src/android/app/src/main/res/layout/fragment_installables.xml
+++ b/src/android/app/src/main/res/layout/fragment_installables.xml
@@ -10,12 +10,14 @@
android:id="@+id/appbar_installables"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_installables"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:title="@string/manage_yuzu_data"
app:navigationIcon="@drawable/ic_back" />
diff --git a/src/android/app/src/main/res/layout/fragment_licenses.xml b/src/android/app/src/main/res/layout/fragment_licenses.xml
index 6b31ff5b4..59d68b112 100644
--- a/src/android/app/src/main/res/layout/fragment_licenses.xml
+++ b/src/android/app/src/main/res/layout/fragment_licenses.xml
@@ -10,12 +10,14 @@
android:id="@+id/appbar_licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_licenses"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:title="@string/licenses"
app:navigationIcon="@drawable/ic_back" />
diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml
index ebedbf1ec..110c70eef 100644
--- a/src/android/app/src/main/res/layout/fragment_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_settings.xml
@@ -11,6 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
+ android:touchscreenBlocksFocus="false"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
@@ -24,6 +25,7 @@
android:id="@+id/toolbar_settings"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:touchscreenBlocksFocus="false"
app:layout_collapseMode="pin"
app:navigationIcon="@drawable/ic_back" />
diff --git a/src/android/app/src/main/res/layout/list_item_addon.xml b/src/android/app/src/main/res/layout/list_item_addon.xml
index 3a1382fe2..9b1c0e6fc 100644
--- a/src/android/app/src/main/res/layout/list_item_addon.xml
+++ b/src/android/app/src/main/res/layout/list_item_addon.xml
@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
- android:focusable="true"
+ android:focusable="false"
android:paddingHorizontal="20dp"
android:paddingVertical="16dp">
diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml
index 21276b19e..615860368 100644
--- a/src/android/app/src/main/res/layout/list_item_settings_header.xml
+++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml
@@ -12,4 +12,5 @@
android:textAlignment="viewStart"
android:textColor="?attr/colorPrimary"
android:textStyle="bold"
+ android:focusable="false"
tools:text="CPU Settings" />
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 779eb36a8..3cd1586fd 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -144,6 +144,9 @@
<string name="no_save_data_found">No save data found</string>
<string name="verify_installed_content">Verify installed content</string>
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
+ <string name="keys_missing">Encryption keys are missing</string>
+ <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
+ <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<!-- Applet launcher strings -->
<string name="applets">Applet launcher</string>