updates: fix stale UI states & cancel-all coverage
- AppUpdateItem now shows a disabled "Installing" button while the download is COMPLETED and awaiting install, instead of briefly reverting to "Update" between download-done and InstallerEvent.Installed. - Section header replaces all-or-nothing "Update all" toggle with an any-active check, so "Cancel all" appears as soon as a single update is in flight. Same treatment for the ownership-approval section. - Empty placeholder no longer hides the ignored-updates list when there are no live updates. - DownloadHelper.cancelAll also flips COMPLETED -> CANCELLED so the Cancel-all button gives immediate feedback for pending-install rows. - DownloadListItem: surface the failed icon for FAILED downloads too.
This commit is contained in:
@@ -67,7 +67,10 @@ fun DownloadListItem(modifier: Modifier = Modifier, download: Download, onClick:
|
||||
tint = Color(0xFF4CAF50)
|
||||
)
|
||||
}
|
||||
} else if (download.status == DownloadStatus.CANCELLED) {
|
||||
} else if (
|
||||
download.status == DownloadStatus.CANCELLED ||
|
||||
download.status == DownloadStatus.FAILED
|
||||
) {
|
||||
{
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_cancel),
|
||||
|
||||
@@ -43,7 +43,8 @@ fun AppUpdateItem(
|
||||
onCancel: () -> Unit = {},
|
||||
onUnignore: (() -> Unit)? = null
|
||||
) {
|
||||
val inProgress = download != null && download.status !in DownloadStatus.finished
|
||||
val inProgress = download != null && !download.isFinished
|
||||
val pendingInstall = download?.status == DownloadStatus.COMPLETED
|
||||
val progress = if (download?.status == DownloadStatus.DOWNLOADING) {
|
||||
download.progress.toFloat()
|
||||
} else {
|
||||
@@ -64,7 +65,7 @@ fun AppUpdateItem(
|
||||
AnimatedAppIcon(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)),
|
||||
iconUrl = update.iconURL,
|
||||
inProgress = inProgress,
|
||||
inProgress = inProgress || pendingInstall,
|
||||
progress = progress
|
||||
)
|
||||
}
|
||||
@@ -99,6 +100,12 @@ fun AppUpdateItem(
|
||||
}
|
||||
}
|
||||
|
||||
pendingInstall -> {
|
||||
OutlinedButton(onClick = {}, enabled = false) {
|
||||
Text(stringResource(R.string.action_installing))
|
||||
}
|
||||
}
|
||||
|
||||
inProgress -> {
|
||||
OutlinedButton(onClick = onCancel) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.ShimmerUpdateItem
|
||||
import com.aurora.store.compose.composable.app.AppUpdateItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.data.room.update.Update
|
||||
import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
@@ -72,8 +73,8 @@ fun UpdatesScreen(
|
||||
}
|
||||
val (mainEntries, approvalEntries, incompatibleEntries) = groupedUpdates
|
||||
|
||||
val mainAllEnqueued = mainEntries.isNotEmpty() &&
|
||||
mainEntries.all { it.value?.isRunning == true }
|
||||
val mainAnyActive = mainEntries.any { it.value.isActive() }
|
||||
val approvalAnyActive = approvalEntries.any { it.value.isActive() }
|
||||
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@@ -92,7 +93,7 @@ fun UpdatesScreen(
|
||||
}
|
||||
}
|
||||
|
||||
updateMap.isEmpty() -> {
|
||||
updateMap.isEmpty() && ignoredUpdates.isEmpty() -> {
|
||||
Placeholder(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(R.drawable.ic_updates),
|
||||
@@ -119,8 +120,8 @@ fun UpdatesScreen(
|
||||
}
|
||||
)
|
||||
val actionLabel = stringResource(
|
||||
if (mainAllEnqueued) {
|
||||
R.string.action_cancel
|
||||
if (mainAnyActive) {
|
||||
R.string.download_cancel_all
|
||||
} else {
|
||||
R.string.action_update_all
|
||||
}
|
||||
@@ -130,7 +131,7 @@ fun UpdatesScreen(
|
||||
trailing = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (mainAllEnqueued) {
|
||||
if (mainAnyActive) {
|
||||
onCancelAll()
|
||||
} else {
|
||||
onRequestUpdateAll(mainEntries.map { it.key })
|
||||
@@ -151,15 +152,28 @@ fun UpdatesScreen(
|
||||
|
||||
if (approvalEntries.isNotEmpty()) {
|
||||
item(key = "header_approval") {
|
||||
val actionLabel = stringResource(
|
||||
if (approvalAnyActive) {
|
||||
R.string.download_cancel_all
|
||||
} else {
|
||||
R.string.action_update_all
|
||||
}
|
||||
)
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.updates_approval_header),
|
||||
subtitle = stringResource(R.string.updates_approval_desc),
|
||||
trailing = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onRequestUpdateAll(approvalEntries.map { it.key })
|
||||
if (approvalAnyActive) {
|
||||
onCancelAll()
|
||||
} else {
|
||||
onRequestUpdateAll(
|
||||
approvalEntries.map { it.key }
|
||||
)
|
||||
}
|
||||
) { Text(stringResource(R.string.action_update_all)) }
|
||||
}
|
||||
) { Text(actionLabel) }
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -212,6 +226,11 @@ fun UpdatesScreen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun Download?.isActive(): Boolean {
|
||||
if (this == null) return false
|
||||
return !isFinished || status == DownloadStatus.COMPLETED
|
||||
}
|
||||
|
||||
private fun LazyListScope.updateItems(
|
||||
entries: List<Map.Entry<Update, Download?>>,
|
||||
keyPrefix: String,
|
||||
|
||||
@@ -165,9 +165,14 @@ class DownloadHelper @Inject constructor(
|
||||
* @param updatesOnly Whether to cancel only updates, defaults to false
|
||||
*/
|
||||
suspend fun cancelAll(updatesOnly: Boolean = false) {
|
||||
// Cancel all enqueued downloads first to avoid triggering re-download
|
||||
// Cancel queued/completed downloads first to avoid triggering re-download and to give
|
||||
// the user immediate feedback for items that already finished downloading. The actual
|
||||
// OS-level install for COMPLETED entries is wrapped in NonCancellable inside the
|
||||
// worker and cannot be aborted — if it succeeds, the update row is removed via
|
||||
// InstallerEvent.Installed; if it fails, the user can re-trigger an update.
|
||||
val cancellableStatuses = setOf(DownloadStatus.QUEUED, DownloadStatus.COMPLETED)
|
||||
downloadDao.downloads().firstOrNull()
|
||||
?.filter { it.status == DownloadStatus.QUEUED }
|
||||
?.filter { it.status in cancellableStatuses }
|
||||
?.filter { if (updatesOnly) it.isInstalled else true }
|
||||
?.forEach {
|
||||
downloadDao.updateStatus(it.packageName, DownloadStatus.CANCELLED)
|
||||
|
||||
Reference in New Issue
Block a user