Add Cancel button to App Update Notification (#7309)
* Add cancel button in app update download notif
Since stuck downloads are a common issue and only solution until now was
to force close the app or download and update the app manually by
downloading from GitHub (which clears the notif away)
Based on commit
4dea924337
Co-authored-by: Jays2Kings <8617760+Jays2Kings@users.noreply.github.com>
* Linting by Android Studio
* commit PR Review Suggestion
Update app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt
Co-authored-by: arkon <arkon@users.noreply.github.com>
* Use `launchIO`
copied this over from how j2k was doing it. Launching in IO Thread like
how it was before this PR is sufficient
* Clear previous actions before adding `Cancel`
Otherwise, it led to two identical Cancel buttons
Co-authored-by: Jays2Kings <8617760+Jays2Kings@users.noreply.github.com>
Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
parent
284880d096
commit
fdf384b809
@ -16,6 +16,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.AppUpdateService
|
||||||
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.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
@ -82,6 +83,8 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
)
|
)
|
||||||
// Cancel library update and dismiss notification
|
// Cancel library update and dismiss notification
|
||||||
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context, Notifications.ID_LIBRARY_PROGRESS)
|
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context, Notifications.ID_LIBRARY_PROGRESS)
|
||||||
|
// Cancel downloading app update
|
||||||
|
ACTION_CANCEL_APP_UPDATE_DOWNLOAD -> cancelDownloadAppUpdate(context)
|
||||||
// Open reader activity
|
// Open reader activity
|
||||||
ACTION_OPEN_CHAPTER -> {
|
ACTION_OPEN_CHAPTER -> {
|
||||||
openChapter(
|
openChapter(
|
||||||
@ -218,6 +221,10 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
ContextCompat.getMainExecutor(context).execute { dismissNotification(context, notificationId) }
|
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
|
* 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_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_MARK_AS_READ = "$ID.$NAME.MARK_AS_READ"
|
||||||
private const val ACTION_OPEN_CHAPTER = "$ID.$NAME.ACTION_OPEN_CHAPTER"
|
private const val ACTION_OPEN_CHAPTER = "$ID.$NAME.ACTION_OPEN_CHAPTER"
|
||||||
private const val ACTION_DOWNLOAD_CHAPTER = "$ID.$NAME.ACTION_DOWNLOAD_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)
|
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.
|
* Returns [PendingIntent] that opens the extensions controller.
|
||||||
*
|
*
|
||||||
|
@ -88,6 +88,13 @@ internal class AppUpdateNotifier(private val context: Context) {
|
|||||||
setContentText(context.getString(R.string.update_check_notification_download_in_progress))
|
setContentText(context.getString(R.string.update_check_notification_download_in_progress))
|
||||||
setSmallIcon(android.R.drawable.stat_sys_download)
|
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
|
|
||||||
|
clearActions()
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_close_24dp,
|
||||||
|
context.getString(R.string.action_cancel),
|
||||||
|
NotificationReceiver.cancelUpdateDownloadPendingBroadcast(context),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notificationBuilder.show()
|
notificationBuilder.show()
|
||||||
return notificationBuilder
|
return notificationBuilder
|
||||||
@ -162,4 +169,8 @@ internal class AppUpdateNotifier(private val context: Context) {
|
|||||||
}
|
}
|
||||||
notificationBuilder.show(Notifications.ID_APP_UPDATER)
|
notificationBuilder.show(Notifications.ID_APP_UPDATER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
NotificationReceiver.dismissNotification(context, Notifications.ID_APP_UPDATER)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,12 @@ import eu.kanade.tachiyomi.util.storage.saveTo
|
|||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.internal.http2.ErrorCode
|
||||||
|
import okhttp3.internal.http2.StreamResetException
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -36,6 +41,10 @@ class AppUpdateService : Service() {
|
|||||||
|
|
||||||
private lateinit var notifier: AppUpdateNotifier
|
private lateinit var notifier: AppUpdateNotifier
|
||||||
|
|
||||||
|
private var runningJob: Job? = null
|
||||||
|
|
||||||
|
private var runningCall: Call? = null
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
@ -56,11 +65,11 @@ class AppUpdateService : Service() {
|
|||||||
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)
|
||||||
|
|
||||||
launchIO {
|
runningJob = launchIO {
|
||||||
downloadApk(title, url)
|
downloadApk(title, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopSelf(startId)
|
runningJob?.invokeOnCompletion { stopSelf(startId) }
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +84,8 @@ class AppUpdateService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun destroyJob() {
|
private fun destroyJob() {
|
||||||
|
runningJob?.cancel()
|
||||||
|
runningCall?.cancel()
|
||||||
if (wakeLock.isHeld) {
|
if (wakeLock.isHeld) {
|
||||||
wakeLock.release()
|
wakeLock.release()
|
||||||
}
|
}
|
||||||
@ -109,7 +120,9 @@ class AppUpdateService : 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")
|
||||||
@ -123,7 +136,13 @@ class AppUpdateService : Service() {
|
|||||||
notifier.onDownloadFinished(apkFile.getUriCompat(this))
|
notifier.onDownloadFinished(apkFile.getUriCompat(this))
|
||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
logcat(LogPriority.ERROR, error)
|
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.
|
* Returns [PendingIntent] that starts a service which downloads the apk specified in url.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user