fix: upload progress no longer stuck at 0%
UploadWorker now promotes to foreground + reports phase immediately: - 'Hashing…' while computing SHA256 - 'Checking server…' during resume status check - 'Uploading…' with per-chunk percentage Downloads tab renders the phase label for uploads
This commit is contained in:
@@ -45,6 +45,7 @@ data class ActiveDownload(
|
|||||||
val filename: String,
|
val filename: String,
|
||||||
val progress: Int,
|
val progress: Int,
|
||||||
val done: Boolean,
|
val done: Boolean,
|
||||||
|
val status: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
class DashcamViewModel(application: Application) : AndroidViewModel(application) {
|
class DashcamViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
@@ -317,9 +318,10 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
wm.getWorkInfoByIdLiveData(uuid).observeForever { info ->
|
wm.getWorkInfoByIdLiveData(uuid).observeForever { info ->
|
||||||
if (info == null) return@observeForever
|
if (info == null) return@observeForever
|
||||||
val progress = info.progress.getInt("pct", 0)
|
val progress = info.progress.getInt("pct", 0)
|
||||||
|
val status = info.progress.getString("status")
|
||||||
val done = info.state == WorkInfo.State.SUCCEEDED || info.state == WorkInfo.State.FAILED
|
val done = info.state == WorkInfo.State.SUCCEEDED || info.state == WorkInfo.State.FAILED
|
||||||
|
|
||||||
_uploadProgress.value = _uploadProgress.value + (file.name to ActiveDownload(workerId, file.name, progress, done))
|
_uploadProgress.value = _uploadProgress.value + (file.name to ActiveDownload(workerId, file.name, progress, done, status))
|
||||||
|
|
||||||
when (info.state) {
|
when (info.state) {
|
||||||
WorkInfo.State.SUCCEEDED -> {
|
WorkInfo.State.SUCCEEDED -> {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ fun DownloadsScreen(
|
|||||||
Spacer(Modifier.width(10.dp))
|
Spacer(Modifier.width(10.dp))
|
||||||
Column {
|
Column {
|
||||||
Text("↑ $filename", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text("↑ $filename", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
Text("${dl.progress}%", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
Text(uploadLabel(dl), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +245,12 @@ fun DownloadsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun uploadLabel(dl: ActiveDownload): String {
|
||||||
|
val pct = "${dl.progress}%"
|
||||||
|
val status = dl.status ?: return pct
|
||||||
|
return if (dl.progress > 0) "$status · $pct" else status
|
||||||
|
}
|
||||||
|
|
||||||
private fun formatSize(bytes: Long): String = when {
|
private fun formatSize(bytes: Long): String = when {
|
||||||
bytes >= 1_000_000_000 -> "%.1f GB".format(bytes / 1_000_000_000.0)
|
bytes >= 1_000_000_000 -> "%.1f GB".format(bytes / 1_000_000_000.0)
|
||||||
bytes >= 1_000_000 -> "%.1f MB".format(bytes / 1_000_000.0)
|
bytes >= 1_000_000 -> "%.1f MB".format(bytes / 1_000_000.0)
|
||||||
|
|||||||
@@ -53,11 +53,16 @@ class UploadWorker(
|
|||||||
if (!file.exists()) return@withContext Result.failure(workDataOf("error" to "File not found"))
|
if (!file.exists()) return@withContext Result.failure(workDataOf("error" to "File not found"))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Promote to foreground immediately so large files aren't killed during hashing
|
||||||
|
setForeground(DownloadWorker.createForegroundNotification(applicationContext, "Preparing $filename", 0))
|
||||||
|
setProgressAsync(workDataOf("pct" to 0, "status" to "Hashing…"))
|
||||||
|
|
||||||
// Compute SHA256
|
// Compute SHA256
|
||||||
val sha256 = computeSha256(file)
|
val sha256 = computeSha256(file)
|
||||||
val relativePath = filename // Using filename as relative_path since flat dir
|
val relativePath = filename // Using filename as relative_path since flat dir
|
||||||
|
|
||||||
// Check status
|
// Check status
|
||||||
|
setProgressAsync(workDataOf("pct" to 0, "status" to "Checking server…"))
|
||||||
val statusUrl = "$serverUrl/api/v1/dashcam/upload/status?relative_path=$relativePath&sha256=$sha256"
|
val statusUrl = "$serverUrl/api/v1/dashcam/upload/status?relative_path=$relativePath&sha256=$sha256"
|
||||||
val statusReq = Request.Builder().url(statusUrl)
|
val statusReq = Request.Builder().url(statusUrl)
|
||||||
.addHeader("X-Device-Name", deviceName)
|
.addHeader("X-Device-Name", deviceName)
|
||||||
@@ -77,6 +82,7 @@ class UploadWorker(
|
|||||||
|
|
||||||
val totalChunks = ((file.length() + CHUNK_SIZE - 1) / CHUNK_SIZE).toInt()
|
val totalChunks = ((file.length() + CHUNK_SIZE - 1) / CHUNK_SIZE).toInt()
|
||||||
setForeground(DownloadWorker.createForegroundNotification(applicationContext, "Uploading $filename", 0))
|
setForeground(DownloadWorker.createForegroundNotification(applicationContext, "Uploading $filename", 0))
|
||||||
|
setProgressAsync(workDataOf("pct" to 0, "status" to "Uploading…"))
|
||||||
|
|
||||||
// Upload missing chunks
|
// Upload missing chunks
|
||||||
for (i in 0 until totalChunks) {
|
for (i in 0 until totalChunks) {
|
||||||
@@ -114,7 +120,7 @@ class UploadWorker(
|
|||||||
|
|
||||||
val pct = ((i + 1) * 100) / totalChunks
|
val pct = ((i + 1) * 100) / totalChunks
|
||||||
setForeground(DownloadWorker.createForegroundNotification(applicationContext, "Uploading $filename", pct))
|
setForeground(DownloadWorker.createForegroundNotification(applicationContext, "Uploading $filename", pct))
|
||||||
setProgressAsync(workDataOf("pct" to pct))
|
setProgressAsync(workDataOf("pct" to pct, "status" to "Uploading…"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return@withContext Result.success(workDataOf("filename" to filename))
|
return@withContext Result.success(workDataOf("filename" to filename))
|
||||||
|
|||||||
Reference in New Issue
Block a user