handling creating the summary when notification events are filtered to empty due to only containing removals

This commit is contained in:
Adam Brown 2021-10-07 16:03:52 +01:00
parent 3d567d0dcd
commit 0d316e69de
6 changed files with 94 additions and 65 deletions

View file

@ -46,7 +46,12 @@ class NotificationFactory @Inject constructor(
null -> OneShotNotification.Removed(key = roomId) null -> OneShotNotification.Removed(key = roomId)
else -> OneShotNotification.Append( else -> OneShotNotification.Append(
notificationUtils.buildRoomInvitationNotification(event, myUserId), 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) null -> OneShotNotification.Removed(key = eventId)
else -> OneShotNotification.Append( else -> OneShotNotification.Append(
notificationUtils.buildSimpleEventNotification(event, myUserId), 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<RoomNotification>, fun createSummaryNotification(roomNotifications: List<RoomNotification>,
invitationNotifications: List<OneShotNotification>, invitationNotifications: List<OneShotNotification>,
simpleNotifications: List<OneShotNotification>, simpleNotifications: List<OneShotNotification>,
useCompleteNotificationFormat: Boolean): Notification { useCompleteNotificationFormat: Boolean): SummaryNotification {
return summaryGroupMessageCreator.createSummaryNotification( val roomMeta = roomNotifications.mapToMeta()
roomNotifications = roomNotifications.mapToMeta(), val invitationMeta = invitationNotifications.mapToMeta()
invitationNotifications = invitationNotifications.mapToMeta(), val simpleMeta = simpleNotifications.mapToMeta()
simpleNotifications = simpleNotifications.mapToMeta(), return when {
useCompleteNotificationFormat = useCompleteNotificationFormat 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( data class Meta(
val key: String, val key: String,
val summaryLine: CharSequence, 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
}

View file

@ -16,12 +16,8 @@
package im.vector.app.features.notifications package im.vector.app.features.notifications
import android.content.Context import android.content.Context
import android.os.Build
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -41,7 +37,7 @@ class NotificationRenderer @Inject constructor(private val notifiableEventProces
myUserAvatarUrl: String?, myUserAvatarUrl: String?,
useCompleteNotificationFormat: Boolean, useCompleteNotificationFormat: Boolean,
eventList: MutableList<NotifiableEvent>) { eventList: MutableList<NotifiableEvent>) {
Timber.v("refreshNotificationDrawerBg()") Timber.v("Render notification events - count: ${eventList.size}")
val notificationEvents = notifiableEventProcessor.modifyAndProcess(eventList, currentRoomId) val notificationEvents = notifiableEventProcessor.modifyAndProcess(eventList, currentRoomId)
if (lastKnownEventList == notificationEvents.hashCode()) { if (lastKnownEventList == notificationEvents.hashCode()) {
Timber.d("Skipping notification update due to event list not changing") 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 roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl)
val invitationNotifications = invitationEvents.toNotifications(myUserId) val invitationNotifications = invitationEvents.toNotifications(myUserId)
val simpleNotifications = simpleEvents.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()) { roomNotifications.forEach { wrapper ->
notificationDisplayer.cancelNotificationMessage(null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) when (wrapper) {
} else { is RoomNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID)
val summaryNotification = createSummaryNotification( is RoomNotification.Message -> if (useCompleteNotificationFormat) {
roomNotifications = roomNotifications, Timber.d("Updating room messages notification ${wrapper.meta.roomId}")
invitationNotifications = invitationNotifications, wrapper.shortcutInfo?.let {
simpleNotifications = simpleNotifications, ShortcutManagerCompat.pushDynamicShortcut(appContext, it)
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)
} }
notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification)
} }
} }
}
invitationNotifications.forEach { wrapper -> invitationNotifications.forEach { wrapper ->
when (wrapper) { when (wrapper) {
is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID)
is OneShotNotification.Append -> if (useCompleteNotificationFormat) { is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
Timber.d("Updating invitation notification ${wrapper.meta.key}") Timber.d("Updating invitation notification ${wrapper.meta.key}")
notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification) notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification)
}
} }
} }
}
simpleNotifications.forEach { wrapper -> simpleNotifications.forEach { wrapper ->
when (wrapper) { when (wrapper) {
is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) is OneShotNotification.Removed -> notificationDisplayer.cancelNotificationMessage(wrapper.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID)
is OneShotNotification.Append -> if (useCompleteNotificationFormat) { is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
Timber.d("Updating simple notification ${wrapper.meta.key}") Timber.d("Updating simple notification ${wrapper.meta.key}")
notificationDisplayer.showNotificationMessage(wrapper.meta.key, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, wrapper.notification) 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)
}
} }
} }
} }

View file

