Add consolidated alert rule editing
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package se.ajpanton.notificationsmaster.alerts
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/** All editable rule fields apart from app assignment, local order, and identity. */
|
||||
data class AlertRuleDefinition(
|
||||
val sources: Set<AlertSource>,
|
||||
val matcher: AlertTextMatcher = AlertTextMatcher(),
|
||||
val outcome: AlertOutcome,
|
||||
val profileId: String? = null,
|
||||
val playToCompletion: Boolean = false,
|
||||
val allowDuringDnd: Boolean = false,
|
||||
val enabled: Boolean = true,
|
||||
val name: String? = null,
|
||||
) {
|
||||
init {
|
||||
require(sources.isNotEmpty())
|
||||
require((outcome == AlertOutcome.PLAY_PROFILE) == (profileId != null))
|
||||
require(name?.isNotBlank() != false)
|
||||
}
|
||||
}
|
||||
|
||||
data class ConsolidatedAlertRule(
|
||||
val definition: AlertRuleDefinition,
|
||||
val ruleIds: Set<String>,
|
||||
val packageNames: Set<String>,
|
||||
)
|
||||
|
||||
object AlertRuleEditor {
|
||||
fun consolidate(rules: Iterable<AlertRule>): List<ConsolidatedAlertRule> =
|
||||
rules.groupBy(AlertRule::groupingKey).values.map { group ->
|
||||
ConsolidatedAlertRule(
|
||||
definition = group.first().definition(),
|
||||
ruleIds = group.mapTo(linkedSetOf()) { it.id },
|
||||
packageNames = group.mapTo(sortedSetOf()) { it.packageName },
|
||||
)
|
||||
}.sortedWith(compareBy<ConsolidatedAlertRule> { it.definition.name ?: "" }.thenBy { it.packageNames.first() })
|
||||
|
||||
fun updateConsolidated(
|
||||
configuration: AlertConfiguration,
|
||||
ruleIds: Set<String>,
|
||||
definition: AlertRuleDefinition,
|
||||
): AlertConfiguration = configuration.copy(
|
||||
rules = configuration.rules.map { rule ->
|
||||
if (rule.id in ruleIds) rule.withDefinition(definition) else rule
|
||||
},
|
||||
)
|
||||
|
||||
fun updateForApp(
|
||||
configuration: AlertConfiguration,
|
||||
ruleId: String,
|
||||
definition: AlertRuleDefinition,
|
||||
): AlertConfiguration = updateConsolidated(configuration, setOf(ruleId), definition)
|
||||
|
||||
fun addForApps(
|
||||
configuration: AlertConfiguration,
|
||||
packageNames: Iterable<String>,
|
||||
definition: AlertRuleDefinition,
|
||||
newId: () -> String = { UUID.randomUUID().toString() },
|
||||
): AlertConfiguration {
|
||||
val added = packageNames.distinct().map { packageName ->
|
||||
AlertRule(
|
||||
id = newId(),
|
||||
packageName = packageName,
|
||||
order = configuration.rules.filter { it.packageName == packageName }.maxOfOrNull { it.order }?.plus(1) ?: 0,
|
||||
sources = definition.sources,
|
||||
matcher = definition.matcher,
|
||||
outcome = definition.outcome,
|
||||
profileId = definition.profileId,
|
||||
playToCompletion = definition.playToCompletion,
|
||||
allowDuringDnd = definition.allowDuringDnd,
|
||||
enabled = definition.enabled,
|
||||
name = definition.name,
|
||||
)
|
||||
}
|
||||
return configuration.copy(rules = configuration.rules + added)
|
||||
}
|
||||
|
||||
private fun AlertRule.definition() = AlertRuleDefinition(
|
||||
sources, matcher, outcome, profileId, playToCompletion, allowDuringDnd, enabled, name,
|
||||
)
|
||||
|
||||
private fun AlertRule.withDefinition(definition: AlertRuleDefinition) = copy(
|
||||
sources = definition.sources,
|
||||
matcher = definition.matcher,
|
||||
outcome = definition.outcome,
|
||||
profileId = definition.profileId,
|
||||
playToCompletion = definition.playToCompletion,
|
||||
allowDuringDnd = definition.allowDuringDnd,
|
||||
enabled = definition.enabled,
|
||||
name = definition.name,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package se.ajpanton.notificationsmaster.alerts
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class AlertRuleEditorTest {
|
||||
private val profile = AlertProfile("profile", "Profile", "content://sound")
|
||||
private val definition = AlertRuleDefinition(setOf(AlertSource.NOTIFICATION_POST), outcome = AlertOutcome.PLAY_PROFILE, profileId = profile.id)
|
||||
|
||||
@Test
|
||||
fun consolidatesIdenticalRulesAcrossApps() {
|
||||
val consolidated = AlertRuleEditor.consolidate(listOf(rule("one", "chat.app", 1), rule("two", "mail.app", 4)))
|
||||
|
||||
assertEquals(1, consolidated.size)
|
||||
assertEquals(setOf("one", "two"), consolidated.single().ruleIds)
|
||||
assertEquals(setOf("chat.app", "mail.app"), consolidated.single().packageNames)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun globalEditPreservesEveryAppsIdentityAndOrder() {
|
||||
val original = configuration(rule("one", "chat.app", 1), rule("two", "mail.app", 4))
|
||||
val edited = AlertRuleEditor.updateConsolidated(
|
||||
original,
|
||||
setOf("one", "two"),
|
||||
definition.copy(name = "All messages", playToCompletion = true),
|
||||
)
|
||||
|
||||
assertEquals(listOf("one", "two"), edited.rules.map { it.id })
|
||||
assertEquals(listOf("chat.app", "mail.app"), edited.rules.map { it.packageName })
|
||||
assertEquals(listOf(1, 4), edited.rules.map { it.order })
|
||||
assertEquals(listOf("All messages", "All messages"), edited.rules.map { it.name })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appEditSplitsOnlyThatAppsRule() {
|
||||
val original = configuration(rule("one", "chat.app", 1), rule("two", "mail.app", 4))
|
||||
val edited = AlertRuleEditor.updateForApp(
|
||||
original,
|
||||
"one",
|
||||
definition.copy(matcher = AlertTextMatcher(mode = AlertTextMode.CONTAINS, value = "urgent")),
|
||||
)
|
||||
|
||||
assertEquals(2, AlertRuleEditor.consolidate(edited.rules).size)
|
||||
assertEquals(AlertTextMode.CONTAINS, edited.rules.first { it.id == "one" }.matcher.mode)
|
||||
assertEquals(AlertTextMode.ANY, edited.rules.first { it.id == "two" }.matcher.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun newRuleAppendsAfterExistingRulesForEachApp() {
|
||||
val original = configuration(rule("one", "chat.app", 2))
|
||||
val ids = iterOf("chat-new", "mail-new")
|
||||
val edited = AlertRuleEditor.addForApps(original, listOf("chat.app", "mail.app"), definition, ids::next)
|
||||
|
||||
assertEquals(listOf(2, 3, 0), edited.rules.map { it.order })
|
||||
assertEquals(listOf("chat-new", "mail-new"), edited.rules.drop(1).map { it.id })
|
||||
}
|
||||
|
||||
private fun rule(id: String, packageName: String, order: Int) = AlertRule(
|
||||
id, packageName, order, definition.sources, definition.matcher, definition.outcome, definition.profileId,
|
||||
)
|
||||
|
||||
private fun configuration(vararg rules: AlertRule) = AlertConfiguration(listOf(profile), rules = rules.toList())
|
||||
|
||||
private fun iterOf(vararg values: String) = values.iterator()
|
||||
}
|
||||
Reference in New Issue
Block a user