remove refresh icon and implement pull down to refresh

This commit is contained in:
2026-03-12 16:47:58 +05:00
parent f44703a9fa
commit a40986f9b8
2 changed files with 132 additions and 40 deletions

View File

@@ -134,7 +134,7 @@ fun FileBrowser(
// Parent directory item
if (canNavigateUp) {
item {
FileItem(
FileItemCard(
name = "..",
size = "",
isDirectory = true,
@@ -145,7 +145,7 @@ fun FileBrowser(
}
items(files) { file ->
FileItem(
FileItemCard(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
@@ -164,7 +164,7 @@ fun FileBrowser(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FileItem(
fun FileItemCard(
name: String,
size: String,
isDirectory: Boolean = false,

View File

@@ -5,18 +5,28 @@
package sh.sar.isodroid.ui.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Album
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Eject
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
@@ -42,10 +52,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import sh.sar.isodroid.data.IsoFile
import sh.sar.isodroid.data.MountOptions
import sh.sar.isodroid.ui.components.CreateImgDialog
import sh.sar.isodroid.ui.components.FileContextMenu
import sh.sar.isodroid.ui.components.FileBrowser
import sh.sar.isodroid.ui.components.FileItemCard
import sh.sar.isodroid.ui.components.MountDialog
import sh.sar.isodroid.ui.components.StatusCard
import sh.sar.isodroid.viewmodel.MainViewModel
@@ -66,6 +75,22 @@ fun MainScreen(
var showCreateImgDialog by remember { mutableStateOf(false) }
var contextMenuFile by remember { mutableStateOf<IsoFile?>(null) }
val pullToRefreshState = rememberPullToRefreshState()
// Handle pull-to-refresh
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
viewModel.refresh()
}
}
// Stop refreshing when loading completes
LaunchedEffect(uiState.isLoading) {
if (!uiState.isLoading) {
pullToRefreshState.endRefresh()
}
}
// Show error messages
LaunchedEffect(uiState.errorMessage) {
uiState.errorMessage?.let { message ->
@@ -104,12 +129,6 @@ fun MainScreen(
contentDescription = "Create IMG"
)
}
IconButton(onClick = { viewModel.refresh() }) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh"
)
}
IconButton(onClick = onNavigateToDownloads) {
Icon(
imageVector = Icons.Default.Download,
@@ -147,45 +166,118 @@ fun MainScreen(
}
}
) { paddingValues ->
Column(
val hapticFeedback = LocalHapticFeedback.current
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
StatusCard(
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported,
rootDenied = uiState.rootDenied,
onRequestRoot = { viewModel.requestRootAccess() }
)
Spacer(modifier = Modifier.height(16.dp))
if (uiState.isLoading) {
if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
} else if (uiState.hasRoot == true && uiState.isSupported == true) {
FileBrowser(
files = uiState.isoFiles,
currentPath = uiState.currentPath,
onFileClick = { file ->
selectedFile = file
showMountDialog = true
},
onFileLongClick = { file ->
contextMenuFile = file
},
onNavigateUp = { viewModel.navigateUp() },
canNavigateUp = viewModel.canNavigateUp(),
modifier = Modifier.weight(1f)
)
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// Status card
item {
StatusCard(
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported,
rootDenied = uiState.rootDenied,
onRequestRoot = { viewModel.requestRootAccess() }
)
}
// File browser content
if (uiState.hasRoot == true && uiState.isSupported == true) {
if (uiState.isoFiles.isEmpty()) {
// Empty state
item {
Column(
modifier = Modifier
.fillParentMaxSize()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Default.Album,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "No ISO/IMG files found",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Place ISO or IMG files in:\n${uiState.currentPath}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Tap + to create an empty IMG file\nChange directory in Settings",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
}
} else {
// Parent directory navigation
if (viewModel.canNavigateUp()) {
item {
FileItemCard(
name = "..",
size = "",
isDirectory = true,
onClick = { viewModel.navigateUp() },
onLongClick = null
)
}
}
// File list
items(uiState.isoFiles) { file ->
FileItemCard(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
isImg = file.name.lowercase().endsWith(".img"),
onClick = {
selectedFile = file
showMountDialog = true
},
onLongClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
contextMenuFile = file
}
)
}
}
}
}
}
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}