using a process state of keep/removed rather than mapping to an ignored event id

- this state will be used to diff the currently rendered events against the new ones
This commit is contained in:
Adam Brown 2021-10-11 15:32:12 +01:00
parent b7b4c01bde
commit b27fb264fc
5 changed files with 56 additions and 43 deletions
vector/src
main/java/im/vector/app/features/notifications
test/java/im/vector/app/features/notifications

View file

@ -17,6 +17,8 @@
package im.vector.app.features.notifications package im.vector.app.features.notifications
import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.notifications.Processed.KEEP
import im.vector.app.features.notifications.Processed.REMOVE
import javax.inject.Inject import javax.inject.Inject
class NotifiableEventProcessor @Inject constructor( class NotifiableEventProcessor @Inject constructor(
@ -24,20 +26,26 @@ class NotifiableEventProcessor @Inject constructor(
private val autoAcceptInvites: AutoAcceptInvites private val autoAcceptInvites: AutoAcceptInvites
) { ) {
fun process(eventList: List<NotifiableEvent>, currentRoomId: String?): Map<String, NotifiableEvent?> { fun process(eventList: List<NotifiableEvent>, currentRoomId: String?): List<Pair<Processed, NotifiableEvent>> {
return eventList.associateBy { it.eventId } return eventList.map {
.mapValues { (_, value) -> when (it) {
when (value) { is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) REMOVE else KEEP
is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) null else value is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) || outdatedDetector.isMessageOutdated(it)) {
is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, value.roomId) || outdatedDetector.isMessageOutdated(value)) { REMOVE
null } else KEEP
} else value is SimpleNotifiableEvent -> KEEP
is SimpleNotifiableEvent -> value } to it
} }
}
} }
private fun shouldIgnoreMessageEventInRoom(currentRoomId: String?, roomId: String?): Boolean { private fun shouldIgnoreMessageEventInRoom(currentRoomId: String?, roomId: String?): Boolean {
return currentRoomId != null && roomId == currentRoomId return currentRoomId != null && roomId == currentRoomId
} }
} }
enum class Processed {
KEEP,
REMOVE
}
fun List<Pair<Processed, NotifiableEvent>>.onlyKeptEvents() = filter { it.first == KEEP }.map { it.second }

View file

