feat: cancel button on active downloads

- ActiveDownload now carries workerId (UUID string) for cancellation
- cancelDownload() calls WorkManager.cancelWorkById()
- Downloads tab shows ✕ button on in-progress download cards
This commit is contained in:
Mathieu Moran
2026-08-12 03:35:03 -04:00
parent 78cd05c4cb
commit b523d77e16
3 changed files with 46 additions and 13 deletions
@@ -23,6 +23,7 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import kotlinx.serialization.json.Json
import java.io.File
import java.util.UUID
data class DownloadProgress(
val path: String,
@@ -37,8 +38,9 @@ data class DownloadedFile(
)
data class ActiveDownload(
val workerId: String, // UUID string to cancel
val filename: String,
val progress: Int, // 0-100
val progress: Int,
val done: Boolean,
)
@@ -140,19 +142,19 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
.build()
val wm = WorkManager.getInstance(getApplication())
val uuid = request.id
val workerId = uuid.toString()
wm.enqueue(request)
// Observe progress
val workerId = request.id
_activeDownloads.value = _activeDownloads.value + (filename to ActiveDownload(filename, 0, false))
_activeDownloads.value = _activeDownloads.value + (filename to ActiveDownload(workerId, filename, 0, false))
wm.getWorkInfoByIdLiveData(workerId).observeForever { info ->
wm.getWorkInfoByIdLiveData(uuid).observeForever { info ->
if (info == null) return@observeForever
val progress = info.progress.getInt("pct", 0)
val done = info.state == WorkInfo.State.SUCCEEDED || info.state == WorkInfo.State.FAILED
_activeDownloads.value = _activeDownloads.value + (filename to ActiveDownload(filename, progress, done))
_activeDownloads.value = _activeDownloads.value + (filename to ActiveDownload(workerId, filename, progress, done))
when (info.state) {
WorkInfo.State.SUCCEEDED -> {
@@ -169,7 +171,7 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
}
_activeDownloads.value = _activeDownloads.value - filename
}
else -> {} // running / enqueued
else -> {}
}
}
@@ -197,6 +199,17 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
}
}
fun cancelDownload(dl: ActiveDownload) {
try {
val uuid = java.util.UUID.fromString(dl.workerId)
WorkManager.getInstance(getApplication()).cancelWorkById(uuid)
_activeDownloads.value = _activeDownloads.value - dl.filename
viewModelScope.launch {
_downloadEvents.emit("✕ Cancelled: ${dl.filename}")
}
} catch (_: Exception) {}
}
fun deleteVideo(video: VideoFile) {
viewModelScope.launch {
try {
@@ -37,6 +37,7 @@ fun DownloadsScreen(
files: List<DownloadedFile>,
activeDownloads: Map<String, ActiveDownload>,
onDelete: (DownloadedFile) -> Unit,
onCancel: (ActiveDownload) -> Unit,
onRefresh: () -> Unit,
) {
val context = LocalContext.current
@@ -80,11 +81,29 @@ fun DownloadsScreen(
)
) {
Column(modifier = Modifier.padding(12.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (dl.done) "✓ $filename" else "⏳ $filename",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.weight(1f)
)
if (!dl.done) {
Button(
onClick = { onCancel(dl) },
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.error
),
modifier = Modifier.height(32.dp)
) {
Text("✕", style = MaterialTheme.typography.bodySmall)
}
}
}
if (!dl.done) {
Spacer(modifier = Modifier.height(4.dp))
LinearProgressIndicator(
@@ -105,9 +105,10 @@ fun YarisMainScreen(client: OkHttpClient) {
onDelete = { dashcamVm.deleteVideo(it) },
)
1 -> DownloadsScreen(
activeDownloads = activeDownloads,
files = downloadedFiles,
activeDownloads = activeDownloads,
onDelete = { dashcamVm.deleteDownloaded(it) },
onCancel = { dashcamVm.cancelDownload(it) },
onRefresh = { dashcamVm.scanDownloads() },
)
2 -> DualNetworkTestScreen(