Persist notifications

- Persist the map of active notifications on every change and reload it on
application startup.
- Reshow all active notifications on application startup using the original
reminder due time.
pull/1509/head
Felix Wiemuth 3 years ago
parent 794289b9b8
commit 09c46f5b88

@ -94,6 +94,7 @@ class HabitsApplication : Application() {
taskRunner.execute {
reminderScheduler.scheduleAll()
widgetUpdater.updateWidgets()
notificationTray.reshowAll()
}
}

@ -72,9 +72,10 @@ class HabitsModule(dbFile: File) {
taskRunner: TaskRunner,
commandRunner: CommandRunner,
preferences: Preferences,
screen: AndroidNotificationTray
screen: AndroidNotificationTray,
habitList: HabitList
): NotificationTray {
return NotificationTray(taskRunner, commandRunner, preferences, screen)
return NotificationTray(taskRunner, commandRunner, preferences, screen, habitList)
}
@Provides

@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.models
import kotlinx.serialization.Serializable
import org.isoron.platform.time.LocalDate
import org.isoron.uhabits.core.utils.DateFormats.Companion.getCSVDateFormat
import org.isoron.uhabits.core.utils.DateFormats.Companion.getDialogDateFormat
@ -29,6 +30,7 @@ import java.util.Date
import java.util.GregorianCalendar
import java.util.TimeZone
@Serializable
data class Timestamp(var unixTime: Long) : Comparable<Timestamp> {
constructor(cal: GregorianCalendar) : this(cal.timeInMillis)

@ -18,11 +18,17 @@
*/
package org.isoron.uhabits.core.preferences
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.isoron.platform.time.DayOfWeek
import org.isoron.platform.utils.StringUtils.Companion.joinLongs
import org.isoron.platform.utils.StringUtils.Companion.splitLongs
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.ui.NotificationTray
import org.isoron.uhabits.core.ui.ThemeSwitcher
import org.isoron.uhabits.core.utils.DateUtils.Companion.getFirstWeekdayNumberAccordingToLocale
import java.util.LinkedList
@ -135,6 +141,23 @@ open class Preferences(private val storage: Storage) {
storage.putBoolean("pref_short_toggle", enabled)
}
internal fun setActiveNotifications(activeNotifications: Map<Habit, NotificationTray.NotificationData>) {
val activeById = activeNotifications.mapKeys { it.key.id }
val serialized = Json.encodeToString(activeById)
storage.putString("pref_active_notifications", serialized)
}
internal fun getActiveNotifications(habitList: HabitList): HashMap<Habit, NotificationTray.NotificationData> {
val serialized = storage.getString("pref_active_notifications", "")
return if (serialized == "") {
HashMap()
} else {
val activeById = Json.decodeFromString(MapSerializer(Long.serializer(), NotificationTray.NotificationData.serializer()), serialized)
val activeByHabit = activeById.mapNotNull { (id, v) -> habitList.getById(id)?.let { it to v } }
activeByHabit.toMap(HashMap())
}
}
fun removeListener(listener: Listener) {
listeners.remove(listener)
}

@ -18,17 +18,18 @@
*/
package org.isoron.uhabits.core.ui
import kotlinx.serialization.Serializable
import org.isoron.uhabits.core.AppScope
import org.isoron.uhabits.core.commands.Command
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
import org.isoron.uhabits.core.commands.DeleteHabitsCommand
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.tasks.Task
import org.isoron.uhabits.core.tasks.TaskRunner
import java.util.HashMap
import java.util.Locale
import java.util.Objects
import javax.inject.Inject
@ -38,9 +39,31 @@ class NotificationTray @Inject constructor(
private val taskRunner: TaskRunner,
private val commandRunner: CommandRunner,
private val preferences: Preferences,
private val systemTray: SystemTray
private val systemTray: SystemTray,
private val habitList: HabitList
) : CommandRunner.Listener, Preferences.Listener {
private val active: HashMap<Habit, NotificationData> = HashMap()
/**
* A mapping from habits to active notifications, automatically persisting on removal.
*/
private val active = object {
private val m: HashMap<Habit, NotificationData> =
preferences.getActiveNotifications(habitList)
val entries get() = m.entries
operator fun set(habit: Habit, notificationData: NotificationData) {
m[habit] = notificationData
persist()
}
fun remove(habit: Habit) {
m.remove(habit)?.let { persist() } // persist if changed
}
fun persist() = preferences.setActiveNotifications(m)
}
fun cancel(habit: Habit) {
val notificationId = getNotificationId(habit)
systemTray.removeNotification(notificationId)
@ -64,7 +87,6 @@ class NotificationTray @Inject constructor(
fun show(habit: Habit, timestamp: Timestamp, reminderTime: Long) {
val data = NotificationData(timestamp, reminderTime)
active[habit] = data
taskRunner.execute(ShowNotificationTask(habit, data))
}
@ -83,7 +105,7 @@ class NotificationTray @Inject constructor(
return (id % Int.MAX_VALUE).toInt()
}
private fun reshowAll() {
fun reshowAll() {
for ((habit, data) in active.entries) {
taskRunner.execute(ShowNotificationTask(habit, data))
}
@ -101,6 +123,7 @@ class NotificationTray @Inject constructor(
fun log(msg: String)
}
@Serializable
internal class NotificationData(val timestamp: Timestamp, val reminderTime: Long)
private inner class ShowNotificationTask(private val habit: Habit, data: NotificationData) :
Task {
@ -164,6 +187,17 @@ class NotificationTray @Inject constructor(
timestamp,
reminderTime
)
if (data.shown) {
systemTray.log(
String.format(
Locale.US,
"Showing notification for habit %d silently because it has been shown before.",
habit.id
)
)
}
data.shown = true
active[habit] = data
}
private fun shouldShowReminderToday(): Boolean {

Loading…
Cancel
Save