diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt index bf9e805fc8..782f70645b 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -17,6 +17,8 @@ package im.vector.app.features.notifications 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 class NotifiableEventProcessor @Inject constructor( @@ -24,20 +26,26 @@ class NotifiableEventProcessor @Inject constructor( private val autoAcceptInvites: AutoAcceptInvites ) { - fun process(eventList: List, currentRoomId: String?): Map { - return eventList.associateBy { it.eventId } - .mapValues { (_, value) -> - when (value) { - is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) null else value - is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, value.roomId) || outdatedDetector.isMessageOutdated(value)) { - null - } else value - is SimpleNotifiableEvent -> value - } - } + fun process(eventList: List, currentRoomId: String?): List> { + return eventList.map { + when (it) { + is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) REMOVE else KEEP + is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) || outdatedDetector.isMessageOutdated(it)) { + REMOVE + } else KEEP + is SimpleNotifiableEvent -> KEEP + } to it + } } private fun shouldIgnoreMessageEventInRoom(currentRoomId: String?, roomId: String?): Boolean { return currentRoomId != null && roomId == currentRoomId } } + +enum class Processed { + KEEP, + REMOVE +} + +fun List>.onlyKeptEvents() = filter { it.first == KEEP }.map { it.second } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index 43d9eff185..c73b4b2a9c 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -56,7 +56,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } private val eventList = loadEventInfo() - private var renderedEventsList = emptyMap() + private var renderedEventsList = emptyList>() private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size) private var currentRoomId: String? = null @@ -236,10 +236,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context val eventsToRender = synchronized(eventList) { notifiableEventProcessor.process(eventList, currentRoomId).also { eventList.clear() - eventList.addAll(it.values.filterNotNull()) + eventList.addAll(it.onlyKeptEvents()) } } + + if (renderedEventsList == eventsToRender) { Timber.d("Skipping notification update due to event list not changing") } else { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt index d5de0221f6..88f21a02a6 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -40,14 +40,14 @@ class NotificationFactory @Inject constructor( private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted - fun Map.toNotifications(myUserId: String): List { - return map { (roomId, event) -> - when (event) { - null -> OneShotNotification.Removed(key = roomId) - else -> OneShotNotification.Append( + fun List>.toNotifications(myUserId: String): List { + return map { (processed, event) -> + when (processed) { + Processed.REMOVE -> OneShotNotification.Removed(key = event.roomId) + Processed.KEEP -> OneShotNotification.Append( notificationUtils.buildRoomInvitationNotification(event, myUserId), OneShotNotification.Append.Meta( - key = roomId, + key = event.roomId, summaryLine = event.description, isNoisy = event.noisy, timestamp = event.timestamp @@ -58,14 +58,14 @@ class NotificationFactory @Inject constructor( } @JvmName("toNotificationsSimpleNotifiableEvent") - fun Map.toNotifications(myUserId: String): List { - return map { (eventId, event) -> - when (event) { - null -> OneShotNotification.Removed(key = eventId) - else -> OneShotNotification.Append( + fun List>.toNotifications(myUserId: String): List { + return map { (processed, event) -> + when (processed) { + Processed.REMOVE -> OneShotNotification.Removed(key = event.eventId) + Processed.KEEP -> OneShotNotification.Append( notificationUtils.buildSimpleEventNotification(event, myUserId), OneShotNotification.Append.Meta( - key = eventId, + key = event.eventId, summaryLine = event.description, isNoisy = event.noisy, timestamp = event.timestamp diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt index 80391b1e06..31a99810e0 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -34,7 +34,7 @@ class NotificationRenderer @Inject constructor(private val notificationDisplayer myUserDisplayName: String, myUserAvatarUrl: String?, useCompleteNotificationFormat: Boolean, - eventsToProcess: Map) { + eventsToProcess: List>) { val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType() with(notificationFactory) { val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) @@ -108,25 +108,28 @@ class NotificationRenderer @Inject constructor(private val notificationDisplayer } } -private fun Map.groupByType(): GroupedNotificationEvents { +private fun List>.groupByType(): GroupedNotificationEvents { val roomIdToEventMap: MutableMap> = LinkedHashMap() - val simpleEvents: MutableMap = LinkedHashMap() - val invitationEvents: MutableMap = LinkedHashMap() - forEach { (_, value) -> - when (value) { - is InviteNotifiableEvent -> invitationEvents[value.roomId] + val simpleEvents: MutableList> = ArrayList() + val invitationEvents: MutableList> = ArrayList() + forEach { + when (val event = it.second) { + is InviteNotifiableEvent -> invitationEvents.add(it.asPair()) is NotifiableMessageEvent -> { - val roomEvents = roomIdToEventMap.getOrPut(value.roomId) { ArrayList() } - roomEvents.add(value) + val roomEvents = roomIdToEventMap.getOrPut(event.roomId) { ArrayList() } + roomEvents.add(event) } - is SimpleNotifiableEvent -> simpleEvents[value.eventId] = value + is SimpleNotifiableEvent -> simpleEvents.add(it.asPair()) } } return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents) } +@Suppress("UNCHECKED_CAST") +private fun Pair.asPair(): Pair = this as Pair + data class GroupedNotificationEvents( val roomEvents: Map>, - val simpleEvents: Map, - val invitationEvents: Map + val simpleEvents: List>, + val invitationEvents: List> ) diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt index 3e66f82bc3..154763afae 100644 --- a/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt +++ b/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt @@ -37,7 +37,7 @@ class NotifiableEventProcessorTest { 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( simpleEvents = mapOf( @@ -56,7 +56,7 @@ class NotifiableEventProcessorTest { 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( invitationEvents = mapOf( @@ -75,7 +75,7 @@ class NotifiableEventProcessorTest { 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( invitationEvents = mapOf( @@ -91,7 +91,7 @@ class NotifiableEventProcessorTest { val (events) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) 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( roomEvents = mapOf( @@ -106,7 +106,7 @@ class NotifiableEventProcessorTest { val (events, originalEvents) = createEventsList(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) 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( roomEvents = mapOf( @@ -120,7 +120,7 @@ class NotifiableEventProcessorTest { 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 result = eventProcessor.process(events, currentRoomId = "room-1") + val result = eventProcessor.process(events, currentRoomId = "room-1", renderedEventsList = renderedEventsList) result shouldBeEqualTo aProcessedNotificationEvents( roomEvents = mapOf(