forked from eden-emu/eden
refactor: wip, trying to fix intent launching
This commit is contained in:
parent
e99b129cc2
commit
2c4d5f7a81
1 changed files with 205 additions and 107 deletions
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue