From 3abc4ee7dbd75f463337afa40c709aece1d3f9aa Mon Sep 17 00:00:00 2001 From: Producdevity Date: Sun, 13 Jul 2025 07:31:22 +0200 Subject: [PATCH] refactor: extract and streamline EmuReady intent handling * Moved custom settings logic from `onCreate` to `handleEmuReadyIntent` for better readability. * Added `showLaunchConfirmationDialog` to confirm game launch with or without custom settings. * Updated `CustomSettingsHandler.findGameByTitleId` to show user feedback via `Toast`. * Ensured improved separation of concerns and reusable methods. --- .../yuzu_emu/fragments/EmulationFragment.kt | 167 ++++++++++++------ .../yuzu_emu/utils/CustomSettingsHandler.kt | 5 +- 2 files changed, 117 insertions(+), 55 deletions(-) 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 36870e9768..a4c86b94c7 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 @@ -84,6 +84,8 @@ import org.yuzu.yuzu_emu.utils.CustomSettingsHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import java.io.File class EmulationFragment : Fragment(), SurfaceHolder.Callback { @@ -138,60 +140,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { isCustomSettingsIntent = false if (intent.action == CustomSettingsHandler.CUSTOM_CONFIG_ACTION) { - val titleId = intent.getStringExtra(CustomSettingsHandler.EXTRA_TITLE_ID) - val customSettings = intent.getStringExtra(CustomSettingsHandler.EXTRA_CUSTOM_SETTINGS) - - if (titleId != null && customSettings != null) { - Log.info("[EmulationFragment] Received custom settings intent for title: $titleId") - - // Handle custom settings asynchronously to allow for driver checking/installation - CoroutineScope(Dispatchers.Main).launch { - try { - intentGame = CustomSettingsHandler.applyCustomSettingsWithDriverCheck( - titleId, - customSettings, - requireContext(), - requireActivity(), - driverViewModel - ) - - if (intentGame == null) { - Log.error("[EmulationFragment] Custom settings processing failed for title ID: $titleId") - Toast.makeText( - requireContext(), - "Failed to apply custom settings. This could be due to:\n• Game not found in library\n• User cancelled configuration overwrite\n• Driver installation failed\n• Missing required drivers", - Toast.LENGTH_LONG - ).show() - requireActivity().finish() - return@launch - } - - isCustomSettingsIntent = true - - finishGameSetup() - - } catch (e: Exception) { - Log.error("[EmulationFragment] Error processing custom settings: ${e.message}") - Toast.makeText( - requireContext(), - "Error processing custom settings: ${e.message}", - Toast.LENGTH_LONG - ).show() - requireActivity().finish() - } - } - - return - } else { - Log.error("[EmulationFragment] Custom settings intent missing required extras") - Toast.makeText( - requireContext(), - "Invalid custom settings data", - Toast.LENGTH_SHORT - ).show() - requireActivity().finish() - return - } + handleEmuReadyIntent(intent) + return } else if (intentUri != null) { // Handle regular file intent intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) { @@ -246,6 +196,115 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } + /** + * Handle EmuReady intent for launching games with or without custom settings + */ + private fun handleEmuReadyIntent(intent: Intent) { + val titleId = intent.getStringExtra(CustomSettingsHandler.EXTRA_TITLE_ID) + val customSettings = intent.getStringExtra(CustomSettingsHandler.EXTRA_CUSTOM_SETTINGS) + + if (titleId != null) { + Log.info("[EmulationFragment] Received EmuReady intent for title: $titleId") + + CoroutineScope(Dispatchers.Main).launch { + try { + // Find the game first to get the title for confirmation + val foundGame = CustomSettingsHandler.findGameByTitleId(titleId, requireContext()) + if (foundGame == null) { + Log.error("[EmulationFragment] Game not found for title ID: $titleId") + Toast.makeText( + requireContext(), + "Game not found in library for title ID: $titleId", + Toast.LENGTH_LONG + ).show() + requireActivity().finish() + return@launch + } + + // Show confirmation dialog + val shouldLaunch = showLaunchConfirmationDialog(foundGame.title, customSettings != null) + if (!shouldLaunch) { + Log.info("[EmulationFragment] User cancelled EmuReady launch") + requireActivity().finish() + return@launch + } + + if (customSettings != null) { + // Handle custom settings launch + intentGame = CustomSettingsHandler.applyCustomSettingsWithDriverCheck( + titleId, + customSettings, + requireContext(), + requireActivity(), + driverViewModel + ) + + if (intentGame == null) { + Log.error("[EmulationFragment] Custom settings processing failed for title ID: $titleId") + Toast.makeText( + requireContext(), + "Failed to apply custom settings. This could be due to:\n• User cancelled configuration overwrite\n• Driver installation failed\n• Missing required drivers", + Toast.LENGTH_LONG + ).show() + requireActivity().finish() + return@launch + } + + isCustomSettingsIntent = true + } else { + // Handle title-only launch (no custom settings) + intentGame = foundGame + } + + finishGameSetup() + + } catch (e: Exception) { + Log.error("[EmulationFragment] Error processing EmuReady intent: ${e.message}") + Toast.makeText( + requireContext(), + "Error processing EmuReady request: ${e.message}", + Toast.LENGTH_LONG + ).show() + requireActivity().finish() + } + } + } else { + Log.error("[EmulationFragment] EmuReady intent missing title_id") + Toast.makeText( + requireContext(), + "Invalid EmuReady request: missing title ID", + Toast.LENGTH_SHORT + ).show() + requireActivity().finish() + } + } + + /** + * Show confirmation dialog for EmuReady game launches + */ + private suspend fun showLaunchConfirmationDialog(gameTitle: String, hasCustomSettings: Boolean): Boolean { + return suspendCoroutine { continuation -> + requireActivity().runOnUiThread { + val message = if (hasCustomSettings) { + "EmuReady wants to launch \"$gameTitle\" with custom settings.\n\nDo you want to continue?" + } else { + "EmuReady wants to launch \"$gameTitle\".\n\nDo you want to continue?" + } + + MaterialAlertDialogBuilder(requireContext()) + .setTitle("Launch Game") + .setMessage(message) + .setPositiveButton("Launch") { _, _ -> + continuation.resume(true) + } + .setNegativeButton("Cancel") { _, _ -> + continuation.resume(false) + } + .setCancelable(false) + .show() + } + } + } /** * Initialize the UI and start emulation in here. diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt index 030a328f20..cb0b505ff9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt @@ -7,6 +7,7 @@ package org.yuzu.yuzu_emu.utils import android.content.Context +import android.widget.Toast import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.model.DriverViewModel @@ -131,7 +132,7 @@ object CustomSettingsHandler { /** * Find a game by its title ID in the user's game library */ - private fun findGameByTitleId(titleId: String, context: Context): Game? { + fun findGameByTitleId(titleId: String, context: Context): Game? { Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId") // Convert hex title ID to decimal for comparison with programId val programIdDecimal = try { @@ -160,8 +161,10 @@ object CustomSettingsHandler { } if (foundGame != null) { Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}") + Toast.makeText(context, "Found game: ${foundGame.title}", Toast.LENGTH_SHORT).show() } else { Log.warning("[CustomSettingsHandler] No game found for title ID: $titleId") + Toast.makeText(context, "Game not found for title ID: $titleId", Toast.LENGTH_SHORT).show() } return foundGame }