@ -56,7 +56,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
} }
private val eventList = loadEventInfo() private val eventList = loadEventInfo()
private var renderedEventsList = emptyMap<String, NotifiableEvent?>() private var renderedEventsList = emptyList<Pair<Processed, NotifiableEvent>>()
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size) private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
private var currentRoomId: String? = null private var currentRoomId: String? = null
@ -236,10 +236,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
val eventsToRender = synchronized(eventList) { val eventsToRender = synchronized(eventList) {
notifiableEventProcessor.process(eventList, currentRoomId).also { notifiableEventProcessor.process(eventList, currentRoomId).also {
eventList.clear() eventList.clear()
eventList.addAll(it.values.filterNotNull()) eventList.addAll(it.onlyKeptEvents())
} }
} }
if (renderedEventsList == eventsToRender) { if (renderedEventsList == eventsToRender) {
Timber.d("Skipping notification update due to event list not changing") Timber.d("Skipping notification update due to event list not changing")
} else { } else {

View file

@ -40,14 +40,14 @@ class NotificationFactory @Inject constructor(
private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted
fun Map<String, InviteNotifiableEvent?>.toNotifications(myUserId: String): List<OneShotNotification> { fun List<Pair<Processed, InviteNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
return map { (roomId, event) -> return map { (processed, event) ->
when (event) { when (processed) {
null -> OneShotNotification.Removed(key = roomId) Processed.REMOVE -> OneShotNotification.Removed(key = event.roomId)
else -> OneShotNotification.Append( Processed.KEEP -> OneShotNotification.Append(
notificationUtils.buildRoomInvitationNotification(event, myUserId), notificationUtils.buildRoomInvitationNotification(event, myUserId),
OneShotNotification.Append.Meta( OneShotNotification.Append.Meta(
key = roomId, key = event.roomId,
summaryLine = event.description, summaryLine = event.description,
isNoisy = event.noisy, isNoisy = event.noisy,
timestamp = event.timestamp timestamp = event.timestamp
@ -58,14 +58,14 @@ class NotificationFactory @Inject constructor(
} }
@JvmName("toNotificationsSimpleNotifiableEvent") @JvmName("toNotificationsSimpleNotifiableEvent")
fun Map<String, SimpleNotifiableEvent?>.toNotifications(myUserId: String): List<OneShotNotification> { fun List<Pair<Processed, SimpleNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
return map { (eventId, event) -> return map { (processed, event) ->
when (event) { when (processed) {
null -> OneShotNotification.Removed(key = eventId) Processed.REMOVE -> OneShotNotification.Removed(key = event.eventId)
else -> OneShotNotification.Append( Processed.KEEP -> OneShotNotification.Append(
notificationUtils.buildSimpleEventNotification(event, myUserId), notificationUtils.buildSimpleEventNotification(event, myUserId),
OneShotNotification.Append.Meta( OneShotNotification.Append.Meta(
key = eventId, key = event.eventId,
summaryLine = event.description, summaryLine = event.description,
isNoisy = event.noisy, isNoisy = event.noisy,
timestamp = event.timestamp timestamp = event.timestamp

View file

@ -34,7 +34,7 @@ class NotificationRenderer @Inject constructor(private val notificationDisplayer
myUserDisplayName: String, myUserDisplayName: String,
myUserAvatarUrl: String?, myUserAvatarUrl: String?,
useCompleteNotificationFormat: Boolean, useCompleteNotificationFormat: Boolean,
eventsToProcess: Map<String, NotifiableEvent?>) { eventsToProcess: List<Pair<Processed, NotifiableEvent>>) {
val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType() val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType()
with(notificationFactory) { with(notificationFactory) {
val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl)
@ -108,25 +108,28 @@ class NotificationRenderer @Inject constructor(private val notificationDisplayer
} }
} }
private fun Map<String, NotifiableEvent?>.groupByType(): GroupedNotificationEvents { private fun List<Pair<Processed, NotifiableEvent>>.groupByType(): GroupedNotificationEvents {
val roomIdToEventMap: MutableMap<String, MutableList<NotifiableMessageEvent>> = LinkedHashMap() val roomIdToEventMap: MutableMap<String, MutableList<NotifiableMessageEvent>> = LinkedHashMap()
val simpleEvents: MutableMap<String, SimpleNotifiableEvent?> = LinkedHashMap() val simpleEvents: MutableList<Pair<Processed, SimpleNotifiableEvent>> = ArrayList()
val invitationEvents: MutableMap<String, InviteNotifiableEvent?> = LinkedHashMap() val invitationEvents: MutableList<Pair<Processed, InviteNotifiableEvent>> = ArrayList()
forEach { (_, value) -> forEach {
when (value) { when (val event = it.second) {
is InviteNotifiableEvent -> invitationEvents[value.roomId] is InviteNotifiableEvent -> invitationEvents.add(it.asPair())
is NotifiableMessageEvent -> { is NotifiableMessageEvent -> {
val roomEvents = roomIdToEventMap.getOrPut(value.roomId) { ArrayList() } val roomEvents = roomIdToEventMap.getOrPut(event.roomId) { ArrayList() }
roomEvents.add(value) roomEvents.add(event)
} }
is SimpleNotifiableEvent -> simpleEvents[value.eventId] = value is SimpleNotifiableEvent -> simpleEvents.add(it.asPair())
} }
} }
return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents) return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents)
} }
@Suppress("UNCHECKED_CAST")
private fun <T: NotifiableEvent> Pair<Processed, *>.asPair(): Pair<Processed, T> = this as Pair<Processed, T>
data class GroupedNotificationEvents( data class GroupedNotificationEvents(
val roomEvents: Map<String, List<NotifiableMessageEvent>>, val roomEvents: Map<String, List<NotifiableMessageEvent>>,
val simpleEvents: Map<String, SimpleNotifiableEvent?>, val simpleEvents: List<Pair<Processed, SimpleNotifiableEvent>>,
val invitationEvents: Map<String, InviteNotifiableEvent?> val invitationEvents: List<Pair<Processed, InviteNotifiableEvent>>
) )

View file

@ -37,7 +37,7 @@ class NotifiableEventProcessorTest {
aSimpleNotifiableEvent(eventId = "event-2") aSimpleNotifiableEvent(eventId = "event-2")
) )
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM) val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
simpleEvents = mapOf( simpleEvents = mapOf(
@ -56,7 +56,7 @@ class NotifiableEventProcessorTest {
anInviteNotifiableEvent(roomId = "room-2") anInviteNotifiableEvent(roomId = "room-2")
) )
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM) val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
invitationEvents = mapOf( invitationEvents = mapOf(
@ -75,7 +75,7 @@ class NotifiableEventProcessorTest {
anInviteNotifiableEvent(roomId = "room-2") anInviteNotifiableEvent(roomId = "room-2")
) )
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM) val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
invitationEvents = mapOf( invitationEvents = mapOf(
@ -91,7 +91,7 @@ class NotifiableEventProcessorTest {
val (events) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) val (events) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1"))
outdatedDetector.givenEventIsOutOfDate(events[0]) outdatedDetector.givenEventIsOutOfDate(events[0])
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM) val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
roomEvents = mapOf( roomEvents = mapOf(
@ -106,7 +106,7 @@ class NotifiableEventProcessorTest {
val (events, originalEvents) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) val (events, originalEvents) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1"))
outdatedDetector.givenEventIsInDate(events[0]) outdatedDetector.givenEventIsInDate(events[0])
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM) val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
roomEvents = mapOf( roomEvents = mapOf(
@ -120,7 +120,7 @@ class NotifiableEventProcessorTest {
fun `given viewing the same room as message event when processing then removes message`() { fun `given viewing the same room as message event when processing then removes message`() {
val (events) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) val (events) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1"))
val result = eventProcessor.process(events, currentRoomId = "room-1") val result = eventProcessor.process(events, currentRoomId = "room-1", renderedEventsList = renderedEventsList)
result shouldBeEqualTo aProcessedNotificationEvents( result shouldBeEqualTo aProcessedNotificationEvents(
roomEvents = mapOf( roomEvents = mapOf(