From 67392cbdae5d776a76565d5e18feb94015170af4 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Tue, 10 Mar 2026 14:04:36 +0500 Subject: [PATCH] Fix flickering status card on app startup --- app/src/main/assets/osicons/arch.svg | 1 + app/src/main/assets/osicons/artix.svg | 1 + .../sar/isodroid/ui/components/StatusCard.kt | 29 ++++++++++++------- .../sh/sar/isodroid/ui/screens/MainScreen.kt | 4 +-- .../sar/isodroid/ui/screens/SettingsScreen.kt | 2 +- .../sar/isodroid/viewmodel/MainViewModel.kt | 8 +++-- 6 files changed, 29 insertions(+), 16 deletions(-) create mode 120000 app/src/main/assets/osicons/arch.svg create mode 120000 app/src/main/assets/osicons/artix.svg diff --git a/app/src/main/assets/osicons/arch.svg b/app/src/main/assets/osicons/arch.svg new file mode 120000 index 0000000..830aabc --- /dev/null +++ b/app/src/main/assets/osicons/arch.svg @@ -0,0 +1 @@ +archlinux.svg \ No newline at end of file diff --git a/app/src/main/assets/osicons/artix.svg b/app/src/main/assets/osicons/artix.svg new file mode 120000 index 0000000..2377f30 --- /dev/null +++ b/app/src/main/assets/osicons/artix.svg @@ -0,0 +1 @@ +artixlinux.svg \ No newline at end of file diff --git a/app/src/main/java/sh/sar/isodroid/ui/components/StatusCard.kt b/app/src/main/java/sh/sar/isodroid/ui/components/StatusCard.kt index 2e70e56..0feb2ca 100644 --- a/app/src/main/java/sh/sar/isodroid/ui/components/StatusCard.kt +++ b/app/src/main/java/sh/sar/isodroid/ui/components/StatusCard.kt @@ -33,16 +33,25 @@ import sh.sar.isodroid.ui.theme.UnmountedGray @Composable fun StatusCard( mountStatus: MountStatus, - rootAvailable: Boolean, - deviceSupported: Boolean, + rootAvailable: Boolean?, + deviceSupported: Boolean?, modifier: Modifier = Modifier ) { + // Only show errors when explicitly false (not null = checking) + val showRootError = rootAvailable == false + val showSupportError = deviceSupported == false + val hasError = showRootError || showSupportError + + // Hide card completely until checks are done + if (rootAvailable == null || (rootAvailable == true && deviceSupported == null)) { + return + } + Card( modifier = modifier.fillMaxWidth(), colors = CardDefaults.cardColors( containerColor = when { - !rootAvailable -> MaterialTheme.colorScheme.errorContainer - !deviceSupported -> MaterialTheme.colorScheme.errorContainer + hasError -> MaterialTheme.colorScheme.errorContainer mountStatus.mounted -> MaterialTheme.colorScheme.primaryContainer else -> MaterialTheme.colorScheme.surfaceVariant } @@ -61,13 +70,13 @@ fun StatusCard( Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = when { - !rootAvailable || !deviceSupported -> Icons.Default.Error + hasError -> Icons.Default.Error mountStatus.mounted -> Icons.Default.CheckCircle else -> Icons.Default.Usb }, contentDescription = null, tint = when { - !rootAvailable || !deviceSupported -> ErrorRed + hasError -> ErrorRed mountStatus.mounted -> MountedGreen else -> UnmountedGray }, @@ -77,21 +86,21 @@ fun StatusCard( Column { Text( text = when { - !rootAvailable -> "Root Access Required" - !deviceSupported -> "Device Not Supported" + showRootError -> "Root Access Required" + showSupportError -> "Device Not Supported" mountStatus.mounted -> "Mounted" else -> "Not Mounted" }, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold ) - if (!rootAvailable) { + if (showRootError) { Text( text = "Grant root access to use ISODroid", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) - } else if (!deviceSupported) { + } else if (showSupportError) { Text( text = "USB gadget not supported on this device", style = MaterialTheme.typography.bodySmall, diff --git a/app/src/main/java/sh/sar/isodroid/ui/screens/MainScreen.kt b/app/src/main/java/sh/sar/isodroid/ui/screens/MainScreen.kt index cf7148f..e88cafb 100644 --- a/app/src/main/java/sh/sar/isodroid/ui/screens/MainScreen.kt +++ b/app/src/main/java/sh/sar/isodroid/ui/screens/MainScreen.kt @@ -92,7 +92,7 @@ fun MainScreen( actions = { IconButton( onClick = { showCreateImgDialog = true }, - enabled = uiState.hasRoot && uiState.isSupported + enabled = uiState.hasRoot == true && uiState.isSupported == true ) { Icon( imageVector = Icons.Default.Add, @@ -163,7 +163,7 @@ fun MainScreen( ) { CircularProgressIndicator() } - } else if (uiState.hasRoot && uiState.isSupported) { + } else if (uiState.hasRoot == true && uiState.isSupported == true) { FileBrowser( files = uiState.isoFiles, currentPath = uiState.currentPath, diff --git a/app/src/main/java/sh/sar/isodroid/ui/screens/SettingsScreen.kt b/app/src/main/java/sh/sar/isodroid/ui/screens/SettingsScreen.kt index 9aaaa66..8280802 100644 --- a/app/src/main/java/sh/sar/isodroid/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/sh/sar/isodroid/ui/screens/SettingsScreen.kt @@ -163,7 +163,7 @@ fun SettingsScreen( modifier = Modifier.padding(16.dp) ) { // Root Access status - val hasRoot = uiState.hasRoot + val hasRoot = uiState.hasRoot ?: false val rootDenied = uiState.rootDenied PermissionStatusItem( icon = Icons.Default.Security, diff --git a/app/src/main/java/sh/sar/isodroid/viewmodel/MainViewModel.kt b/app/src/main/java/sh/sar/isodroid/viewmodel/MainViewModel.kt index ad6d78a..e1f24a4 100644 --- a/app/src/main/java/sh/sar/isodroid/viewmodel/MainViewModel.kt +++ b/app/src/main/java/sh/sar/isodroid/viewmodel/MainViewModel.kt @@ -178,6 +178,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { loadFiles() checkMountStatus() } + } else { + _uiState.update { it.copy(isSupported = false) } } _uiState.update { it.copy(isLoading = false) } @@ -380,7 +382,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { viewModelScope.launch { // Check cached status (no popup) val cachedStatus = RootManager.isRootGrantedCached() - if (cachedStatus == true && !_uiState.value.hasRoot) { + if (cachedStatus == true && _uiState.value.hasRoot != true) { // Root was granted externally (e.g., in Magisk settings) _uiState.update { it.copy(hasRoot = true, rootDenied = false) } @@ -546,9 +548,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { data class MainUiState( val isLoading: Boolean = true, - val hasRoot: Boolean = false, + val hasRoot: Boolean? = null, // null = checking, true = granted, false = denied val rootDenied: Boolean = false, // True if user denied root in Magisk - val isSupported: Boolean = false, + val isSupported: Boolean? = null, // null = checking, true = supported, false = not supported val mountStatus: MountStatus = MountStatus.UNMOUNTED, val isoFiles: List = emptyList(), val currentPath: String = "",