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:
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -96,6 +96,6 @@ class UpdatesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAll() {
|
fun cancelAll() {
|
||||||
viewModelScope.launch { downloadWorkerUtil.cancelAll(downloads = false) }
|
viewModelScope.launch { downloadWorkerUtil.cancelAll(true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user