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.download.DownloadService
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.UpdaterService
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
@ -72,6 +73,7 @@ class NotificationReceiver : BroadcastReceiver() {
) )
// Cancel library update and dismiss notification // Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context) ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
ACTION_CANCEL_UPDATE_DOWNLOAD -> cancelDownloadUpdate(context)
ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context) ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context)
// Share backup file // Share backup file
ACTION_SHARE_BACKUP -> ACTION_SHARE_BACKUP ->
@ -262,6 +264,10 @@ class NotificationReceiver : BroadcastReceiver() {
Handler().post { dismissNotification(context, Notifications.ID_RESTORE_PROGRESS) } Handler().post { dismissNotification(context, Notifications.ID_RESTORE_PROGRESS) }
} }
private fun cancelDownloadUpdate(context: Context) {
UpdaterService.stop(context)
}
companion object { companion object {
private const val NAME = "NotificationReceiver" private const val NAME = "NotificationReceiver"
@ -600,6 +606,19 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) 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. * 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) setOngoing(true)
clearActions() clearActions()
// Cancel action
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.cancel),
NotificationReceiver.cancelUpdateDownloadPendingBroadcast(context)
)
addReleasePageAction() addReleasePageAction()
} }
notificationBuilder.show() notificationBuilder.show()
@ -179,5 +185,8 @@ internal class UpdaterNotifier(private val context: Context) {
} }
notificationBuilder.show(Notifications.ID_UPDATER) 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.network.newCallWithProgress
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.storage.saveTo
import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isServiceRunning 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.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.internal.http2.ErrorCode
import okhttp3.internal.http2.StreamResetException
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
@ -36,17 +43,17 @@ class UpdaterService : Service() {
private lateinit var notifier: UpdaterNotifier private lateinit var notifier: UpdaterNotifier
private var runningJob: Job? = null
private var runningCall: Call? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
notifier = UpdaterNotifier(this) notifier = UpdaterNotifier(this)
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build()) startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock( wakeLock = acquireWakeLock(javaClass.name)
PowerManager.PARTIAL_WAKE_LOCK,
"${javaClass.name}:WakeLock"
)
wakeLock.acquire()
} }
/** /**
@ -57,14 +64,20 @@ class UpdaterService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) return START_NOT_STICKY 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 url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return START_NOT_STICKY
val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name) val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name)
GlobalScope.launch(Dispatchers.IO) { runningJob = GlobalScope.launch(handler) {
downloadApk(title, url) downloadApk(title, url)
} }
stopSelf(startId) runningJob?.invokeOnCompletion { stopSelf(startId) }
return START_NOT_STICKY return START_NOT_STICKY
} }
@ -79,6 +92,8 @@ class UpdaterService : Service() {
} }
private fun destroyJob() { private fun destroyJob() {
runningJob?.cancel()
runningCall?.cancel()
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
wakeLock.release() wakeLock.release()
} }
@ -113,7 +128,9 @@ class UpdaterService : Service() {
try { try {
// Download the new update. // 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. // File where the apk will be saved.
val apkFile = File(externalCacheDir, "update.apk") val apkFile = File(externalCacheDir, "update.apk")
@ -127,7 +144,13 @@ class UpdaterService : Service() {
notifier.onDownloadFinished(apkFile.getUriCompat(this)) notifier.onDownloadFinished(apkFile.getUriCompat(this))
} catch (error: Exception) { } catch (error: Exception) {
Timber.e(error) 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. * Returns [PendingIntent] that starts a service which downloads the apk specified in url.
* *