using dedicated ProcessedEvent data class instead of type alias for passing around the process notificatiable events

- also includes @JvmName on all conflicting extensions for consistency
This commit is contained in:
Adam Brown 2021-10-19 11:38:34 +01:00
parent d3234b33d3
commit d1f6db4236
8 changed files with 80 additions and 66 deletions

View File

@ -17,29 +17,32 @@
package im.vector.app.features.notifications
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.notifications.ProcessedType.KEEP
import im.vector.app.features.notifications.ProcessedType.REMOVE
import im.vector.app.features.notifications.ProcessedEvent.Type.KEEP
import im.vector.app.features.notifications.ProcessedEvent.Type.REMOVE
import javax.inject.Inject
private typealias ProcessedEvents = List<ProcessedEvent<NotifiableEvent>>
class NotifiableEventProcessor @Inject constructor(
private val outdatedDetector: OutdatedEventDetector,
private val autoAcceptInvites: AutoAcceptInvites
) {
fun process(eventList: List<NotifiableEvent>, currentRoomId: String?, renderedEventsList: List<ProcessedEvent>): List<ProcessedEvent> {
fun process(eventList: List<NotifiableEvent>, currentRoomId: String?, renderedEventsList: ProcessedEvents): ProcessedEvents {
val processedEventList = eventList.map {
when (it) {
val type = 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
}
ProcessedEvent(type, it)
}
val removedEventsDiff = renderedEventsList.filter { renderedEvent ->
eventList.none { it.eventId == renderedEvent.second.eventId }
}.map { REMOVE to it.second }
eventList.none { it.eventId == renderedEvent.event.eventId }
}.map { ProcessedEvent(REMOVE, it.event) }
return removedEventsDiff + processedEventList
}

View File

@ -70,7 +70,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
* we keep track of them in order to know which events have been removed from the eventList
* allowing us to cancel any notifications previous displayed by now removed events
*/
private var renderedEventsList = emptyList<Pair<ProcessedType, NotifiableEvent>>()
private var renderedEventsList = emptyList<ProcessedEvent<NotifiableEvent>>()
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
private var currentRoomId: String? = null

View File

@ -20,7 +20,7 @@ import android.app.Notification
import androidx.core.content.pm.ShortcutInfoCompat
import javax.inject.Inject
private typealias ProcessedMessageEvent = Pair<ProcessedType, NotifiableMessageEvent>
private typealias ProcessedMessageEvents = List<ProcessedEvent<NotifiableMessageEvent>>
class NotificationFactory @Inject constructor(
private val notificationUtils: NotificationUtils,
@ -28,29 +28,30 @@ class NotificationFactory @Inject constructor(
private val summaryGroupMessageCreator: SummaryGroupMessageCreator
) {
fun Map<String, List<ProcessedMessageEvent>>.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List<RoomNotification> {
fun Map<String, ProcessedMessageEvents>.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List<RoomNotification> {
return map { (roomId, events) ->
when {
events.hasNoEventsToDisplay() -> RoomNotification.Removed(roomId)
else -> {
val messageEvents = events.filter { it.first == ProcessedType.KEEP }.map { it.second }
val messageEvents = events.onlyKeptEvents()
roomGroupMessageCreator.createRoomMessage(messageEvents, roomId, myUserDisplayName, myUserAvatarUrl)
}
}
}
}
private fun List<Pair<ProcessedType, NotifiableMessageEvent>>.hasNoEventsToDisplay() = isEmpty() || all {
it.first == ProcessedType.REMOVE || it.second.canNotBeDisplayed()
private fun ProcessedMessageEvents.hasNoEventsToDisplay() = isEmpty() || all {
it.type == ProcessedEvent.Type.REMOVE || it.event.canNotBeDisplayed()
}
private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted
fun List<Pair<ProcessedType, InviteNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
@JvmName("toNotificationsInviteNotifiableEvent")
fun List<ProcessedEvent<InviteNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
return map { (processed, event) ->
when (processed) {
ProcessedType.REMOVE -> OneShotNotification.Removed(key = event.roomId)
ProcessedType.KEEP -> OneShotNotification.Append(
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.roomId)
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
notificationUtils.buildRoomInvitationNotification(event, myUserId),
OneShotNotification.Append.Meta(
key = event.roomId,
@ -64,11 +65,11 @@ class NotificationFactory @Inject constructor(
}
@JvmName("toNotificationsSimpleNotifiableEvent")
fun List<Pair<ProcessedType, SimpleNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
fun List<ProcessedEvent<SimpleNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
return map { (processed, event) ->
when (processed) {
ProcessedType.REMOVE -> OneShotNotification.Removed(key = event.eventId)
ProcessedType.KEEP -> OneShotNotification.Append(
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId)
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
notificationUtils.buildSimpleEventNotification(event, myUserId),
OneShotNotification.Append.Meta(
key = event.eventId,

View File

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

View File

@ -16,11 +16,15 @@
package im.vector.app.features.notifications
typealias ProcessedEvent = Pair<ProcessedType, NotifiableEvent>
data class ProcessedEvent<T>(
val type: Type,
val event: T
) {
enum class ProcessedType {
KEEP,
REMOVE
enum class Type {
KEEP,
REMOVE
}
}
fun List<ProcessedEvent>.onlyKeptEvents() = filter { it.first == ProcessedType.KEEP }.map { it.second }
fun <T> List<ProcessedEvent<T>>.onlyKeptEvents() = filter { it.type == ProcessedEvent.Type.KEEP }.map { it.event }

View File

@ -16,6 +16,7 @@
package im.vector.app.features.notifications
import im.vector.app.features.notifications.ProcessedEvent.Type
import im.vector.app.test.fakes.FakeAutoAcceptInvites
import im.vector.app.test.fakes.FakeOutdatedEventDetector
import org.amshove.kluent.shouldBeEqualTo
@ -39,9 +40,9 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.KEEP to events[0],
ProcessedType.KEEP to events[1]
result shouldBeEqualTo listOfProcessedEvents(
Type.KEEP to events[0],
Type.KEEP to events[1]
)
}
@ -55,9 +56,9 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.REMOVE to events[0],
ProcessedType.REMOVE to events[1]
result shouldBeEqualTo listOfProcessedEvents(
Type.REMOVE to events[0],
Type.REMOVE to events[1]
)
}
@ -71,9 +72,9 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.KEEP to events[0],
ProcessedType.KEEP to events[1]
result shouldBeEqualTo listOfProcessedEvents(
Type.KEEP to events[0],
Type.KEEP to events[1]
)
}
@ -84,8 +85,8 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.REMOVE to events[0],
result shouldBeEqualTo listOfProcessedEvents(
Type.REMOVE to events[0],
)
}
@ -96,8 +97,8 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.KEEP to events[0],
result shouldBeEqualTo listOfProcessedEvents(
Type.KEEP to events[0],
)
}
@ -107,26 +108,30 @@ class NotifiableEventProcessorTest {
val result = eventProcessor.process(events, currentRoomId = "room-1", renderedEventsList = emptyList())
result shouldBeEqualTo listOf(
ProcessedType.REMOVE to events[0],
result shouldBeEqualTo listOfProcessedEvents(
Type.REMOVE to events[0],
)
}
@Test
fun `given events are different to rendered events when processing then removes difference`() {
val events = listOf(aSimpleNotifiableEvent(eventId = "event-1"))
val renderedEvents = listOf(
ProcessedType.KEEP to events[0],
ProcessedType.KEEP to anInviteNotifiableEvent(roomId = "event-2")
val renderedEvents = listOf<ProcessedEvent<NotifiableEvent>>(
ProcessedEvent(Type.KEEP, events[0]),
ProcessedEvent(Type.KEEP, anInviteNotifiableEvent(roomId = "event-2"))
)
val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEventsList = renderedEvents)
result shouldBeEqualTo listOf(
ProcessedType.REMOVE to renderedEvents[1].second,
ProcessedType.KEEP to renderedEvents[0].second
result shouldBeEqualTo listOfProcessedEvents(
Type.REMOVE to renderedEvents[1].event,
Type.KEEP to renderedEvents[0].event
)
}
private fun listOfProcessedEvents(vararg event: Pair<Type, NotifiableEvent>) = event.map {
ProcessedEvent(it.first, it.second)
}
}
fun aSimpleNotifiableEvent(eventId: String) = SimpleNotifiableEvent(

View File

@ -16,6 +16,7 @@
package im.vector.app.features.notifications
import im.vector.app.features.notifications.ProcessedEvent.Type
import im.vector.app.test.fakes.FakeNotificationUtils
import im.vector.app.test.fakes.FakeRoomGroupMessageCreator
import im.vector.app.test.fakes.FakeSummaryGroupMessageCreator
@ -46,7 +47,7 @@ class NotificationFactoryTest {
@Test
fun `given a room invitation when mapping to notification then is Append`() = testWith(notificationFactory) {
val expectedNotification = notificationUtils.givenBuildRoomInvitationNotificationFor(AN_INVITATION_EVENT, MY_USER_ID)
val roomInvitation = listOf(ProcessedType.KEEP to AN_INVITATION_EVENT)
val roomInvitation = listOf(ProcessedEvent(Type.KEEP, AN_INVITATION_EVENT))
val result = roomInvitation.toNotifications(MY_USER_ID)
@ -63,7 +64,7 @@ class NotificationFactoryTest {
@Test
fun `given a missing event in room invitation when mapping to notification then is Removed`() = testWith(notificationFactory) {
val missingEventRoomInvitation = listOf(ProcessedType.REMOVE to AN_INVITATION_EVENT)
val missingEventRoomInvitation = listOf(ProcessedEvent(Type.REMOVE, AN_INVITATION_EVENT))
val result = missingEventRoomInvitation.toNotifications(MY_USER_ID)
@ -75,7 +76,7 @@ class NotificationFactoryTest {
@Test
fun `given a simple event when mapping to notification then is Append`() = testWith(notificationFactory) {
val expectedNotification = notificationUtils.givenBuildSimpleInvitationNotificationFor(A_SIMPLE_EVENT, MY_USER_ID)
val roomInvitation = listOf(ProcessedType.KEEP to A_SIMPLE_EVENT)
val roomInvitation = listOf(ProcessedEvent(Type.KEEP, A_SIMPLE_EVENT))
val result = roomInvitation.toNotifications(MY_USER_ID)
@ -92,7 +93,7 @@ class NotificationFactoryTest {
@Test
fun `given a missing simple event when mapping to notification then is Removed`() = testWith(notificationFactory) {
val missingEventRoomInvitation = listOf(ProcessedType.REMOVE to A_SIMPLE_EVENT)
val missingEventRoomInvitation = listOf(ProcessedEvent(Type.REMOVE, A_SIMPLE_EVENT))
val result = missingEventRoomInvitation.toNotifications(MY_USER_ID)
@ -105,7 +106,7 @@ class NotificationFactoryTest {
fun `given room with message when mapping to notification then delegates to room group message creator`() = testWith(notificationFactory) {
val events = listOf(A_MESSAGE_EVENT)
val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor(events, A_ROOM_ID, MY_USER_ID, MY_AVATAR_URL)
val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedType.KEEP to A_MESSAGE_EVENT))
val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT)))
val result = roomWithMessage.toNotifications(MY_USER_ID, MY_AVATAR_URL)
@ -114,7 +115,7 @@ class NotificationFactoryTest {
@Test
fun `given a room with no events to display when mapping to notification then is Empty`() = testWith(notificationFactory) {
val events = listOf(ProcessedType.REMOVE to A_MESSAGE_EVENT)
val events = listOf(ProcessedEvent(Type.REMOVE, A_MESSAGE_EVENT))
val emptyRoom = mapOf(A_ROOM_ID to events)
val result = emptyRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)
@ -126,7 +127,7 @@ class NotificationFactoryTest {
@Test
fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationFactory) {
val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedType.KEEP to A_MESSAGE_EVENT.copy(isRedacted = true)))
val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true))))
val result = redactedRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL)

View File

@ -30,7 +30,7 @@ private const val AN_EVENT_ID = "event-id"
private const val A_ROOM_ID = "room-id"
private const val USE_COMPLETE_NOTIFICATION_FORMAT = true
private val AN_EVENT_LIST = listOf<Pair<ProcessedType, NotifiableEvent>>()
private val AN_EVENT_LIST = listOf<ProcessedEvent<NotifiableEvent>>()
private val A_PROCESSED_EVENTS = GroupedNotificationEvents(emptyMap(), emptyList(), emptyList())
private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(mockk())
private val A_REMOVE_SUMMARY_NOTIFICATION = SummaryNotification.Removed