DownloadWorkerUtil: Rework cancelAll logic to only cancel request downloads

We don't want to cancel all downloads when user requests to cancel only updates.
Also, update the download's status to cancelled atomically.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-02-21 18:39:17 +05:30
parent 11ac8092e2
commit 1a01f96f08
4 changed files with 31 additions and 36 deletions

View File

@@ -4,7 +4,6 @@ import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Update
import com.aurora.gplayapi.data.models.File import com.aurora.gplayapi.data.models.File
import com.aurora.store.data.model.DownloadStatus import com.aurora.store.data.model.DownloadStatus
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -15,9 +14,6 @@ interface DownloadDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(download: Download) suspend fun insert(download: Download)
@Update
suspend fun update(download: Download)
@Query("UPDATE download SET downloadStatus=:downloadStatus WHERE packageName=:packageName") @Query("UPDATE download SET downloadStatus=:downloadStatus WHERE packageName=:packageName")
suspend fun updateStatus(packageName: String, downloadStatus: DownloadStatus) suspend fun updateStatus(packageName: String, downloadStatus: DownloadStatus)
@@ -30,13 +26,12 @@ interface DownloadDao {
@Query( @Query(
""" """
UPDATE download UPDATE download
SET downloadStatus=:downloadStatus, progress=:progress, speed=:speed, timeRemaining=:timeRemaining SET progress=:progress, speed=:speed, timeRemaining=:timeRemaining
WHERE packageName=:packageName WHERE packageName=:packageName
""" """
) )
suspend fun updateStatusProgress( suspend fun updateProgress(
packageName: String, packageName: String,
downloadStatus: DownloadStatus,
progress: Int, progress: Int,
speed: Long, speed: Long,
timeRemaining: Long timeRemaining: Long
@@ -46,7 +41,7 @@ interface DownloadDao {
fun downloads(): Flow<List<Download>> fun downloads(): Flow<List<Download>>
@Query("SELECT * FROM download WHERE packageName = :packageName") @Query("SELECT * FROM download WHERE packageName = :packageName")
suspend fun getDownload(packageName: String): Download? suspend fun getDownload(packageName: String): Download
@Query("DELETE FROM download WHERE packageName = :packageName") @Query("DELETE FROM download WHERE packageName = :packageName")
suspend fun delete(packageName: String) suspend fun delete(packageName: String)

View File

@@ -43,7 +43,6 @@ import java.io.File
import java.net.URL import java.net.URL
import java.security.DigestInputStream import java.security.DigestInputStream
import java.security.MessageDigest import java.security.MessageDigest
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlin.properties.Delegates import kotlin.properties.Delegates
import com.aurora.gplayapi.data.models.File as GPlayFile import com.aurora.gplayapi.data.models.File as GPlayFile
@@ -136,6 +135,7 @@ class DownloadWorker @AssistedInject constructor(
downloadDao.updateSharedLibs(download.packageName, download.sharedLibs) downloadDao.updateSharedLibs(download.packageName, download.sharedLibs)
// Download and verify all files exists // Download and verify all files exists
notifyStatus(DownloadStatus.DOWNLOADING)
requestList.forEach { request -> requestList.forEach { request ->
downloading = true downloading = true
runCatching { downloadFile(request); download.downloadedFiles++ } runCatching { downloadFile(request); download.downloadedFiles++ }
@@ -148,7 +148,8 @@ class DownloadWorker @AssistedInject constructor(
} }
while (downloading) { while (downloading) {
delay(1000) delay(1000)
if (isStopped) { val d = downloadDao.getDownload(download.packageName)
if (isStopped || d.downloadStatus == DownloadStatus.CANCELLED) {
onFailure() onFailure()
break break
} }
@@ -180,13 +181,12 @@ class DownloadWorker @AssistedInject constructor(
withContext(NonCancellable) { withContext(NonCancellable) {
Log.i(TAG, "Cleaning up!") Log.i(TAG, "Cleaning up!")
val cancelReasons = listOf(STOP_REASON_USER, STOP_REASON_CANCELLED_BY_APP) val cancelReasons = listOf(STOP_REASON_USER, STOP_REASON_CANCELLED_BY_APP)
val status = if (isSAndAbove() && stopReason in cancelReasons) { if (isSAndAbove() && stopReason in cancelReasons) {
DownloadStatus.CANCELLED notifyStatus(DownloadStatus.CANCELLED)
} else { } else {
DownloadStatus.FAILED notifyStatus(DownloadStatus.FAILED)
} }
notifyStatus(status)
PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode) PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode)
.deleteRecursively() .deleteRecursively()
with(appContext.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager) { with(appContext.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager) {
@@ -282,14 +282,12 @@ class DownloadWorker @AssistedInject constructor(
} }
download.apply { download.apply {
this.downloadStatus = DownloadStatus.DOWNLOADING
this.progress = progress this.progress = progress
this.speed = downloadInfo.speed this.speed = downloadInfo.speed
this.timeRemaining = bytesRemaining / speed * 1000 this.timeRemaining = bytesRemaining / speed * 1000
} }
downloadDao.updateStatusProgress( downloadDao.updateProgress(
download.packageName, download.packageName,
download.downloadStatus,
download.progress, download.progress,
download.speed, download.speed,
download.timeRemaining download.timeRemaining
@@ -315,18 +313,20 @@ class DownloadWorker @AssistedInject constructor(
} }
private suspend fun notifyStatus(status: DownloadStatus, dID: Int = -1) { private suspend fun notifyStatus(status: DownloadStatus, dID: Int = -1) {
// Update database for all status except downloading which is handled onProgress // Update status in database
if (status != DownloadStatus.DOWNLOADING) { download.downloadStatus = status
download.downloadStatus = status downloadDao.updateStatus(download.packageName, status)
if (download.downloadStatus == DownloadStatus.COMPLETED) {
download.progress = 100
downloadDao.updateStatusProgress(download.packageName, status, 100, 0, 0)
} else {
downloadDao.updateStatus(download.packageName, status)
}
}
if (status == DownloadStatus.CANCELLED) return when (status) {
DownloadStatus.DOWNLOADING, DownloadStatus.CANCELLED -> return
DownloadStatus.COMPLETED -> {
// Mark progress as 100 manually to avoid race conditions
download.progress = 100
downloadDao.updateProgress(download.packageName, 100, 0, 0)
}
else -> {}
}
val notification = NotificationUtil.getDownloadNotification(appContext, download, id, icon) val notification = NotificationUtil.getDownloadNotification(appContext, download, id, icon)
val notificationID = if (dID != -1) dID else download.packageName.hashCode() val notificationID = if (dID != -1) dID else download.packageName.hashCode()

View File

@@ -107,15 +107,15 @@ class DownloadWorkerUtil @Inject constructor(
} }
} }
suspend fun cancelAll(downloads: Boolean = true, updates: Boolean = true) { suspend fun cancelAll(updatesOnly: Boolean = false) {
// Cancel all enqueued downloads first to avoid triggering re-download // Cancel all enqueued downloads first to avoid triggering re-download
downloadsList.value.filter { it.downloadStatus == DownloadStatus.QUEUED }.forEach { downloadsList.value.filter { it.downloadStatus == DownloadStatus.QUEUED }
downloadDao.update(it.copy(downloadStatus = DownloadStatus.CANCELLED)) .filter { if (updatesOnly) it.isInstalled else true }.forEach {
} downloadDao.updateStatus(it.packageName, DownloadStatus.CANCELLED)
}
val workManager = WorkManager.getInstance(context) WorkManager.getInstance(context)
if (downloads) workManager.cancelAllWorkByTag(DOWNLOAD_APP) .cancelAllWorkByTag(if (updatesOnly) DOWNLOAD_UPDATE else DOWNLOAD_APP)
if (updates) workManager.cancelAllWorkByTag(DOWNLOAD_UPDATE)
} }
private fun trigger(download: Download) { private fun trigger(download: Download) {

View File

@@ -96,6 +96,6 @@ class UpdatesViewModel @Inject constructor(
} }
fun cancelAll() { fun cancelAll() {
viewModelScope.launch { downloadWorkerUtil.cancelAll(downloads = false) } viewModelScope.launch { downloadWorkerUtil.cancelAll(true) }
} }
} }