diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 47039a174..7fc7415c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -16,6 +16,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.AppUpdateService import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaController @@ -82,6 +83,8 @@ class NotificationReceiver : BroadcastReceiver() { ) // Cancel library update and dismiss notification ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context, Notifications.ID_LIBRARY_PROGRESS) + // Cancel downloading app update + ACTION_CANCEL_APP_UPDATE_DOWNLOAD -> cancelDownloadAppUpdate(context) // Open reader activity ACTION_OPEN_CHAPTER -> { openChapter( @@ -218,6 +221,10 @@ class NotificationReceiver : BroadcastReceiver() { ContextCompat.getMainExecutor(context).execute { dismissNotification(context, notificationId) } } + private fun cancelDownloadAppUpdate(context: Context) { + AppUpdateService.stop(context) + } + /** * Method called when user wants to mark manga chapters as read * @@ -279,6 +286,8 @@ class NotificationReceiver : BroadcastReceiver() { private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE" + private const val ACTION_CANCEL_APP_UPDATE_DOWNLOAD = "$ID.$NAME.CANCEL_APP_UPDATE_DOWNLOAD" + private const val ACTION_MARK_AS_READ = "$ID.$NAME.MARK_AS_READ" private const val ACTION_OPEN_CHAPTER = "$ID.$NAME.ACTION_OPEN_CHAPTER" private const val ACTION_DOWNLOAD_CHAPTER = "$ID.$NAME.ACTION_DOWNLOAD_CHAPTER" @@ -508,6 +517,16 @@ class NotificationReceiver : BroadcastReceiver() { return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) } + /** + * + */ + internal fun cancelUpdateDownloadPendingBroadcast(context: Context): PendingIntent { + val intent = Intent(context, NotificationReceiver::class.java).apply { + action = ACTION_CANCEL_APP_UPDATE_DOWNLOAD + } + return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + } + /** * Returns [PendingIntent] that opens the extensions controller. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt index 25b96cd02..ae8846c5a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt @@ -88,6 +88,13 @@ internal class AppUpdateNotifier(private val context: Context) { setContentText(context.getString(R.string.update_check_notification_download_in_progress)) setSmallIcon(android.R.drawable.stat_sys_download) setOngoing(true) + + clearActions() + addAction( + R.drawable.ic_close_24dp, + context.getString(R.string.action_cancel), + NotificationReceiver.cancelUpdateDownloadPendingBroadcast(context), + ) } notificationBuilder.show() return notificationBuilder @@ -162,4 +169,8 @@ internal class AppUpdateNotifier(private val context: Context) { } notificationBuilder.show(Notifications.ID_APP_UPDATER) } + + fun cancel() { + NotificationReceiver.dismissNotification(context, Notifications.ID_APP_UPDATER) + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateService.kt index 5a8808fcc..00ddbd2f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateService.kt @@ -21,7 +21,12 @@ 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.logcat +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Job import logcat.LogPriority +import okhttp3.Call +import okhttp3.internal.http2.ErrorCode +import okhttp3.internal.http2.StreamResetException import uy.kohesive.injekt.injectLazy import java.io.File @@ -36,6 +41,10 @@ class AppUpdateService : Service() { private lateinit var notifier: AppUpdateNotifier + private var runningJob: Job? = null + + private var runningCall: Call? = null + override fun onCreate() { super.onCreate() @@ -56,11 +65,11 @@ class AppUpdateService : Service() { val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return START_NOT_STICKY val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name) - launchIO { + runningJob = launchIO { downloadApk(title, url) } - stopSelf(startId) + runningJob?.invokeOnCompletion { stopSelf(startId) } return START_NOT_STICKY } @@ -75,6 +84,8 @@ class AppUpdateService : Service() { } private fun destroyJob() { + runningJob?.cancel() + runningCall?.cancel() if (wakeLock.isHeld) { wakeLock.release() } @@ -109,7 +120,9 @@ class AppUpdateService : 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") @@ -123,7 +136,13 @@ class AppUpdateService : Service() { notifier.onDownloadFinished(apkFile.getUriCompat(this)) } catch (error: Exception) { logcat(LogPriority.ERROR, error) - notifier.onDownloadError(url) + if (error is CancellationException || + (error is StreamResetException && error.errorCode == ErrorCode.CANCEL) + ) { + notifier.cancel() + } else { + notifier.onDownloadError(url) + } } } @@ -157,6 +176,15 @@ class AppUpdateService : Service() { } } + /** + * Stops the service. + * + * @param context the application context + */ + fun stop(context: Context) { + context.stopService(Intent(context, AppUpdateService::class.java)) + } + /** * Returns [PendingIntent] that starts a service which downloads the apk specified in url. *