Add cancel button in notification to cancel download of updated version

Since if a download gets stuck theres no way to cancel it (then again updating form GitHub would clear this out)
This commit is contained in:
Jays2Kings 2021-04-27 23:10:00 -04:00
parent 3255fac29e
commit 4dea924337
3 changed files with 70 additions and 10 deletions

View File

@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.UpdaterService
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
@ -72,6 +73,7 @@ class NotificationReceiver : BroadcastReceiver() {
)
// Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
ACTION_CANCEL_UPDATE_DOWNLOAD -> cancelDownloadUpdate(context)
ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context)
// Share backup file
ACTION_SHARE_BACKUP ->
@ -262,6 +264,10 @@ class NotificationReceiver : BroadcastReceiver() {
Handler().post { dismissNotification(context, Notifications.ID_RESTORE_PROGRESS) }
}
private fun cancelDownloadUpdate(context: Context) {
UpdaterService.stop(context)
}
companion object {
private const val NAME = "NotificationReceiver"
@ -600,6 +606,19 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
/**
* Returns [PendingIntent] that cancels the download for a Tachiyomi update
*
* @param context context of application
* @return [PendingIntent]
*/
internal fun cancelUpdateDownloadPendingBroadcast(context: Context): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_CANCEL_UPDATE_DOWNLOAD
}
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
/**
* Returns [PendingIntent] that starts a share activity for a backup file.
*

View File

@ -99,6 +99,12 @@ internal class UpdaterNotifier(private val context: Context) {
setOngoing(true)
clearActions()
// Cancel action
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.cancel),
NotificationReceiver.cancelUpdateDownloadPendingBroadcast(context)
)
addReleasePageAction()
}
notificationBuilder.show()
@ -179,5 +185,8 @@ internal class UpdaterNotifier(private val context: Context) {
}
notificationBuilder.show(Notifications.ID_UPDATER)
}
fun cancel() {
NotificationReceiver.dismissNotification(context, Notifications.ID_UPDATER)
}
}

View File

@ -17,10 +17,17 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.newCallWithProgress
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.storage.saveTo
import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isServiceRunning
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.internal.http2.ErrorCode
import okhttp3.internal.http2.StreamResetException
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.io.File
@ -36,17 +43,17 @@ class UpdaterService : Service() {
private lateinit var notifier: UpdaterNotifier
private var runningJob: Job? = null
private var runningCall: Call? = null
override fun onCreate() {
super.onCreate()
notifier = UpdaterNotifier(this)
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"${javaClass.name}:WakeLock"
)
wakeLock.acquire()
wakeLock = acquireWakeLock(javaClass.name)
}
/**
@ -57,14 +64,20 @@ class UpdaterService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) return START_NOT_STICKY
val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception)
stopSelf(startId)
}
val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return START_NOT_STICKY
val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name)
GlobalScope.launch(Dispatchers.IO) {
runningJob = GlobalScope.launch(handler) {
downloadApk(title, url)
}
stopSelf(startId)
runningJob?.invokeOnCompletion { stopSelf(startId) }
return START_NOT_STICKY
}
@ -79,6 +92,8 @@ class UpdaterService : Service() {
}
private fun destroyJob() {
runningJob?.cancel()
runningCall?.cancel()
if (wakeLock.isHeld) {
wakeLock.release()
}
@ -113,7 +128,9 @@ class UpdaterService : Service() {
try {
// Download the new update.
val response = network.client.newCallWithProgress(GET(url), progressListener).await()
val call = network.client.newCallWithProgress(GET(url), progressListener)
runningCall = call
val response = call.await()
// File where the apk will be saved.
val apkFile = File(externalCacheDir, "update.apk")
@ -127,7 +144,13 @@ class UpdaterService : Service() {
notifier.onDownloadFinished(apkFile.getUriCompat(this))
} catch (error: Exception) {
Timber.e(error)
notifier.onDownloadError(url)
if (error is CancellationException ||
(error is StreamResetException && error.errorCode == ErrorCode.CANCEL)
) {
notifier.cancel()
} else {
notifier.onDownloadError(url)
}
}
}
@ -164,6 +187,15 @@ class UpdaterService : Service() {
}
}
/**
* Stops the service.
*
* @param context the application context.
*/
fun stop(context: Context) {
context.stopService(Intent(context, UpdaterService::class.java))
}
/**
* Returns [PendingIntent] that starts a service which downloads the apk specified in url.
*