Skip to content

Commit f5a8a3a

Browse files
Giyutomioka-SSdavid-allison
authored andcommitted
fix(sync): Cancel notifications on worker cancellation
1 parent e1f4294 commit f5a8a3a

3 files changed

Lines changed: 74 additions & 6 deletions

File tree

AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncMediaWorker.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,23 @@ class SyncMediaWorker(
8383
monitorProgress(backend)
8484
}
8585
} catch (cancellationException: CancellationException) {
86-
Timber.w(cancellationException)
86+
Timber.w(cancellationException, "SyncMediaWorker cancelled (user tapped Cancel or WorkManager cancelled)")
87+
notificationManager?.cancel(NotificationId.SYNC_MEDIA)
88+
Timber.d("SyncMediaWorker: progress notification cancelled after worker cancellation")
8789
cancelMediaSync(CollectionManager.getBackend())
8890
throw cancellationException
8991
} catch (throwable: Throwable) {
90-
Timber.w(throwable)
92+
Timber.w(throwable, "SyncMediaWorker failed")
9193
notify {
9294
setContentTitle(CollectionManager.TR.syncMediaFailed())
9395
throwable.localizedMessage?.let { message ->
9496
setContentText(message)
9597
}
9698
}
99+
Timber.d("SyncMediaWorker: showing failure notification")
97100
return Result.failure()
98101
}
99-
Timber.d("SyncMediaWorker: cancelling notification")
102+
Timber.d("SyncMediaWorker: cancelling progress notification (sync completed)")
100103
notificationManager?.cancel(NotificationId.SYNC_MEDIA)
101104

102105
Timber.d("SyncMediaWorker: success")

AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncWorker.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,23 @@ class SyncWorker(
9999
try {
100100
syncCollection(auth, shouldSyncMedia)
101101
} catch (cancellationException: CancellationException) {
102+
Timber.w(cancellationException, "SyncWorker cancelled (user tapped Cancel or WorkManager cancelled)")
103+
notificationManager?.cancel(NotificationId.SYNC)
104+
Timber.d("SyncWorker: progress notification cancelled after worker cancellation")
102105
cancelSync(CollectionManager.getBackend())
103106
throw cancellationException
104107
} catch (throwable: Throwable) {
105-
Timber.w(throwable)
108+
Timber.w(throwable, "SyncWorker failed")
106109
notify {
107110
setContentTitle(applicationContext.getString(R.string.sync_error))
108111
throwable.localizedMessage?.let { message ->
109112
setContentText(message)
110113
}
111114
}
115+
Timber.d("SyncWorker: showing failure notification")
112116
return Result.failure()
113117
}
114-
Timber.d("SyncWorker: cancelling notification")
118+
Timber.d("SyncWorker: cancelling progress notification (sync completed)")
115119
notificationManager?.cancel(NotificationId.SYNC)
116120

117121
Timber.d("SyncWorker: success")

AnkiDroid/src/test/java/com/ichi2/anki/worker/SyncMediaWorkerTest.kt

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,74 @@
1515
*/
1616
package com.ichi2.anki.worker
1717

18+
import androidx.core.app.NotificationManagerCompat
19+
import androidx.test.ext.junit.runners.AndroidJUnit4
20+
import androidx.work.Data
21+
import androidx.work.testing.TestListenableWorkerBuilder
22+
import com.ichi2.anki.CollectionManager
1823
import com.ichi2.anki.NOTIFICATION_MIN_DELAY_MS
24+
import com.ichi2.anki.RobolectricTest
25+
import com.ichi2.anki.notifications.NotificationId
26+
import com.ichi2.utils.Permissions
27+
import io.mockk.every
28+
import io.mockk.mockk
29+
import io.mockk.mockkObject
30+
import io.mockk.mockkStatic
31+
import io.mockk.unmockkAll
32+
import io.mockk.verify
33+
import net.ankiweb.rsdroid.Backend
34+
import org.junit.After
1935
import org.junit.Test
36+
import org.junit.runner.RunWith
37+
38+
@RunWith(AndroidJUnit4::class)
39+
class SyncMediaWorkerTest : RobolectricTest() {
40+
private lateinit var notificationManager: NotificationManagerCompat
41+
private lateinit var backend: Backend
42+
43+
@After
44+
override fun tearDown() {
45+
super.tearDown()
46+
unmockkAll()
47+
}
2048

21-
class SyncMediaWorkerTest {
2249
@Test
2350
@Suppress("SimplifyBooleanWithConstants")
2451
fun `notification update delay is not lower than min delay`() {
2552
assert(SyncMediaWorker.NOTIFICATION_UPDATE_RATE_MS >= NOTIFICATION_MIN_DELAY_MS)
2653
}
54+
55+
@Test
56+
fun `doWork cancels notification when error happens after notification is shown`() =
57+
runTest {
58+
mockkObject(Permissions)
59+
every { Permissions.canPostNotifications(any()) } returns true
60+
61+
mockkStatic(NotificationManagerCompat::class)
62+
notificationManager = mockk(relaxed = true)
63+
every { NotificationManagerCompat.from(any()) } returns notificationManager
64+
65+
mockkObject(CollectionManager)
66+
backend = mockk(relaxed = true)
67+
every { CollectionManager.getColUnsafe().backend } returns backend
68+
every { CollectionManager.getBackend() } returns backend
69+
70+
// Simulate an error after the notification would be shown by throwing from mediaSyncStatus()
71+
every { backend.mediaSyncStatus() } throws IllegalStateException("simulated failure after notification")
72+
73+
val inputData =
74+
Data
75+
.Builder()
76+
.putString("hkey", "test-hkey")
77+
.build()
78+
79+
val worker =
80+
TestListenableWorkerBuilder<SyncMediaWorker>(targetContext)
81+
.setInputData(inputData)
82+
.build()
83+
84+
worker.doWork()
85+
86+
verify { notificationManager.cancel(NotificationId.SYNC_MEDIA) }
87+
}
2788
}

0 commit comments

Comments
 (0)