diff --git a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt index da4bf010..5133e7f2 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt @@ -189,7 +189,7 @@ class EmulationFragment(private val launchPath: String) : Fragment() { } private fun LayoutSideMenuTextItemBinding.setEnabled(isEnabled: Boolean) { - textItem.isClickable = isEnabled + textItem.isEnabled = isEnabled textItem.alpha = if (isEnabled) 1f else 0.7f } diff --git a/src/android/app/src/main/java/info/cemu/cemu/gamelist/GameListViewModel.kt b/src/android/app/src/main/java/info/cemu/cemu/gamelist/GameListViewModel.kt index e1332b7f..345e7391 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/gamelist/GameListViewModel.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/gamelist/GameListViewModel.kt @@ -37,11 +37,13 @@ class GameListViewModel : ViewModel() { ) init { - NativeGameTitles.setGameTitleLoadedCallback(NativeGameTitles.GameTitleLoadedCallback { game: Game? -> - if (game == null || !isGameValid(game)) + NativeGameTitles.setGameTitleLoadedCallback(NativeGameTitles.GameTitleLoadedCallback { game: Game -> + if (!game.isValid()) return@GameTitleLoadedCallback + if (_games.value.any { it.titleId == game.titleId }) return@GameTitleLoadedCallback + _games.value += game }) refreshGames() @@ -63,10 +65,6 @@ class GameListViewModel : ViewModel() { _gameToRemoveShaders.value = null } - private fun isGameValid(game: Game): Boolean { - return !game.path.isNullOrEmpty() && !game.name.isNullOrEmpty() - } - fun setGameTitleFavorite(game: Game, isFavorite: Boolean) { if (!_games.value.contains(game)) { return @@ -82,7 +80,7 @@ class GameListViewModel : ViewModel() { NativeGameTitles.setGameTitleLoadedCallback(null) } - fun checkIfGamePathsHaveChanged(): Boolean { + fun gamePathsHaveChanged(): Boolean { val newGamePaths = NativeSettings.getGamesPaths().toSet() if (newGamePaths != gamePaths) { gamePaths = newGamePaths diff --git a/src/android/app/src/main/java/info/cemu/cemu/gamelist/GamesListScreen.kt b/src/android/app/src/main/java/info/cemu/cemu/gamelist/GamesListScreen.kt index 277f6f71..180f662a 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/gamelist/GamesListScreen.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/gamelist/GamesListScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -56,6 +57,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel @@ -74,7 +76,6 @@ data class GamesListScreenActions( val startGame: (Game) -> Unit, ) - @Composable fun GamesListScreen( selectedGameViewModel: GameViewModel, @@ -83,27 +84,21 @@ fun GamesListScreen( toolbarActions: @Composable RowScope.() -> Unit, ) { val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val lifecycleState by lifecycleOwner.lifecycle.currentStateFlow.collectAsState() val coroutineScope = rememberCoroutineScope() var refreshing by remember { mutableStateOf(false) } val gameToRemoveShaders by gameListViewModel.gameToRemoveShaders.collectAsStateWithLifecycle() val snackbarHostState = remember { SnackbarHostState() } - fun onRefresh() = coroutineScope.launch { - refreshing = true - gameListViewModel.refreshGames() - delay(1500) - refreshing = false - } - val state = rememberPullToRefreshState() - LaunchedEffect(Unit) { - if (gameListViewModel.checkIfGamePathsHaveChanged()) { + LaunchedEffect(lifecycleState) { + if (lifecycleState == Lifecycle.State.RESUMED && gameListViewModel.gamePathsHaveChanged()) gameListViewModel.refreshGames() - } } - DisposableEffect(LocalLifecycleOwner.current) { + DisposableEffect(lifecycleOwner) { onDispose { gameListViewModel.setFilterText("") } @@ -125,7 +120,14 @@ fun GamesListScreen( .pullToRefresh( isRefreshing = refreshing, state = state, - onRefresh = ::onRefresh, + onRefresh = { + coroutineScope.launch { + refreshing = true + gameListViewModel.refreshGames() + delay(1500) + refreshing = false + } + }, ), ) { GameList( @@ -367,35 +369,39 @@ private fun createShortcutForGame( game: Game, onFailedToCreateShortCut: () -> Unit, ) { - val shortcutManager = context.getSystemService( - ShortcutManager::class.java - ) - if (!shortcutManager.isRequestPinShortcutSupported) { + try { + val shortcutManager = context.getSystemService( + ShortcutManager::class.java + ) + if (!shortcutManager.isRequestPinShortcutSupported) { + onFailedToCreateShortCut() + return + } + val icon = game.icon?.asAndroidBitmap().let { + if (it != null) ShortcutIcon.createWithBitmap(it) + else ShortcutIcon.createWithResource(context, R.mipmap.ic_launcher) + } + val intent = Intent( + context, + EmulationActivity::class.java + ) + intent.setAction(Intent.ACTION_VIEW) + intent.putExtra(EmulationActivity.EXTRA_LAUNCH_PATH, game.path) + val pinShortcutInfo = ShortcutInfo.Builder(context, game.titleId.toString()) + .setShortLabel(game.name!!) + .setIntent(intent) + .setIcon(icon) + .build() + val pinnedShortcutCallbackIntent = + shortcutManager.createShortcutResultIntent(pinShortcutInfo) + val successCallback = PendingIntent.getBroadcast( + context, + 0, + pinnedShortcutCallbackIntent, + PendingIntent.FLAG_IMMUTABLE + ) + shortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.intentSender) + } catch (_: Exception) { onFailedToCreateShortCut() - return } - val icon = game.icon?.asAndroidBitmap().let { - if (it != null) ShortcutIcon.createWithBitmap(it) - else ShortcutIcon.createWithResource(context, R.mipmap.ic_launcher) - } - val intent = Intent( - context, - EmulationActivity::class.java - ) - intent.setAction(Intent.ACTION_VIEW) - intent.putExtra(EmulationActivity.EXTRA_LAUNCH_PATH, game.path) - val pinShortcutInfo = ShortcutInfo.Builder(context, game.titleId.toString()) - .setShortLabel(game.name!!) - .setIntent(intent) - .setIcon(icon) - .build() - val pinnedShortcutCallbackIntent = - shortcutManager.createShortcutResultIntent(pinShortcutInfo) - val successCallback = PendingIntent.getBroadcast( - context, - 0, - pinnedShortcutCallbackIntent, - PendingIntent.FLAG_IMMUTABLE - ) - shortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.intentSender) } diff --git a/src/android/app/src/main/java/info/cemu/cemu/nativeinterface/NativeGameTitles.kt b/src/android/app/src/main/java/info/cemu/cemu/nativeinterface/NativeGameTitles.kt index 834e5246..f51ede25 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/nativeinterface/NativeGameTitles.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/nativeinterface/NativeGameTitles.kt @@ -102,11 +102,15 @@ object NativeGameTitles { } val icon: ImageBitmap? = _icon?.asImageBitmap() + + fun isValid(): Boolean { + return !path.isNullOrEmpty() && !name.isNullOrEmpty() + } } @Keep fun interface GameTitleLoadedCallback { - fun onGameTitleLoaded(game: Game?) + fun onGameTitleLoaded(game: Game) } @Keep