From 068dc188ecb433c97eee9128b0ce179b588b28d6 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 27 May 2025 12:35:57 +0700 Subject: [PATCH] fix(notification): Prevent duplicate notifications during backup and restore processes (#933) Avoid calling show() before returning builder for ForegroundInfo --- .../tachiyomi/data/backup/BackupNotifier.kt | 14 ++- .../data/backup/restore/BackupRestorer.kt | 93 ++++++++++++------- .../tachiyomi/data/sync/SyncNotifier.kt | 6 +- .../data/updater/AppUpdateDownloadJob.kt | 7 +- .../data/updater/AppUpdateNotifier.kt | 10 +- 5 files changed, 92 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt index 4f16dcf4c..2720792d3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt @@ -52,7 +52,7 @@ class BackupNotifier(private val context: Context) { setAutoCancel(false) } - private fun NotificationCompat.Builder.show(id: Int) { + internal fun NotificationCompat.Builder.show(id: Int) { context.notify(id, build()) } @@ -63,7 +63,11 @@ class BackupNotifier(private val context: Context) { setProgress(0, 0, true) } - builder.show(Notifications.ID_BACKUP_PROGRESS) + // KMK --> + // Avoid calling show() before returning builder for ForegroundInfo. + // Calling show() here can cause duplicate notifications, as setForegroundSafely will display the notification using the returned builder. + // builder.show(Notifications.ID_BACKUP_PROGRESS) + // KMK <-- return builder } @@ -129,7 +133,11 @@ class BackupNotifier(private val context: Context) { ) } - builder.show(Notifications.ID_RESTORE_PROGRESS) + // KMK --> + // Avoid calling show() before returning builder for ForegroundInfo. + // Calling show() here can cause duplicate notifications, as setForegroundSafely will display the notification using the returned builder. + // builder.show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- return builder } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index 1c4959d8d..683e31308 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.backup.restore.restorers.FeedRestorer import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaRestorer import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceRestorer import eu.kanade.tachiyomi.data.backup.restore.restorers.SavedSearchRestorer +import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.util.system.createFileInCacheDir import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope @@ -139,12 +140,17 @@ class BackupRestorer( categoriesRestorer(backupCategories) restoreProgress += 1 - notifier.showRestoreProgress( - context.stringResource(MR.strings.categories), - restoreProgress, - restoreAmount, - isSync, - ) + with(notifier) { + showRestoreProgress( + context.stringResource(MR.strings.categories), + restoreProgress, + restoreAmount, + isSync, + ) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } // SY --> @@ -161,12 +167,17 @@ class BackupRestorer( // KMK <-- restoreProgress += 1 - notifier.showRestoreProgress( - context.stringResource(KMR.strings.saved_searches_feeds), - restoreProgress, - restoreAmount, - isSync, - ) + with(notifier) { + showRestoreProgress( + context.stringResource(KMR.strings.saved_searches_feeds), + restoreProgress, + restoreAmount, + isSync, + ) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } // SY <-- @@ -186,7 +197,12 @@ class BackupRestorer( } restoreProgress += 1 - notifier.showRestoreProgress(it.title, restoreProgress, restoreAmount, isSync) + with(notifier) { + showRestoreProgress(it.title, restoreProgress, restoreAmount, isSync) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } } @@ -201,12 +217,17 @@ class BackupRestorer( ) restoreProgress += 1 - notifier.showRestoreProgress( - context.stringResource(MR.strings.app_settings), - restoreProgress, - restoreAmount, - isSync, - ) + with(notifier) { + showRestoreProgress( + context.stringResource(MR.strings.app_settings), + restoreProgress, + restoreAmount, + isSync, + ) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } private fun CoroutineScope.restoreSourcePreferences(preferences: List) = launch { @@ -214,12 +235,17 @@ class BackupRestorer( preferenceRestorer.restoreSource(preferences) restoreProgress += 1 - notifier.showRestoreProgress( - context.stringResource(MR.strings.source_settings), - restoreProgress, - restoreAmount, - isSync, - ) + with(notifier) { + showRestoreProgress( + context.stringResource(MR.strings.source_settings), + restoreProgress, + restoreAmount, + isSync, + ) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } private fun CoroutineScope.restoreExtensionRepos( @@ -236,12 +262,17 @@ class BackupRestorer( } restoreProgress += 1 - notifier.showRestoreProgress( - context.stringResource(MR.strings.extensionRepo_settings), - restoreProgress, - restoreAmount, - isSync, - ) + with(notifier) { + showRestoreProgress( + context.stringResource(MR.strings.extensionRepo_settings), + restoreProgress, + restoreAmount, + isSync, + ) + // KMK --> + .show(Notifications.ID_RESTORE_PROGRESS) + // KMK <-- + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt index ee4641086..8363b4cc7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt @@ -74,7 +74,11 @@ class SyncNotifier(private val context: Context) { ) } - builder.show(Notifications.ID_SYNC_PROGRESS) + // KMK --> + // Avoid calling show() before returning builder for ForegroundInfo. + // Calling show() here can cause duplicate notifications, as setForegroundSafely will display the notification using the returned builder. + // builder.show(Notifications.ID_SYNC_PROGRESS) + // KMK <-- return builder } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt index 1414537da..76486ed80 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt @@ -124,7 +124,12 @@ class AppUpdateDownloadJob(private val context: Context, workerParams: WorkerPar */ private suspend fun downloadApk(title: String, url: String) = coroutineScope { // Show notification download starting. - notifier.onDownloadStarted(title) + with(notifier) { + onDownloadStarted(title) + // KMK --> + .show() + // KMK <-- + } val progressListener = object : ProgressListener { // KMK --> 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 85082e878..37b29d5cc 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 @@ -32,7 +32,7 @@ internal class AppUpdateNotifier(private val context: Context) { * * @param id id of the notification channel. */ - private fun NotificationCompat.Builder.show(id: Int = Notifications.ID_APP_UPDATER) { + internal fun NotificationCompat.Builder.show(id: Int = Notifications.ID_APP_UPDATER) { context.notify(id, build()) } @@ -102,7 +102,13 @@ internal class AppUpdateNotifier(private val context: Context) { NotificationReceiver.cancelDownloadAppUpdatePendingBroadcast(context), ) } - notificationBuilder.show() + + // KMK --> + // Avoid calling show() before returning builder for ForegroundInfo. + // Calling show() here can cause duplicate notifications, as setForegroundSafely will display the notification using the returned builder. + // notificationBuilder.show() + // KMK <-- + return notificationBuilder }