Add alert playback controller
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package se.ajpanton.notificationsmaster.alerts
|
||||
|
||||
/** Plays one resolved effect and invokes [onFinished] when all of its effects finish. */
|
||||
fun interface AlertEffectPlayer {
|
||||
fun play(alert: QueuedAlert, onFinished: () -> Unit)
|
||||
|
||||
fun stop() {}
|
||||
}
|
||||
|
||||
/** Serializes effect playback around the pure queue policy. */
|
||||
class AlertPlaybackController(
|
||||
private val effectPlayer: AlertEffectPlayer,
|
||||
private val settings: () -> AlertQueueSettings,
|
||||
) {
|
||||
private var queue = AlertQueue()
|
||||
|
||||
@Synchronized
|
||||
fun enqueue(alert: QueuedAlert): AlertQueueTransition {
|
||||
val before = queue
|
||||
return AlertQueueController.enqueue(queue, alert, settings()).also { transition ->
|
||||
queue = transition.queue
|
||||
playCurrentIfChanged(before.current)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun currentQueue() = queue
|
||||
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
queue = AlertQueue()
|
||||
effectPlayer.stop()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun complete(alert: QueuedAlert) {
|
||||
if (queue.current != alert) return
|
||||
val before = queue
|
||||
queue = AlertQueueController.completeCurrent(queue).queue
|
||||
playCurrentIfChanged(before.current)
|
||||
}
|
||||
|
||||
private fun playCurrentIfChanged(previous: QueuedAlert?) {
|
||||
val current = queue.current
|
||||
if (previous == current) return
|
||||
effectPlayer.stop()
|
||||
current?.let { alert -> effectPlayer.play(alert) { complete(alert) } }
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package se.ajpanton.notificationsmaster.alerts
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class AlertPlaybackControllerTest {
|
||||
private val effects = FakeEffects()
|
||||
private val controller = AlertPlaybackController(effects) { AlertQueueSettings() }
|
||||
|
||||
@Test
|
||||
fun replacementStopsCurrentAndStartsIncomingAlert() {
|
||||
val first = alert("first")
|
||||
val second = alert("second")
|
||||
|
||||
controller.enqueue(first)
|
||||
controller.enqueue(second)
|
||||
|
||||
assertEquals(listOf("first", "second"), effects.played)
|
||||
assertEquals(2, effects.stopCalls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun protectedCurrentContinuesWhileIncomingAlertWaits() {
|
||||
val current = alert("current", protected = true)
|
||||
val incoming = alert("incoming")
|
||||
|
||||
controller.enqueue(current)
|
||||
controller.enqueue(incoming)
|
||||
|
||||
assertEquals(listOf("current"), effects.played)
|
||||
assertEquals(listOf("current", "incoming"), controller.currentQueue().alerts.map { it.ruleId })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun completionStartsTheNextWaitingAlert() {
|
||||
val current = alert("current", protected = true)
|
||||
val incoming = alert("incoming")
|
||||
controller.enqueue(current)
|
||||
controller.enqueue(incoming)
|
||||
|
||||
effects.finish("current")
|
||||
|
||||
assertEquals(listOf("current", "incoming"), effects.played)
|
||||
assertEquals(listOf("incoming"), controller.currentQueue().alerts.map { it.ruleId })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun staleCompletionCannotAdvanceReplacement() {
|
||||
val first = alert("first")
|
||||
controller.enqueue(first)
|
||||
val staleCompletion = effects.callbacks.getValue("first")
|
||||
controller.enqueue(alert("second"))
|
||||
|
||||
staleCompletion()
|
||||
|
||||
assertEquals(listOf("second"), controller.currentQueue().alerts.map { it.ruleId })
|
||||
}
|
||||
|
||||
private fun alert(id: String, protected: Boolean = false) = QueuedAlert(
|
||||
id,
|
||||
AlertSnapshot("content://" + id, emptyList(), protected, false),
|
||||
)
|
||||
|
||||
private class FakeEffects : AlertEffectPlayer {
|
||||
val played = mutableListOf<String>()
|
||||
val callbacks = mutableMapOf<String, () -> Unit>()
|
||||
var stopCalls = 0
|
||||
|
||||
override fun play(alert: QueuedAlert, onFinished: () -> Unit) {
|
||||
played += alert.ruleId
|
||||
callbacks[alert.ruleId] = onFinished
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
stopCalls++
|
||||
}
|
||||
|
||||
fun finish(ruleId: String) = callbacks.getValue(ruleId).invoke()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user