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
@@ -134,7 +134,7 @@ fun FileBrowser(
// Parent directory item // Parent directory item
if (canNavigateUp) { if (canNavigateUp) {
item { item {
FileItem( FileItemCard(
name = "..", name = "..",
size = "", size = "",
isDirectory = true, isDirectory = true,
@@ -145,7 +145,7 @@ fun FileBrowser(
} }
items(files) { file -> items(files) { file ->
FileItem( FileItemCard(
name = file.name, name = file.name,
size = file.formattedSize, size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"), isIso = file.name.lowercase().endsWith(".iso"),
@@ -164,7 +164,7 @@ fun FileBrowser(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun FileItem( fun FileItemCard(
name: String, name: String,
size: String, size: String,
isDirectory: Boolean = false, isDirectory: Boolean = false,
@@ -5,18 +5,28 @@
package sh.sar.isodroid.ui.screens package sh.sar.isodroid.ui.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.filled.Add 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.Download
import androidx.compose.material.icons.filled.Eject import androidx.compose.material.icons.filled.Eject
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings 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.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
@@ -42,10 +52,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import sh.sar.isodroid.data.IsoFile 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.CreateImgDialog
import sh.sar.isodroid.ui.components.FileContextMenu 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.MountDialog
import sh.sar.isodroid.ui.components.StatusCard import sh.sar.isodroid.ui.components.StatusCard
import sh.sar.isodroid.viewmodel.MainViewModel import sh.sar.isodroid.viewmodel.MainViewModel
@@ -66,6 +75,22 @@ fun MainScreen(
var showCreateImgDialog by remember { mutableStateOf(false) } var showCreateImgDialog by remember { mutableStateOf(false) }
var contextMenuFile by remember { mutableStateOf<IsoFile?>(null) } 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 // Show error messages
LaunchedEffect(uiState.errorMessage) { LaunchedEffect(uiState.errorMessage) {
uiState.errorMessage?.let { message -> uiState.errorMessage?.let { message ->
@@ -104,12 +129,6 @@ fun MainScreen(
contentDescription = "Create IMG" contentDescription = "Create IMG"
) )
} }
IconButton(onClick = { viewModel.refresh() }) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh"
)
}
IconButton(onClick = onNavigateToDownloads) { IconButton(onClick = onNavigateToDownloads) {
Icon( Icon(
imageVector = Icons.Default.Download, imageVector = Icons.Default.Download,
@@ -147,45 +166,118 @@ fun MainScreen(
} }
} }
) { paddingValues -> ) { paddingValues ->
Column( val hapticFeedback = LocalHapticFeedback.current
Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.padding(16.dp) .nestedScroll(pullToRefreshState.nestedScrollConnection)
) { ) {
StatusCard( if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported,
rootDenied = uiState.rootDenied,
onRequestRoot = { viewModel.requestRootAccess() }
)
Spacer(modifier = Modifier.height(16.dp))
if (uiState.isLoading) {
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
} }
} else if (uiState.hasRoot == true && uiState.isSupported == true) { } else {
FileBrowser( LazyColumn(
files = uiState.isoFiles, modifier = Modifier.fillMaxSize(),
currentPath = uiState.currentPath, contentPadding = PaddingValues(16.dp),
onFileClick = { file -> verticalArrangement = Arrangement.spacedBy(8.dp)
selectedFile = file ) {
showMountDialog = true // Status card
}, item {
onFileLongClick = { file -> StatusCard(
contextMenuFile = file mountStatus = uiState.mountStatus,
}, rootAvailable = uiState.hasRoot,
onNavigateUp = { viewModel.navigateUp() }, deviceSupported = uiState.isSupported,
canNavigateUp = viewModel.canNavigateUp(), rootDenied = uiState.rootDenied,
modifier = Modifier.weight(1f) 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)
)
} }
} }