@ -57,7 +57,9 @@ class SummaryGroupMessageCreator @Inject constructor(
val messageCount = roomNotifications.fold(initial = 0) { acc, current -> acc + current.messageCount } 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 // FIXME roomIdToEventMap.size is not correct, this is the number of rooms
val nbEvents = roomNotifications.size + simpleNotifications.size val nbEvents = roomNotifications.size + simpleNotifications.size
@ -71,12 +73,12 @@ class SummaryGroupMessageCreator @Inject constructor(
summaryInboxStyle, summaryInboxStyle,
sumTitle, sumTitle,
noisy = summaryIsNoisy, noisy = summaryIsNoisy,
lastMessageTimestamp = lastMessageTimestamp1 lastMessageTimestamp = lastMessageTimestamp
) )
} else { } else {
processSimpleGroupSummary(summaryIsNoisy, messageCount, processSimpleGroupSummary(summaryIsNoisy, messageCount,
simpleNotifications.size, invitationNotifications.size, simpleNotifications.size, invitationNotifications.size,
roomNotifications.size, lastMessageTimestamp1) roomNotifications.size, lastMessageTimestamp)
} }
} }

View file

@ -55,7 +55,8 @@ class NotificationFactoryTest {
meta = OneShotNotification.Append.Meta( meta = OneShotNotification.Append.Meta(
key = A_ROOM_ID, key = A_ROOM_ID,
summaryLine = AN_INVITATION_EVENT.description, 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( meta = OneShotNotification.Append.Meta(
key = AN_EVENT_ID, key = AN_EVENT_ID,
summaryLine = A_SIMPLE_EVENT.description, summaryLine = A_SIMPLE_EVENT.description,
isNoisy = A_SIMPLE_EVENT.noisy isNoisy = A_SIMPLE_EVENT.noisy,
timestamp = AN_INVITATION_EVENT.timestamp
)) ))
) )
} }

View file

@ -34,12 +34,13 @@ private const val USE_COMPLETE_NOTIFICATION_FORMAT = true
private val AN_EVENT_LIST = mutableListOf<NotifiableEvent>() private val AN_EVENT_LIST = mutableListOf<NotifiableEvent>()
private val A_PROCESSED_EVENTS = ProcessedNotificationEvents(emptyMap(), emptyMap(), emptyMap()) private val A_PROCESSED_EVENTS = ProcessedNotificationEvents(emptyMap(), emptyMap(), emptyMap())
private val A_SUMMARY_NOTIFICATION = mockk<Notification>() private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(mockk())
private val A_REMOVE_SUMMARY_NOTIFICATION = SummaryNotification.Removed
private val A_NOTIFICATION = mockk<Notification>() private val A_NOTIFICATION = mockk<Notification>()
private val MESSAGE_META = RoomNotification.Message.Meta( private val MESSAGE_META = RoomNotification.Message.Meta(
summaryLine = "ignored", messageCount = 1, latestTimestamp = -1, roomId = A_ROOM_ID, shouldBing = false 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 { class NotificationRendererTest {
@ -71,7 +72,7 @@ class NotificationRendererTest {
notificationDisplayer.verifyInOrder { notificationDisplayer.verifyInOrder {
cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) 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 { notificationDisplayer.verifyInOrder {
showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, A_NOTIFICATION) 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 { notificationDisplayer.verifyInOrder {
cancelNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) 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 { notificationDisplayer.verifyInOrder {
showNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) 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 { notificationDisplayer.verifyInOrder {
cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) 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 { notificationDisplayer.verifyInOrder {
showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) 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() { 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<RoomNotification> = emptyList(), private fun givenNotifications(roomNotifications: List<RoomNotification> = emptyList(),
invitationNotifications: List<OneShotNotification> = emptyList(), invitationNotifications: List<OneShotNotification> = emptyList(),
simpleNotifications: List<OneShotNotification> = emptyList(), simpleNotifications: List<OneShotNotification> = emptyList(),
useCompleteNotificationFormat: Boolean = USE_COMPLETE_NOTIFICATION_FORMAT, 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) notifiableEventProcessor.givenProcessedEventsFor(AN_EVENT_LIST, A_CURRENT_ROOM_ID, A_PROCESSED_EVENTS)
notificationFactory.givenNotificationsFor( notificationFactory.givenNotificationsFor(
processedEvents = A_PROCESSED_EVENTS, processedEvents = A_PROCESSED_EVENTS,

View file

@ -16,11 +16,11 @@
package im.vector.app.test.fakes package im.vector.app.test.fakes
import android.app.Notification
import im.vector.app.features.notifications.NotificationFactory import im.vector.app.features.notifications.NotificationFactory
import im.vector.app.features.notifications.OneShotNotification import im.vector.app.features.notifications.OneShotNotification
import im.vector.app.features.notifications.ProcessedNotificationEvents import im.vector.app.features.notifications.ProcessedNotificationEvents
import im.vector.app.features.notifications.RoomNotification import im.vector.app.features.notifications.RoomNotification
import im.vector.app.features.notifications.SummaryNotification
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
@ -36,7 +36,7 @@ class FakeNotificationFactory {
roomNotifications: List<RoomNotification>, roomNotifications: List<RoomNotification>,
invitationNotifications: List<OneShotNotification>, invitationNotifications: List<OneShotNotification>,
simpleNotifications: List<OneShotNotification>, simpleNotifications: List<OneShotNotification>,
summaryNotification: Notification) { summaryNotification: SummaryNotification) {
with(instance) { with(instance) {
every { processedEvents.roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) } returns roomNotifications every { processedEvents.roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) } returns roomNotifications
every { processedEvents.invitationEvents.toNotifications(myUserId) } returns invitationNotifications every { processedEvents.invitationEvents.toNotifications(myUserId) } returns invitationNotifications