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 f47e82e845..8189977ba8 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 @@ -46,7 +46,12 @@ class NotificationFactory @Inject constructor( null -> OneShotNotification.Removed(key = roomId) else -> OneShotNotification.Append( notificationUtils.buildRoomInvitationNotification(event, myUserId), - OneShotNotification.Append.Meta(key = roomId, summaryLine = event.description, isNoisy = event.noisy) + OneShotNotification.Append.Meta( + key = roomId, + summaryLine = event.description, + isNoisy = event.noisy, + timestamp = event.timestamp + ) ) } } @@ -59,7 +64,12 @@ class NotificationFactory @Inject constructor( null -> OneShotNotification.Removed(key = eventId) else -> OneShotNotification.Append( notificationUtils.buildSimpleEventNotification(event, myUserId), - OneShotNotification.Append.Meta(key = eventId, summaryLine = event.description, isNoisy = event.noisy) + OneShotNotification.Append.Meta( + key = eventId, + summaryLine = event.description, + isNoisy = event.noisy, + timestamp = event.timestamp + ) ) } } @@ -68,13 +78,19 @@ class NotificationFactory @Inject constructor( fun createSummaryNotification(roomNotifications: List, invitationNotifications: List, simpleNotifications: List, - useCompleteNotificationFormat: Boolean): Notification { - return summaryGroupMessageCreator.createSummaryNotification( - roomNotifications = roomNotifications.mapToMeta(), - invitationNotifications = invitationNotifications.mapToMeta(), - simpleNotifications = simpleNotifications.mapToMeta(), - useCompleteNotificationFormat = useCompleteNotificationFormat - ) + useCompleteNotificationFormat: Boolean): SummaryNotification { + val roomMeta = roomNotifications.mapToMeta() + val invitationMeta = invitationNotifications.mapToMeta() + val simpleMeta = simpleNotifications.mapToMeta() + return when { + roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed + else -> SummaryNotification.Update(summaryGroupMessageCreator.createSummaryNotification( + roomNotifications = roomMeta, + invitationNotifications = invitationMeta, + simpleNotifications = simpleMeta, + useCompleteNotificationFormat = useCompleteNotificationFormat + )) + } } } @@ -102,7 +118,13 @@ sealed interface OneShotNotification { data class Meta( val key: String, val summaryLine: CharSequence, - val isNoisy: Boolean + val isNoisy: Boolean, + val timestamp: Long, ) } } + +sealed interface SummaryNotification { + object Removed : SummaryNotification + data class Update(val notification: Notification) : SummaryNotification +} 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 005cf04f07..749e3c61b6 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 @@ -16,12 +16,8 @@ package im.vector.app.features.notifications import android.content.Context -import android.os.Build import androidx.annotation.WorkerThread -import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat -import im.vector.app.features.home.room.detail.RoomDetailActivity import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -41,7 +37,7 @@ class NotificationRenderer @Inject constructor(private val notifiableEventProces myUserAvatarUrl: String?, useCompleteNotificationFormat: Boolean, eventList: MutableList) { - Timber.v("refreshNotificationDrawerBg()") + Timber.v("Render notification events - count: ${eventList.size}") val notificationEvents = notifiableEventProcessor.modifyAndProcess(eventList, currentRoomId) if (lastKnownEventList == notificationEvents.hashCode()) { Timber.d("Skipping notification update due to event list not changing") @@ -57,49 +53,55 @@ class NotificationRenderer @Inject constructor(private val notifiableEventProces val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) val invitationNotifications = invitationEvents.toNotifications(myUserId) val simpleNotifications = simpleEvents.toNotifications(myUserId) + val summaryNotification = createSummaryNotification( + roomNotifications = roomNotifications, + invitationNotifications = invitationNotifications, + simpleNotifications = simpleNotifications, + useCompleteNotificationFormat = useCompleteNotificationFormat + ) - if (roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty()) { - notificationDisplayer.cancelNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) - } else { - val summaryNotification = createSummaryNotification( - roomNotifications = roomNotifications, - invitationNotifications = invitationNotifications, - simpleNotifications = simpleNotifications, - useCompleteNotificationFormat = useCompleteNotificationFormat - ) - roomNotifications.forEach { wrapper -> - when (wrapper) { - is RoomNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) - is RoomNotification.Message -> if (useCompleteNotificationFormat) { - Timber.d("Updating room messages notification ${wrapper.meta.roomId}") - wrapper.shortcutInfo?.let { - ShortcutManagerCompat.pushDynamicShortcut(appContext, it) - } - notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification) + roomNotifications.forEach { wrapper -> + when (wrapper) { + is RoomNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) + is RoomNotification.Message -> if (useCompleteNotificationFormat) { + Timber.d("Updating room messages notification ${wrapper.meta.roomId}") + wrapper.shortcutInfo?.let { + ShortcutManagerCompat.pushDynamicShortcut(appContext, it) } + notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification) } } + } - invitationNotifications.forEach { wrapper -> - when (wrapper) { - is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) - is OneShotNotification.Append -> if (useCompleteNotificationFormat) { - Timber.d("Updating invitation notification ${wrapper.meta.key}") - notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification) - } + invitationNotifications.forEach { wrapper -> + when (wrapper) { + is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) + is OneShotNotification.Append -> if (useCompleteNotificationFormat) { + Timber.d("Updating invitation notification ${wrapper.meta.key}") + notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification) } } + } - simpleNotifications.forEach { wrapper -> - when (wrapper) { - is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) - is OneShotNotification.Append -> if (useCompleteNotificationFormat) { - Timber.d("Updating simple notification ${wrapper.meta.key}") - notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, wrapper.notification) - } + simpleNotifications.forEach { wrapper -> + when (wrapper) { + is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) + is OneShotNotification.Append -> if (useCompleteNotificationFormat) { + Timber.d("Updating simple notification ${wrapper.meta.key}") + notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, wrapper.notification) } } - notificationDisplayer.showNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, summaryNotification) + } + + when (summaryNotification) { + SummaryNotification.Removed -> { + Timber.d("Removing summary notification") + notificationDisplayer.cancelNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) + } + is SummaryNotification.Update -> { + Timber.d("Updating summary notification") + notificationDisplayer.showNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, summaryNotification.notification) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt b/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt index dc9ff92aa6..821f24b436 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt @@ -57,7 +57,9 @@ class SummaryGroupMessageCreator @Inject constructor( val messageCount = roomNotifications.fold(initial = 0) { acc, current -> acc + current.messageCount } - val lastMessageTimestamp1 = roomNotifications.last().latestTimestamp + val lastMessageTimestamp = roomNotifications.lastOrNull()?.latestTimestamp + ?: invitationNotifications.lastOrNull()?.timestamp + ?: simpleNotifications.last().timestamp // FIXME roomIdToEventMap.size is not correct, this is the number of rooms val nbEvents = roomNotifications.size + simpleNotifications.size @@ -71,12 +73,12 @@ class SummaryGroupMessageCreator @Inject constructor( summaryInboxStyle, sumTitle, noisy = summaryIsNoisy, - lastMessageTimestamp = lastMessageTimestamp1 + lastMessageTimestamp = lastMessageTimestamp ) } else { processSimpleGroupSummary(summaryIsNoisy, messageCount, simpleNotifications.size, invitationNotifications.size, - roomNotifications.size, lastMessageTimestamp1) + roomNotifications.size, lastMessageTimestamp) } } diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt index f8e6813d9b..fc20f09811 100644 --- a/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt +++ b/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt @@ -55,7 +55,8 @@ class NotificationFactoryTest { meta = OneShotNotification.Append.Meta( key = A_ROOM_ID, summaryLine = AN_INVITATION_EVENT.description, - isNoisy = AN_INVITATION_EVENT.noisy + isNoisy = AN_INVITATION_EVENT.noisy, + timestamp = AN_INVITATION_EVENT.timestamp )) ) } @@ -83,7 +84,8 @@ class NotificationFactoryTest { meta = OneShotNotification.Append.Meta( key = AN_EVENT_ID, summaryLine = A_SIMPLE_EVENT.description, - isNoisy = A_SIMPLE_EVENT.noisy + isNoisy = A_SIMPLE_EVENT.noisy, + timestamp = AN_INVITATION_EVENT.timestamp )) ) } diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt index 74a11b6d0d..e3c97ad3cf 100644 --- a/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt +++ b/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt @@ -34,12 +34,13 @@ private const val USE_COMPLETE_NOTIFICATION_FORMAT = true private val AN_EVENT_LIST = mutableListOf() private val A_PROCESSED_EVENTS = ProcessedNotificationEvents(emptyMap(), emptyMap(), emptyMap()) -private val A_SUMMARY_NOTIFICATION = mockk() +private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(mockk()) +private val A_REMOVE_SUMMARY_NOTIFICATION = SummaryNotification.Removed private val A_NOTIFICATION = mockk() private val MESSAGE_META = RoomNotification.Message.Meta( summaryLine = "ignored", messageCount = 1, latestTimestamp = -1, roomId = A_ROOM_ID, shouldBing = false ) -private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", summaryLine = "ignored", isNoisy = false) +private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1) class NotificationRendererTest { @@ -71,7 +72,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -86,7 +87,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, A_NOTIFICATION) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -98,7 +99,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { cancelNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -113,7 +114,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { showNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -125,7 +126,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -140,7 +141,7 @@ class NotificationRendererTest { notificationDisplayer.verifyInOrder { showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) - showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) } } @@ -156,14 +157,14 @@ class NotificationRendererTest { } private fun givenNoNotifications() { - givenNotifications(emptyList(), emptyList(), emptyList(), USE_COMPLETE_NOTIFICATION_FORMAT, A_SUMMARY_NOTIFICATION) + givenNotifications(emptyList(), emptyList(), emptyList(), USE_COMPLETE_NOTIFICATION_FORMAT, A_REMOVE_SUMMARY_NOTIFICATION) } private fun givenNotifications(roomNotifications: List = emptyList(), invitationNotifications: List = emptyList(), simpleNotifications: List = emptyList(), useCompleteNotificationFormat: Boolean = USE_COMPLETE_NOTIFICATION_FORMAT, - summaryNotification: Notification = A_SUMMARY_NOTIFICATION) { + summaryNotification: SummaryNotification = A_SUMMARY_NOTIFICATION) { notifiableEventProcessor.givenProcessedEventsFor(AN_EVENT_LIST, A_CURRENT_ROOM_ID, A_PROCESSED_EVENTS) notificationFactory.givenNotificationsFor( processedEvents = A_PROCESSED_EVENTS, diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt index 921999bd93..da2dbc27da 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt @@ -16,11 +16,11 @@ package im.vector.app.test.fakes -import android.app.Notification import im.vector.app.features.notifications.NotificationFactory import im.vector.app.features.notifications.OneShotNotification import im.vector.app.features.notifications.ProcessedNotificationEvents import im.vector.app.features.notifications.RoomNotification +import im.vector.app.features.notifications.SummaryNotification import io.mockk.every import io.mockk.mockk @@ -36,7 +36,7 @@ class FakeNotificationFactory { roomNotifications: List, invitationNotifications: List, simpleNotifications: List, - summaryNotification: Notification) { + summaryNotification: SummaryNotification) { with(instance) { every { processedEvents.roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) } returns roomNotifications every { processedEvents.invitationEvents.toNotifications(myUserId) } returns invitationNotifications