diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/services/AlarmManagerService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/services/AlarmManagerService.kt
index d8244de8060d..956c7b8343a0 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/services/AlarmManagerService.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/services/AlarmManagerService.kt
@@ -30,6 +30,7 @@ import com.ichi2.anki.common.time.TimeManager
import com.ichi2.anki.reviewreminders.ReviewReminder
import com.ichi2.anki.reviewreminders.ReviewRemindersDatabase
import com.ichi2.anki.showThemedToast
+import kotlinx.serialization.SerializationException
import timber.log.Timber
import java.util.Calendar
import kotlin.time.Duration
@@ -223,7 +224,25 @@ class AlarmManagerService : BroadcastReceiver() {
private fun scheduleAllEnabledReviewReminderNotifications(context: Context) {
Timber.d("scheduleAllEnabledReviewReminderNotifications")
val allReviewRemindersAsMap =
- ReviewRemindersDatabase.getAllAppWideReminders() + ReviewRemindersDatabase.getAllDeckSpecificReminders()
+ try {
+ ReviewRemindersDatabase.getAllAppWideReminders() + ReviewRemindersDatabase.getAllDeckSpecificReminders()
+ } catch (e: SerializationException) {
+ Timber.w(e, "Failed to read review reminders from storage")
+ try {
+ showThemedToast(context, context.getString(R.string.boot_service_review_reminders_corrupt), false)
+ } catch (toastException: Exception) {
+ Timber.w(toastException, "Failed to show review reminders corrupt toast")
+ }
+ return
+ } catch (e: IllegalArgumentException) {
+ Timber.w(e, "Failed to read review reminders from storage")
+ try {
+ showThemedToast(context, context.getString(R.string.boot_service_review_reminders_corrupt), false)
+ } catch (toastException: Exception) {
+ Timber.w(toastException, "Failed to show review reminders corrupt toast")
+ }
+ return
+ }
val enabledReviewReminders = allReviewRemindersAsMap.values.filter { it.enabled }
for (reviewReminder in enabledReviewReminders) {
scheduleReviewReminderNotification(context, reviewReminder)
diff --git a/AnkiDroid/src/main/res/values/03-dialogs.xml b/AnkiDroid/src/main/res/values/03-dialogs.xml
index f2173c5cc6ed..473b50848ef9 100644
--- a/AnkiDroid/src/main/res/values/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values/03-dialogs.xml
@@ -152,6 +152,7 @@
Failed to schedule reminders
Too many reminders scheduled. Some will not appear
+ Review reminder data could not be read. Some reminders may not appear
Set Language
diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/services/AlarmManagerServiceTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/services/AlarmManagerServiceTest.kt
index 4e5bd96d08e8..f20e35cd6481 100644
--- a/AnkiDroid/src/test/java/com/ichi2/anki/services/AlarmManagerServiceTest.kt
+++ b/AnkiDroid/src/test/java/com/ichi2/anki/services/AlarmManagerServiceTest.kt
@@ -34,6 +34,7 @@ import com.ichi2.anki.reviewreminders.ReviewReminder
import com.ichi2.anki.reviewreminders.ReviewReminderId
import com.ichi2.anki.reviewreminders.ReviewReminderTime
import com.ichi2.anki.reviewreminders.ReviewRemindersDatabase
+import com.ichi2.anki.showThemedToast
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
@@ -159,6 +160,21 @@ class AlarmManagerServiceTest : RobolectricTest() {
verify(exactly = 3) { alarmManager.setWindow(AlarmManager.RTC_WAKEUP, any(), 10.minutes.inWholeMilliseconds, any()) }
}
+ @Test
+ fun `scheduleAllNotifications does not crash and shows toast when review reminder data is corrupt`() {
+ mockkStatic("com.ichi2.anki.UIUtilsKt")
+ every { showThemedToast(any(), any(), any()) } returns Unit
+
+ ReviewRemindersDatabase.remindersSharedPrefs.edit {
+ putString(ReviewRemindersDatabase.APP_WIDE_KEY, "not valid json at all")
+ }
+
+ AlarmManagerService.scheduleAllNotifications(context)
+
+ verify(exactly = 0) { alarmManager.setWindow(any(), any(), any(), any()) }
+ verify(exactly = 1) { showThemedToast(any(), any(), false) }
+ }
+
@Test
fun `onReceive schedules snoozed notification and cancels clicked notification`() {
val extras = mockk()