Add custom alert queue controller

This commit is contained in:
ajp_anton
2026-08-16 17:17:39 +00:00
parent 720db4b7d3
commit c209ef24bb
3 changed files with 167 additions and 0 deletions
@@ -150,6 +150,22 @@ data class AlertDecision(
val allowDuringDnd: Boolean,
)
/** Resolved at match time so queued alerts never point at mutable settings. */
data class AlertSnapshot(
val soundUri: String?,
val vibrationPattern: List<Long>,
val playToCompletion: Boolean,
val allowDuringDnd: Boolean,
) {
init {
require(soundUri != null || vibrationPattern.any { it > 0 })
}
}
fun AlertDecision.snapshot(): AlertSnapshot? = profile?.let {
AlertSnapshot(it.soundUri, it.vibrationPattern, playToCompletion, allowDuringDnd)
}
data class AlertEvaluation(
val decisions: List<AlertDecision>,
val silenceUnmatchedDirectAlert: Boolean,
@@ -0,0 +1,60 @@
package se.ajpanton.notificationsmaster.alerts
data class QueuedAlert(
val ruleId: String,
val snapshot: AlertSnapshot,
)
/** The first alert is playing; any following alerts are waiting. */
data class AlertQueue(
val alerts: List<QueuedAlert> = emptyList(),
) {
val current get() = alerts.firstOrNull()
}
data class AlertQueueTransition(
val queue: AlertQueue,
val started: QueuedAlert? = null,
val interrupted: QueuedAlert? = null,
val dropped: QueuedAlert? = null,
val completed: QueuedAlert? = null,
)
object AlertQueueController {
fun enqueue(
queue: AlertQueue,
incoming: QueuedAlert,
settings: AlertQueueSettings,
): AlertQueueTransition {
val alerts = queue.alerts
if (alerts.isEmpty()) return AlertQueueTransition(AlertQueue(listOf(incoming)), started = incoming)
val tail = alerts.last()
val appended = if (tail.snapshot.playToCompletion) alerts + incoming else alerts.dropLast(1) + incoming
val interrupted = tail.takeUnless { tail.snapshot.playToCompletion }
if (appended.size <= settings.maximumLength) {
return AlertQueueTransition(
AlertQueue(appended),
started = incoming.takeIf { tail == alerts.first() && interrupted != null },
interrupted = interrupted,
)
}
return when (settings.overflow) {
AlertQueueOverflow.DROP_NEWEST -> AlertQueueTransition(queue, dropped = incoming)
AlertQueueOverflow.DROP_OLDEST -> {
val next = appended.drop(1)
AlertQueueTransition(
AlertQueue(next),
started = next.first(),
interrupted = alerts.first(),
)
}
}
}
fun completeCurrent(queue: AlertQueue): AlertQueueTransition {
val current = queue.current ?: return AlertQueueTransition(queue)
val next = queue.alerts.drop(1)
return AlertQueueTransition(AlertQueue(next), started = next.firstOrNull(), completed = current)
}
}