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