remove refresh icon and implement pull down to refresh
This commit is contained in:
@@ -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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user