refactor: wip, trying to fix intent launching

This commit is contained in:
Producdevity 2025-07-27 14:05:20 +02:00
parent e99b129cc2
commit 2c4d5f7a81

View file

@ -45,6 +45,7 @@ import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature import androidx.window.layout.FoldingFeature
@ -81,9 +82,10 @@ import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import java.io.File import java.io.File
@ -101,12 +103,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private val args by navArgs<EmulationFragmentArgs>() private val args by navArgs<EmulationFragmentArgs>()
private lateinit var game: Game private var game: Game? = null
private val emulationViewModel: EmulationViewModel by activityViewModels() private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels() private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false private var isInFoldableLayout = false
private var emulationStarted = false
private lateinit var gpuModel: String private lateinit var gpuModel: String
private lateinit var fwVersion: String private lateinit var fwVersion: String
@ -143,7 +146,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
handleEmuReadyIntent(intent) handleEmuReadyIntent(intent)
return return
} else if (intentUri != null) { } else if (intentUri != null) {
// Handle regular file intent
intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) { intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
GameHelper.getGame(requireActivity().intent.data!!, false) GameHelper.getGame(requireActivity().intent.data!!, false)
} else { } else {
@ -151,25 +153,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
// For non-EmuReady intents, finish game setup immediately
// EmuReady intents handle setup asynchronously in handleEmuReadyIntent()
if (!isCustomSettingsIntent) {
finishGameSetup() finishGameSetup()
} }
}
/** /**
* Complete the game setup process (extracted for async custom settings handling) * Complete the game setup process (extracted for async custom settings handling)
*/ */
private fun finishGameSetup() { private fun finishGameSetup() {
try { try {
game = if (args.game != null) { val gameToUse = args.game ?: intentGame
args.game!!
} else { if (gameToUse == null) {
intentGame!! Log.error("[EmulationFragment] No game found in arguments or intent")
}
} catch (e: NullPointerException) {
Log.error("[EmulationFragment] No game found in arguments or intent: ${e.message}")
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
R.string.no_game_present, R.string.no_game_present,
@ -177,6 +172,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
).show() ).show()
requireActivity().finish() requireActivity().finish()
return return
}
game = gameToUse
} catch (e: Exception) { } catch (e: Exception) {
Log.error("[EmulationFragment] Error during game setup: ${e.message}") Log.error("[EmulationFragment] Error during game setup: ${e.message}")
Toast.makeText( Toast.makeText(
@ -188,50 +187,58 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return return
} }
// Handle configuration loading
try { try {
if (isCustomSettingsIntent) { if (isCustomSettingsIntent) {
// Custom settings already applied by CustomSettingsHandler
Log.info("[EmulationFragment] Using custom settings from intent") Log.info("[EmulationFragment] Using custom settings from intent")
} else if (args.custom) { } else if (intentGame != null && game != null) {
// Load custom settings when explicitly requested via args val customConfigFile = SettingsFile.getCustomSettingsFile(game!!)
SettingsFile.loadCustomConfig(game)
NativeConfig.unloadPerGameConfig()
Log.info("[EmulationFragment] Loading custom settings for ${game.title}")
} else if (intentGame != null) {
// For intent games, check if custom settings exist and load them, otherwise use global
val customConfigFile = SettingsFile.getCustomSettingsFile(game)
if (customConfigFile.exists()) { if (customConfigFile.exists()) {
Log.info("[EmulationFragment] Found existing custom settings for ${game.title}, loading them") Log.info(
SettingsFile.loadCustomConfig(game) "[EmulationFragment] Found existing custom settings for ${game!!.title}, loading them"
NativeConfig.unloadPerGameConfig() )
SettingsFile.loadCustomConfig(game!!)
} else { } else {
Log.info("[EmulationFragment] No custom settings found for ${game.title}, using global settings") Log.info(
"[EmulationFragment] No custom settings found for ${game!!.title}, using global settings"
)
NativeConfig.reloadGlobalConfig() NativeConfig.reloadGlobalConfig()
} }
} else { } else {
// Default case - use global settings val isCustomFromArgs = if (game != null && game == args.game) {
try {
args.custom
} catch (e: Exception) {
false
}
} else {
false
}
if (isCustomFromArgs && game != null) {
SettingsFile.loadCustomConfig(game!!)
Log.info("[EmulationFragment] Loading custom settings for ${game!!.title}")
} else {
Log.info("[EmulationFragment] Using global settings") Log.info("[EmulationFragment] Using global settings")
NativeConfig.reloadGlobalConfig() NativeConfig.reloadGlobalConfig()
} }
}
} catch (e: Exception) { } catch (e: Exception) {
Log.error("[EmulationFragment] Error loading configuration: ${e.message}") Log.error("[EmulationFragment] Error loading configuration: ${e.message}")
Log.info("[EmulationFragment] Falling back to global settings") Log.info("[EmulationFragment] Falling back to global settings")
try { try {
NativeConfig.reloadGlobalConfig() NativeConfig.reloadGlobalConfig()
} catch (fallbackException: Exception) { } catch (fallbackException: Exception) {
Log.error("[EmulationFragment] Critical error: could not load global config: ${fallbackException.message}") Log.error(
"[EmulationFragment] Critical error: could not load global config: ${fallbackException.message}"
)
throw fallbackException throw fallbackException
} }
} }
// Install the selected driver asynchronously as the game starts emulationState = EmulationState(game!!.path) {
driverViewModel.onLaunchGame()
// Initialize emulation state (ViewModels handle state retention now)
emulationState = EmulationState(game.path) {
return@EmulationState driverViewModel.isInteractionAllowed.value return@EmulationState driverViewModel.isInteractionAllowed.value
} }
} }
/** /**
@ -244,24 +251,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (titleId != null) { if (titleId != null) {
Log.info("[EmulationFragment] Received EmuReady intent for title: $titleId") Log.info("[EmulationFragment] Received EmuReady intent for title: $titleId")
CoroutineScope(Dispatchers.Main).launch { lifecycleScope.launch {
try { try {
// Find the game first to get the title for confirmation Toast.makeText(
Toast.makeText(requireContext(), "Searching for game...", Toast.LENGTH_SHORT).show() requireContext(),
val foundGame = CustomSettingsHandler.findGameByTitleId(titleId, requireContext()) getString(R.string.searching_for_game),
Toast.LENGTH_SHORT
).show()
val foundGame = CustomSettingsHandler.findGameByTitleId(
titleId,
requireContext()
)
if (foundGame == null) { if (foundGame == null) {
Log.error("[EmulationFragment] Game not found for title ID: $titleId") Log.error("[EmulationFragment] Game not found for title ID: $titleId")
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Game not found: $titleId", getString(R.string.game_not_found_for_title_id, titleId),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
requireActivity().finish() requireActivity().finish()
return@launch return@launch
} }
// Show confirmation dialog val shouldLaunch = showLaunchConfirmationDialog(
val shouldLaunch = showLaunchConfirmationDialog(foundGame.title, customSettings != null) foundGame.title,
customSettings != null
)
if (!shouldLaunch) { if (!shouldLaunch) {
Log.info("[EmulationFragment] User cancelled EmuReady launch") Log.info("[EmulationFragment] User cancelled EmuReady launch")
requireActivity().finish() requireActivity().finish()
@ -269,7 +284,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
if (customSettings != null) { if (customSettings != null) {
// Handle custom settings launch
intentGame = CustomSettingsHandler.applyCustomSettingsWithDriverCheck( intentGame = CustomSettingsHandler.applyCustomSettingsWithDriverCheck(
titleId, titleId,
customSettings, customSettings,
@ -279,11 +293,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
if (intentGame == null) { if (intentGame == null) {
Log.error("[EmulationFragment] Custom settings processing failed for title ID: $titleId") Log.error(
// Ask user if they want to launch with default settings "[EmulationFragment] Custom settings processing failed for title ID: $titleId"
)
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Custom settings failed", getString(R.string.custom_settings_failed_title),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
@ -293,57 +308,81 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
if (launchWithDefault) { if (launchWithDefault) {
Log.info("[EmulationFragment] User chose to launch with default settings") Log.info(
"[EmulationFragment] User chose to launch with default settings"
)
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Launching with default settings", getString(R.string.launch_with_default_settings),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
intentGame = foundGame intentGame = foundGame
isCustomSettingsIntent = false isCustomSettingsIntent = false
} else { } else {
Log.info("[EmulationFragment] User cancelled launch after custom settings failure") Log.info(
"[EmulationFragment] User cancelled launch after custom settings failure"
)
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Launch cancelled", getString(R.string.launch_cancelled),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
requireActivity().finish() requireActivity().finish()
return@launch return@launch
} }
} else { } else {
Toast.makeText(
requireContext(),
"Custom settings applied",
Toast.LENGTH_SHORT
).show()
isCustomSettingsIntent = true isCustomSettingsIntent = true
} }
} else { } else {
// Handle title-only launch (no custom settings)
Log.info("[EmulationFragment] Launching game with default settings") Log.info("[EmulationFragment] Launching game with default settings")
val customConfigFile = SettingsFile.getCustomSettingsFile(foundGame)
if (customConfigFile.exists()) {
Log.info("[EmulationFragment] Found existing custom settings for ${foundGame.title}, loading them")
SettingsFile.loadCustomConfig(foundGame)
} else {
Log.info("[EmulationFragment] No custom settings found for ${foundGame.title}, using global settings")
}
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Launching ${foundGame.title}", getString(R.string.launching_game, foundGame.title),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
intentGame = foundGame intentGame = foundGame
isCustomSettingsIntent = false isCustomSettingsIntent = false
} }
// Ensure we have a valid game before finishing setup
if (intentGame != null) { if (intentGame != null) {
withContext(Dispatchers.Main) {
try {
finishGameSetup() finishGameSetup()
Log.info("[EmulationFragment] Game setup complete for intent launch")
if (_binding != null) {
completeViewSetup()
val driverReady = driverViewModel.isInteractionAllowed.value
if (driverReady && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
Log.info("[EmulationFragment] Starting emulation after async intent setup - driver ready")
startEmulation()
}
}
} catch (e: Exception) {
Log.error("[EmulationFragment] Error in finishGameSetup: ${e.message}")
requireActivity().finish()
return@withContext
}
}
} else { } else {
Log.error("[EmulationFragment] No valid game found after processing intent") Log.error("[EmulationFragment] No valid game found after processing intent")
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
"Failed to initialize game", getString(R.string.failed_to_initialize_game),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
requireActivity().finish() requireActivity().finish()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.error("[EmulationFragment] Error processing EmuReady intent: ${e.message}") Log.error("[EmulationFragment] Error processing EmuReady intent: ${e.message}")
Toast.makeText( Toast.makeText(
@ -372,18 +411,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return suspendCoroutine { continuation -> return suspendCoroutine { continuation ->
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
val message = if (hasCustomSettings) { val message = if (hasCustomSettings) {
"EmuReady wants to launch \"$gameTitle\" with custom settings.\n\nDo you want to continue?" getString(
R.string.custom_intent_launch_message_with_settings,
gameTitle
)
} else { } else {
"EmuReady wants to launch \"$gameTitle\".\n\nDo you want to continue?" getString(R.string.custom_intent_launch_message, gameTitle)
} }
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle("Launch Game") .setTitle(getString(R.string.custom_intent_launch_title))
.setMessage(message) .setMessage(message)
.setPositiveButton("Launch") { _, _ -> .setPositiveButton(getString(R.string.launch)) { _, _ ->
continuation.resume(true) continuation.resume(true)
} }
.setNegativeButton("Cancel") { _, _ -> .setNegativeButton(getString(R.string.cancel)) { _, _ ->
continuation.resume(false) continuation.resume(false)
} }
.setCancelable(false) .setCancelable(false)
@ -399,16 +441,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return suspendCoroutine { continuation -> return suspendCoroutine { continuation ->
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle("Custom Settings Failed") .setTitle(getString(R.string.custom_settings_failed_title))
.setMessage( .setMessage(
"Failed to apply custom settings for \"$gameTitle\":\n\n" + getString(R.string.custom_settings_failed_message, gameTitle, errorMessage)
"$errorMessage\n\n" +
"Would you like to launch the game with default settings instead?"
) )
.setPositiveButton("Launch with Default Settings") { _, _ -> .setPositiveButton(getString(R.string.launch_with_default_settings)) { _, _ ->
continuation.resume(true) continuation.resume(true)
} }
.setNegativeButton("Cancel") { _, _ -> .setNegativeButton(getString(R.string.cancel)) { _, _ ->
continuation.resume(false) continuation.resume(false)
} }
.setCancelable(false) .setCancelable(false)
@ -435,6 +475,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return return
} }
if (game == null) {
Log.warning("[EmulationFragment] Game not yet initialized in onViewCreated - will be set up by async intent handler")
return
}
completeViewSetup()
}
private fun completeViewSetup() {
if (_binding == null || game == null) {
return
}
Log.info("[EmulationFragment] Starting view setup for game: ${game?.title}")
gpuModel = GpuDriverHelper.getGpuModel().toString() gpuModel = GpuDriverHelper.getGpuModel().toString()
fwVersion = NativeLibrary.firmwareVersion() fwVersion = NativeLibrary.firmwareVersion()
@ -471,10 +525,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
}) })
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).apply {
val titleView = findViewById<TextView>(R.id.text_game_title) updateGameTitle()
titleView.text = game.title
}
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply { binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt() val lockMode = IntSetting.LOCK_DRAWER.getInt()
@ -541,13 +593,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true true
} }
R.id.menu_multiplayer -> { R.id.menu_multiplayer -> {
emulationActivity?.displayMultiplayerDialog() emulationActivity?.displayMultiplayerDialog()
true true
} }
R.id.menu_controls -> { R.id.menu_controls -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity( val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null, null,
@ -616,8 +666,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
) )
GameIconUtils.loadGameIcon(game, binding.loadingImage) GameIconUtils.loadGameIcon(game!!, binding.loadingImage)
binding.loadingTitle.text = game.title binding.loadingTitle.text = game!!.title
binding.loadingTitle.isSelected = true binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true binding.loadingText.isSelected = true
@ -656,7 +706,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface() emulationState.updateSurface()
// Setup overlays
updateShowStatsOverlay() updateShowStatsOverlay()
updateSocOverlay() updateSocOverlay()
@ -666,7 +715,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val cpuBackendLabel = findViewById<TextView>(R.id.cpu_backend) val cpuBackendLabel = findViewById<TextView>(R.id.cpu_backend)
val vendorLabel = findViewById<TextView>(R.id.gpu_vendor) val vendorLabel = findViewById<TextView>(R.id.gpu_vendor)
titleView.text = game.title titleView.text = game?.title ?: ""
cpuBackendLabel.text = NativeLibrary.getCpuBackend() cpuBackendLabel.text = NativeLibrary.getCpuBackend()
vendorLabel.text = NativeLibrary.getGpuDriver() vendorLabel.text = NativeLibrary.getGpuDriver()
} }
@ -707,7 +756,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationViewModel.emulationStopped.collect(viewLifecycleOwner) { stopped -> emulationViewModel.emulationStopped.collect(viewLifecycleOwner) { stopped ->
if (stopped && emulationViewModel.programChanged.value != -1) { if (stopped && emulationViewModel.programChanged.value != -1) {
perfStatsRunnable?.let { runnable -> perfStatsUpdateHandler.removeCallbacks(runnable) } perfStatsRunnable?.let { runnable ->
perfStatsUpdateHandler.removeCallbacks(
runnable
)
}
socRunnable?.let { runnable -> socUpdateHandler.removeCallbacks(runnable) } socRunnable?.let { runnable -> socUpdateHandler.removeCallbacks(runnable) }
emulationState.changeProgram(emulationViewModel.programChanged.value) emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1) emulationViewModel.setProgramChanged(-1)
@ -716,7 +769,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
if (it) startEmulation() Log.info("[EmulationFragment] Driver interaction allowed: $it")
if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
startEmulation()
}
}
driverViewModel.onLaunchGame()
val currentDriverState = driverViewModel.isInteractionAllowed.value
Log.info("[EmulationFragment] Checking initial driver state after onLaunchGame: $currentDriverState")
if (currentDriverState && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
Log.info("[EmulationFragment] Starting emulation immediately - driver already ready")
startEmulation()
} }
} }
@ -728,6 +793,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateScreenLayout() updateScreenLayout()
Log.info("[EmulationFragment] Calling emulationState.run() - surface will start emulation when available")
emulationState.run(emulationActivity!!.isActivityRecreated, programIndex) emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
} }
} }
@ -761,6 +827,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun updateGameTitle() {
game?.let {
binding.inGameMenu.getHeaderView(0).apply {
val titleView = findViewById<TextView>(R.id.text_game_title)
titleView.text = it.title
}
}
}
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()
@ -921,7 +996,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
if (BooleanSetting.PERF_OVERLAY_BACKGROUND.getBoolean(needsGlobal)) { if (BooleanSetting.PERF_OVERLAY_BACKGROUND.getBoolean(needsGlobal)) {
binding.showStatsOverlayText.setBackgroundResource(R.color.yuzu_transparent_black) binding.showStatsOverlayText.setBackgroundResource(
R.color.yuzu_transparent_black
)
} else { } else {
binding.showStatsOverlayText.setBackgroundResource(0) binding.showStatsOverlayText.setBackgroundResource(0)
} }
@ -1016,31 +1093,48 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) { ) {
sb.setLength(0) sb.setLength(0)
if (BooleanSetting.SHOW_DEVICE_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) { if (BooleanSetting.SHOW_DEVICE_MODEL.getBoolean(
NativeConfig.isPerGameConfigLoaded()
)
) {
sb.append(Build.MODEL) sb.append(Build.MODEL)
} }
if (BooleanSetting.SHOW_GPU_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) { if (BooleanSetting.SHOW_GPU_MODEL.getBoolean(
NativeConfig.isPerGameConfigLoaded()
)
) {
if (sb.isNotEmpty()) sb.append(" | ") if (sb.isNotEmpty()) sb.append(" | ")
sb.append(gpuModel) sb.append(gpuModel)
} }
if (Build.VERSION.SDK_INT >= 31) { if (Build.VERSION.SDK_INT >= 31) {
if (BooleanSetting.SHOW_SOC_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) { if (BooleanSetting.SHOW_SOC_MODEL.getBoolean(
NativeConfig.isPerGameConfigLoaded()
)
) {
if (sb.isNotEmpty()) sb.append(" | ") if (sb.isNotEmpty()) sb.append(" | ")
sb.append(Build.SOC_MODEL) sb.append(Build.SOC_MODEL)
} }
} }
if (BooleanSetting.SHOW_FW_VERSION.getBoolean(NativeConfig.isPerGameConfigLoaded())) { if (BooleanSetting.SHOW_FW_VERSION.getBoolean(
NativeConfig.isPerGameConfigLoaded()
)
) {
if (sb.isNotEmpty()) sb.append(" | ") if (sb.isNotEmpty()) sb.append(" | ")
sb.append(fwVersion) sb.append(fwVersion)
} }
binding.showSocOverlayText.text = sb.toString() binding.showSocOverlayText.text = sb.toString()
if (BooleanSetting.SOC_OVERLAY_BACKGROUND.getBoolean(NativeConfig.isPerGameConfigLoaded())) { if (BooleanSetting.SOC_OVERLAY_BACKGROUND.getBoolean(
binding.showSocOverlayText.setBackgroundResource(R.color.yuzu_transparent_black) NativeConfig.isPerGameConfigLoaded()
)
) {
binding.showSocOverlayText.setBackgroundResource(
R.color.yuzu_transparent_black
)
} else { } else {
binding.showSocOverlayText.setBackgroundResource(0) binding.showSocOverlayText.setBackgroundResource(0)
} }
@ -1055,7 +1149,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() { private fun updateOrientation() {
emulationActivity?.let { emulationActivity?.let {
@ -1167,11 +1260,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height) Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height)
if (!emulationStarted) {
Log.info("[EmulationFragment] Starting emulation")
emulationStarted = true
emulationState.newSurface(holder.surface) emulationState.newSurface(holder.surface)
} else {
Log.debug("[EmulationFragment] Emulation already started, updating surface")
emulationState.newSurface(holder.surface)
}
} }
override fun surfaceDestroyed(holder: SurfaceHolder) { override fun surfaceDestroyed(holder: SurfaceHolder) {
emulationState.clearSurface() emulationState.clearSurface()
emulationStarted = false
} }
private fun showOverlayOptions() { private fun showOverlayOptions() {
@ -1412,7 +1513,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
lateinit var emulationThread: Thread lateinit var emulationThread: Thread
init { init {
// Starting state is stopped.
state = State.STOPPED state = State.STOPPED
} }
@ -1420,7 +1520,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val isStopped: Boolean val isStopped: Boolean
get() = state == State.STOPPED get() = state == State.STOPPED
// Getters for the current state
@get:Synchronized @get:Synchronized
val isPaused: Boolean val isPaused: Boolean
get() = state == State.PAUSED get() = state == State.PAUSED
@ -1440,7 +1539,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
// State changing methods
@Synchronized @Synchronized
fun pause() { fun pause() {
if (state != State.PAUSED) { if (state != State.PAUSED) {