Merge branch 'develop' into feature/bma/notificationCustomSound
This commit is contained in:
commit
35e60efae2
66 changed files with 894 additions and 590 deletions
|
|
@ -34,6 +34,7 @@ interface ActiveNotificationsProvider {
|
|||
fun getMembershipNotificationForSession(sessionId: SessionId): List<StatusBarNotification>
|
||||
fun getMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification>
|
||||
fun getSummaryNotification(sessionId: SessionId): StatusBarNotification?
|
||||
fun getFallbackNotification(sessionId: SessionId): StatusBarNotification?
|
||||
fun count(sessionId: SessionId): Int
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +77,11 @@ class DefaultActiveNotificationsProvider(
|
|||
return getNotificationsForSession(sessionId).find { it.id == summaryId }
|
||||
}
|
||||
|
||||
override fun getFallbackNotification(sessionId: SessionId): StatusBarNotification? {
|
||||
val fallbackId = NotificationIdProvider.getFallbackNotificationId(sessionId)
|
||||
return getNotificationsForSession(sessionId).find { it.id == fallbackId }
|
||||
}
|
||||
|
||||
override fun count(sessionId: SessionId): Int {
|
||||
return getNotificationsForSession(sessionId).size
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,20 @@ import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
|
|||
import io.element.android.libraries.push.api.notifications.NotificationCleaner
|
||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreEventInRoom
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.NavigationState
|
||||
import io.element.android.services.appnavstate.api.currentRoomId
|
||||
import io.element.android.services.appnavstate.api.currentSessionId
|
||||
import io.element.android.services.appnavstate.api.currentThreadId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -46,46 +55,49 @@ class DefaultNotificationDrawerManager(
|
|||
private val matrixClientProvider: MatrixClientProvider,
|
||||
private val imageLoaderHolder: ImageLoaderHolder,
|
||||
private val activeNotificationsProvider: ActiveNotificationsProvider,
|
||||
sessionObserver: SessionObserver,
|
||||
) : NotificationCleaner {
|
||||
// TODO EAx add a setting per user for this
|
||||
private var useCompleteNotificationFormat = true
|
||||
|
||||
private val sessionListener = object : SessionListener {
|
||||
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
|
||||
// User signed out, clear all notifications related to the session.
|
||||
clearAllEvents(SessionId(userId))
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// Observe application state
|
||||
coroutineScope.launch {
|
||||
appNavigationStateService.appNavigationState
|
||||
.collect { onAppNavigationStateChange(it.navigationState) }
|
||||
}
|
||||
sessionObserver.addListener(sessionListener)
|
||||
}
|
||||
|
||||
private var currentAppNavigationState: NavigationState? = null
|
||||
|
||||
private fun onAppNavigationStateChange(navigationState: NavigationState) {
|
||||
when (navigationState) {
|
||||
NavigationState.Root -> {
|
||||
currentAppNavigationState?.currentSessionId()?.let { sessionId ->
|
||||
// User signed out, clear all notifications related to the session.
|
||||
clearAllEvents(sessionId)
|
||||
}
|
||||
NavigationState.Root -> {}
|
||||
is NavigationState.Session -> {
|
||||
// Cleanup the fallback notification
|
||||
clearFallbackForSession(navigationState.sessionId)
|
||||
}
|
||||
is NavigationState.Session -> {}
|
||||
is NavigationState.Space -> {}
|
||||
is NavigationState.Room -> {
|
||||
// Cleanup notification for current room
|
||||
clearMessagesForRoom(
|
||||
sessionId = navigationState.parentSpace.parentSession.sessionId,
|
||||
sessionId = navigationState.parentSession.sessionId,
|
||||
roomId = navigationState.roomId,
|
||||
)
|
||||
}
|
||||
is NavigationState.Thread -> {
|
||||
clearMessagesForThread(
|
||||
sessionId = navigationState.parentRoom.parentSpace.parentSession.sessionId,
|
||||
sessionId = navigationState.parentRoom.parentSession.sessionId,
|
||||
roomId = navigationState.parentRoom.roomId,
|
||||
threadId = navigationState.threadId,
|
||||
)
|
||||
}
|
||||
}
|
||||
currentAppNavigationState = navigationState
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,14 +105,11 @@ class DefaultNotificationDrawerManager(
|
|||
* Events might be grouped and there might not be one notification per event!
|
||||
*/
|
||||
suspend fun onNotifiableEventReceived(notifiableEvent: NotifiableEvent) {
|
||||
if (notifiableEvent.shouldIgnoreEventInRoom(appNavigationStateService.appNavigationState.value)) {
|
||||
return
|
||||
}
|
||||
renderEvents(listOf(notifiableEvent))
|
||||
onNotifiableEventsReceived(listOf(notifiableEvent))
|
||||
}
|
||||
|
||||
suspend fun onNotifiableEventsReceived(notifiableEvents: List<NotifiableEvent>) {
|
||||
val eventsToNotify = notifiableEvents.filter { !it.shouldIgnoreEventInRoom(appNavigationStateService.appNavigationState.value) }
|
||||
val eventsToNotify = notifiableEvents.filter { !appNavigationStateService.appNavigationState.value.shouldIgnoreEvent(it) }
|
||||
renderEvents(eventsToNotify)
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +129,17 @@ class DefaultNotificationDrawerManager(
|
|||
.forEach { notificationDisplayer.cancelNotification(it.tag, it.id) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the fallback notification for the session.
|
||||
*/
|
||||
fun clearFallbackForSession(sessionId: SessionId) {
|
||||
notificationDisplayer.cancelNotification(
|
||||
DefaultNotificationDataFactory.FALLBACK_NOTIFICATION_TAG,
|
||||
NotificationIdProvider.getFallbackNotificationId(sessionId),
|
||||
)
|
||||
clearSummaryNotificationIfNeeded(sessionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called when the application is currently opened and showing timeline for the given [roomId].
|
||||
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
|
||||
|
|
@ -191,3 +211,30 @@ class DefaultNotificationDrawerManager(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if a notifiableEvent should be ignored based on the current application navigation state.
|
||||
*/
|
||||
private fun AppNavigationState.shouldIgnoreEvent(event: NotifiableEvent): Boolean {
|
||||
if (!isInForeground) return false
|
||||
return navigationState.currentSessionId() == event.sessionId &&
|
||||
when (event) {
|
||||
is NotifiableRingingCallEvent -> {
|
||||
// Never ignore ringing call notifications
|
||||
// Note that NotifiableRingingCallEvent are not handled by DefaultNotificationDrawerManager
|
||||
false
|
||||
}
|
||||
is FallbackNotifiableEvent -> {
|
||||
// Ignore if the room list is currently displayed
|
||||
navigationState is NavigationState.Session
|
||||
}
|
||||
is InviteNotifiableEvent,
|
||||
is SimpleNotifiableEvent -> {
|
||||
event.roomId == navigationState.currentRoomId()
|
||||
}
|
||||
is NotifiableMessageEvent -> {
|
||||
event.roomId == navigationState.currentRoomId() &&
|
||||
event.threadId == navigationState.currentThreadId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,12 @@ import dev.zacsweers.metro.Inject
|
|||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
|
||||
@Inject
|
||||
class FallbackNotificationFactory(
|
||||
private val clock: SystemClock,
|
||||
private val stringProvider: StringProvider,
|
||||
) {
|
||||
fun create(
|
||||
sessionId: SessionId,
|
||||
|
|
@ -36,7 +33,7 @@ class FallbackNotificationFactory(
|
|||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
timestamp = clock.epochMillis(),
|
||||
description = stringProvider.getString(R.string.notification_fallback_content),
|
||||
description = "",
|
||||
cause = cause,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,24 +9,18 @@
|
|||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import android.app.Notification
|
||||
import android.graphics.Typeface
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import coil3.ImageLoader
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
|
||||
interface NotificationDataFactory {
|
||||
suspend fun toNotifications(
|
||||
|
|
@ -51,16 +45,15 @@ interface NotificationDataFactory {
|
|||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
fun toNotifications(
|
||||
fun toNotification(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): List<OneShotNotification>
|
||||
): OneShotNotification?
|
||||
|
||||
fun createSummaryNotification(
|
||||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): SummaryNotification
|
||||
}
|
||||
|
|
@ -71,7 +64,6 @@ class DefaultNotificationDataFactory(
|
|||
private val roomGroupMessageCreator: RoomGroupMessageCreator,
|
||||
private val summaryGroupMessageCreator: SummaryGroupMessageCreator,
|
||||
private val activeNotificationsProvider: ActiveNotificationsProvider,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationDataFactory {
|
||||
override suspend fun toNotifications(
|
||||
messages: List<NotifiableMessageEvent>,
|
||||
|
|
@ -81,10 +73,7 @@ class DefaultNotificationDataFactory(
|
|||
val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() }
|
||||
.groupBy { it.roomId }
|
||||
return messagesToDisplay.flatMap { (roomId, events) ->
|
||||
val roomName = events.lastOrNull()?.roomName ?: roomId.value
|
||||
val isDm = events.lastOrNull()?.roomIsDm ?: false
|
||||
val eventsByThreadId = events.groupBy { it.threadId }
|
||||
|
||||
eventsByThreadId.map { (threadId, events) ->
|
||||
val notification = roomGroupMessageCreator.createRoomMessage(
|
||||
events = events,
|
||||
|
|
@ -98,7 +87,6 @@ class DefaultNotificationDataFactory(
|
|||
notification = notification,
|
||||
roomId = roomId,
|
||||
threadId = threadId,
|
||||
summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, isDm),
|
||||
messageCount = events.size,
|
||||
latestTimestamp = events.maxOf { it.timestamp },
|
||||
shouldBing = events.any { it.noisy }
|
||||
|
|
@ -123,7 +111,6 @@ class DefaultNotificationDataFactory(
|
|||
OneShotNotification(
|
||||
tag = event.roomId.value,
|
||||
notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event),
|
||||
summaryLine = event.description,
|
||||
isNoisy = event.noisy,
|
||||
timestamp = event.timestamp
|
||||
)
|
||||
|
|
@ -140,7 +127,6 @@ class DefaultNotificationDataFactory(
|
|||
OneShotNotification(
|
||||
tag = event.eventId.value,
|
||||
notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event),
|
||||
summaryLine = event.description,
|
||||
isNoisy = event.noisy,
|
||||
timestamp = event.timestamp
|
||||
)
|
||||
|
|
@ -149,26 +135,31 @@ class DefaultNotificationDataFactory(
|
|||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(
|
||||
override fun toNotification(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): List<OneShotNotification> {
|
||||
return fallback.map { event ->
|
||||
OneShotNotification(
|
||||
tag = event.eventId.value,
|
||||
notification = notificationCreator.createFallbackNotification(notificationAccountParams, event),
|
||||
summaryLine = event.description.orEmpty(),
|
||||
isNoisy = false,
|
||||
timestamp = event.timestamp
|
||||
)
|
||||
}
|
||||
): OneShotNotification? {
|
||||
if (fallback.isEmpty()) return null
|
||||
val existingNotification = activeNotificationsProvider
|
||||
.getFallbackNotification(notificationAccountParams.user.userId)
|
||||
?.notification
|
||||
val notification = notificationCreator.createFallbackNotification(
|
||||
existingNotification = existingNotification,
|
||||
notificationAccountParams = notificationAccountParams,
|
||||
fallbackNotifiableEvents = fallback,
|
||||
)
|
||||
return OneShotNotification(
|
||||
tag = FALLBACK_NOTIFICATION_TAG,
|
||||
notification = notification,
|
||||
isNoisy = false,
|
||||
timestamp = fallback.first().timestamp
|
||||
)
|
||||
}
|
||||
|
||||
override fun createSummaryNotification(
|
||||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): SummaryNotification {
|
||||
return when {
|
||||
|
|
@ -178,51 +169,14 @@ class DefaultNotificationDataFactory(
|
|||
roomNotifications = roomNotifications,
|
||||
invitationNotifications = invitationNotifications,
|
||||
simpleNotifications = simpleNotifications,
|
||||
fallbackNotifications = fallbackNotifications,
|
||||
notificationAccountParams = notificationAccountParams,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRoomMessagesGroupSummaryLine(events: List<NotifiableMessageEvent>, roomName: String, roomIsDm: Boolean): CharSequence {
|
||||
return when (events.size) {
|
||||
1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDm)
|
||||
else -> {
|
||||
stringProvider.getQuantityString(
|
||||
R.plurals.notification_compat_summary_line_for_room,
|
||||
events.size,
|
||||
roomName,
|
||||
events.size
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDm: Boolean): CharSequence {
|
||||
return if (roomIsDm) {
|
||||
buildSpannedString {
|
||||
event.senderDisambiguatedDisplayName?.let {
|
||||
inSpans(StyleSpan(Typeface.BOLD)) {
|
||||
append(it)
|
||||
append(": ")
|
||||
}
|
||||
}
|
||||
append(event.description)
|
||||
}
|
||||
} else {
|
||||
buildSpannedString {
|
||||
inSpans(StyleSpan(Typeface.BOLD)) {
|
||||
append(roomName)
|
||||
append(": ")
|
||||
event.senderDisambiguatedDisplayName?.let {
|
||||
append(it)
|
||||
append(" ")
|
||||
}
|
||||
}
|
||||
append(event.description)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
const val FALLBACK_NOTIFICATION_TAG = "FALLBACK"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +184,6 @@ data class RoomNotification(
|
|||
val notification: Notification,
|
||||
val roomId: RoomId,
|
||||
val threadId: ThreadId?,
|
||||
val summaryLine: CharSequence,
|
||||
val messageCount: Int,
|
||||
val latestTimestamp: Long,
|
||||
val shouldBing: Boolean,
|
||||
|
|
@ -239,7 +192,6 @@ data class RoomNotification(
|
|||
return notification == other.notification &&
|
||||
roomId == other.roomId &&
|
||||
threadId == other.threadId &&
|
||||
summaryLine.toString() == other.summaryLine.toString() &&
|
||||
messageCount == other.messageCount &&
|
||||
latestTimestamp == other.latestTimestamp &&
|
||||
shouldBing == other.shouldBing
|
||||
|
|
@ -249,7 +201,6 @@ data class RoomNotification(
|
|||
data class OneShotNotification(
|
||||
val notification: Notification,
|
||||
val tag: String,
|
||||
val summaryLine: CharSequence,
|
||||
val isNoisy: Boolean,
|
||||
val timestamp: Long,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -55,12 +55,11 @@ class NotificationRenderer(
|
|||
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, imageLoader, notificationAccountParams)
|
||||
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, notificationAccountParams)
|
||||
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, notificationAccountParams)
|
||||
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, notificationAccountParams)
|
||||
val fallbackNotification = notificationDataFactory.toNotification(groupedEvents.fallbackEvents, notificationAccountParams)
|
||||
val summaryNotification = notificationDataFactory.createSummaryNotification(
|
||||
roomNotifications = roomNotifications,
|
||||
invitationNotifications = invitationNotifications,
|
||||
simpleNotifications = simpleNotifications,
|
||||
fallbackNotifications = fallbackNotifications,
|
||||
notificationAccountParams = notificationAccountParams,
|
||||
)
|
||||
|
||||
|
|
@ -107,13 +106,12 @@ class NotificationRenderer(
|
|||
}
|
||||
}
|
||||
|
||||
// Show only the first fallback notification
|
||||
if (fallbackNotifications.isNotEmpty()) {
|
||||
Timber.tag(loggerTag.value).d("Showing fallback notification")
|
||||
if (fallbackNotification != null) {
|
||||
Timber.tag(loggerTag.value).d("Showing or updating fallback notification")
|
||||
notificationDisplayer.showNotification(
|
||||
tag = "FALLBACK",
|
||||
tag = fallbackNotification.tag,
|
||||
id = NotificationIdProvider.getFallbackNotificationId(currentUser.userId),
|
||||
notification = fallbackNotifications.first().notification
|
||||
notification = fallbackNotification.notification,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ interface SummaryGroupMessageCreator {
|
|||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
): Notification
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +44,6 @@ class DefaultSummaryGroupMessageCreator(
|
|||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
): Notification {
|
||||
val summaryIsNoisy = roomNotifications.any { it.shouldBing } ||
|
||||
invitationNotifications.any { it.isNoisy } ||
|
||||
|
|
|
|||
|
|
@ -75,8 +75,9 @@ interface NotificationCreator {
|
|||
): Notification
|
||||
|
||||
fun createFallbackNotification(
|
||||
existingNotification: Notification?,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
fallbackNotifiableEvents: List<FallbackNotifiableEvent>,
|
||||
): Notification
|
||||
|
||||
/**
|
||||
|
|
@ -308,31 +309,38 @@ class DefaultNotificationCreator(
|
|||
}
|
||||
|
||||
override fun createFallbackNotification(
|
||||
existingNotification: Notification?,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
fallbackNotifiableEvents: List<FallbackNotifiableEvent>,
|
||||
): Notification {
|
||||
val channelId = notificationChannels.getChannelIdForMessage(
|
||||
sessionId = fallbackNotifiableEvent.sessionId,
|
||||
noisy = false,
|
||||
)
|
||||
val existingCounter = existingNotification
|
||||
?.extras
|
||||
?.getInt(FALLBACK_COUNTER_EXTRA)
|
||||
?: 0
|
||||
val counter = existingCounter + fallbackNotifiableEvents.size
|
||||
val fallbackNotifiableEvent = fallbackNotifiableEvents.first()
|
||||
return NotificationCompat.Builder(context, channelId)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentTitle(buildMeta.applicationName.annotateForDebug(7))
|
||||
.setContentText(fallbackNotifiableEvent.description.orEmpty().annotateForDebug(8))
|
||||
.setContentText(
|
||||
stringProvider.getQuantityString(R.plurals.notification_fallback_n_content, counter, counter)
|
||||
.annotateForDebug(8)
|
||||
)
|
||||
.setExtras(
|
||||
bundleOf(
|
||||
FALLBACK_COUNTER_EXTRA to counter
|
||||
)
|
||||
)
|
||||
.setNumber(counter)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
.configureWith(notificationAccountParams)
|
||||
.setAutoCancel(true)
|
||||
.setWhen(fallbackNotifiableEvent.timestamp)
|
||||
// Ideally we'd use `createOpenRoomPendingIntent` here, but the broken notification might apply to an invite
|
||||
// and the user won't have access to the room yet, resulting in an error screen.
|
||||
.setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(fallbackNotifiableEvent.sessionId))
|
||||
.setDeleteIntent(
|
||||
pendingIntentFactory.createDismissEventPendingIntent(
|
||||
fallbackNotifiableEvent.sessionId,
|
||||
fallbackNotifiableEvent.roomId,
|
||||
fallbackNotifiableEvent.eventId
|
||||
)
|
||||
)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.build()
|
||||
}
|
||||
|
|
@ -522,6 +530,7 @@ class DefaultNotificationCreator(
|
|||
|
||||
companion object {
|
||||
const val MESSAGE_EVENT_ID = "message_event_id"
|
||||
private const val FALLBACK_COUNTER_EXTRA = "COUNTER"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,6 @@ import io.element.android.libraries.matrix.api.core.SessionId
|
|||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.currentRoomId
|
||||
import io.element.android.services.appnavstate.api.currentSessionId
|
||||
import io.element.android.services.appnavstate.api.currentThreadId
|
||||
|
||||
data class NotifiableMessageEvent(
|
||||
override val sessionId: SessionId,
|
||||
|
|
@ -56,24 +52,3 @@ data class NotifiableMessageEvent(
|
|||
val imageUri: Uri?
|
||||
get() = imageUriString?.toUri()
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if a notification should be ignored based on the current app and navigation state.
|
||||
*/
|
||||
fun NotifiableEvent.shouldIgnoreEventInRoom(appNavigationState: AppNavigationState): Boolean {
|
||||
val currentSessionId = appNavigationState.navigationState.currentSessionId() ?: return false
|
||||
return when (val currentRoomId = appNavigationState.navigationState.currentRoomId()) {
|
||||
null -> false
|
||||
else -> {
|
||||
// Never ignore ringing call notifications
|
||||
if (this is NotifiableRingingCallEvent) {
|
||||
false
|
||||
} else {
|
||||
appNavigationState.isInForeground &&
|
||||
sessionId == currentSessionId &&
|
||||
roomId == currentRoomId &&
|
||||
(this as? NotifiableMessageEvent)?.threadId == appNavigationState.navigationState.currentThreadId()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@
|
|||
</plurals>
|
||||
<string name="notification_error_unified_push_unregistered_android">"The UnifiedPush notification distributor couldn\'t be registered, so you will not receive notifications anymore. Please check the notifications settings of the app and the status of the push distributor."</string>
|
||||
<string name="notification_fallback_content">"You have new messages."</string>
|
||||
<plurals name="notification_fallback_n_content">
|
||||
<item quantity="one">"You have %d new message."</item>
|
||||
<item quantity="other">"You have %d new messages."</item>
|
||||
</plurals>
|
||||
<string name="notification_incoming_call">"📹 Incoming call"</string>
|
||||
<string name="notification_inline_reply_failed">"** Failed to send - please open room"</string>
|
||||
<string name="notification_invitation_action_join">"Join"</string>
|
||||
|
|
|
|||
|
|
@ -153,6 +153,19 @@ class DefaultActiveNotificationsProviderTest {
|
|||
assertThat(activeNotificationsProvider.getSummaryNotification(A_SESSION_ID_2)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFallbackNotification returns only the fallback notification for that session id if it exists`() {
|
||||
val activeNotifications = listOf(
|
||||
aStatusBarNotification(id = notificationIdProvider.getFallbackNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value),
|
||||
aStatusBarNotification(id = notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value),
|
||||
aStatusBarNotification(id = notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID_2), groupId = A_SESSION_ID_2.value),
|
||||
)
|
||||
val activeNotificationsProvider = createActiveNotificationsProvider(activeNotifications = activeNotifications)
|
||||
|
||||
assertThat(activeNotificationsProvider.getFallbackNotification(A_SESSION_ID)).isNotNull()
|
||||
assertThat(activeNotificationsProvider.getFallbackNotification(A_SESSION_ID_2)).isNull()
|
||||
}
|
||||
|
||||
private fun aStatusBarNotification(id: Int, groupId: String, tag: String? = null) = mockk<StatusBarNotification> {
|
||||
every { this@mockk.id } returns id
|
||||
every { this@mockk.tag } returns tag
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableMess
|
|||
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
||||
import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver
|
||||
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -663,7 +662,7 @@ class DefaultNotifiableEventResolverTest {
|
|||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
editedEventId = null,
|
||||
description = "You have new messages.",
|
||||
description = "",
|
||||
canBeReplaced = true,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
|
|
@ -895,7 +894,6 @@ class DefaultNotifiableEventResolverTest {
|
|||
callNotificationEventResolver = callNotificationEventResolver,
|
||||
fallbackNotificationFactory = FallbackNotificationFactory(
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(defaultResult = "You have new messages.")
|
||||
),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ import io.element.android.features.enterprise.api.EnterpriseService
|
|||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SPACE_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
|
|
@ -29,22 +31,26 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeNotificatio
|
|||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.NavigationState
|
||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
||||
import io.element.android.services.appnavstate.test.aNavigationState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.services.appnavstate.test.anAppNavigationState
|
||||
import io.element.android.tests.testutils.lambda.any
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -92,28 +98,25 @@ class DefaultNotificationDrawerManagerTest {
|
|||
@Test
|
||||
fun `react to applicationStateChange`() = runTest {
|
||||
// For now just call all the API. Later, add more valuable tests.
|
||||
val appNavigationStateFlow: MutableStateFlow<AppNavigationState> = MutableStateFlow(
|
||||
AppNavigationState(
|
||||
navigationState = NavigationState.Root,
|
||||
isInForeground = true,
|
||||
)
|
||||
)
|
||||
val appNavigationStateService = FakeAppNavigationStateService(appNavigationState = appNavigationStateFlow)
|
||||
val appNavigationStateService = FakeAppNavigationStateService()
|
||||
createDefaultNotificationDrawerManager(
|
||||
appNavigationStateService = appNavigationStateService
|
||||
)
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true))
|
||||
appNavigationStateService.emitNavigationState(AppNavigationState(aNavigationState(), isInForeground = true))
|
||||
runCurrent()
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID), isInForeground = true))
|
||||
appNavigationStateService.emitNavigationState(AppNavigationState(aNavigationState(A_SESSION_ID), isInForeground = true))
|
||||
runCurrent()
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID), isInForeground = true))
|
||||
appNavigationStateService.emitNavigationState(AppNavigationState(aNavigationState(A_SESSION_ID, A_ROOM_ID), isInForeground = true))
|
||||
runCurrent()
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_ID), isInForeground = true))
|
||||
runCurrent()
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_ID, A_THREAD_ID), isInForeground = true))
|
||||
appNavigationStateService.emitNavigationState(
|
||||
AppNavigationState(
|
||||
aNavigationState(A_SESSION_ID, A_ROOM_ID, A_THREAD_ID),
|
||||
isInForeground = true
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
// Like a user sign out
|
||||
appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true))
|
||||
appNavigationStateService.emitNavigationState(AppNavigationState(aNavigationState(), isInForeground = true))
|
||||
runCurrent()
|
||||
}
|
||||
|
||||
|
|
@ -205,35 +208,325 @@ class DefaultNotificationDrawerManagerTest {
|
|||
)
|
||||
}
|
||||
|
||||
private fun TestScope.createDefaultNotificationDrawerManager(
|
||||
notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(),
|
||||
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(),
|
||||
roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(),
|
||||
summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(),
|
||||
activeNotificationsProvider: FakeActiveNotificationsProvider = FakeActiveNotificationsProvider(),
|
||||
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
||||
): DefaultNotificationDrawerManager {
|
||||
return DefaultNotificationDrawerManager(
|
||||
@Test
|
||||
fun `when a session is signed out, clearAllEvent is invoked`() = runTest {
|
||||
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
|
||||
val notificationDisplayer = FakeNotificationDisplayer(
|
||||
cancelNotificationResult = cancelNotificationResult,
|
||||
)
|
||||
val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID)
|
||||
val activeNotificationsProvider = FakeActiveNotificationsProvider(
|
||||
getNotificationsForSessionResult = {
|
||||
listOf(
|
||||
mockk {
|
||||
every { id } returns summaryId
|
||||
every { tag } returns null
|
||||
},
|
||||
)
|
||||
},
|
||||
countResult = { 1 },
|
||||
)
|
||||
val sessionObserver = FakeSessionObserver()
|
||||
createDefaultNotificationDrawerManager(
|
||||
notificationDisplayer = notificationDisplayer,
|
||||
notificationRenderer = NotificationRenderer(
|
||||
notificationDisplayer = FakeNotificationDisplayer(),
|
||||
notificationDataFactory = DefaultNotificationDataFactory(
|
||||
notificationCreator = FakeNotificationCreator(),
|
||||
roomGroupMessageCreator = roomGroupMessageCreator,
|
||||
summaryGroupMessageCreator = summaryGroupMessageCreator,
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
stringProvider = FakeStringProvider(),
|
||||
),
|
||||
enterpriseService = enterpriseService,
|
||||
sessionStore = sessionStore,
|
||||
),
|
||||
appNavigationStateService = appNavigationStateService,
|
||||
coroutineScope = backgroundScope,
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
imageLoaderHolder = FakeImageLoaderHolder(),
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
sessionObserver = sessionObserver,
|
||||
)
|
||||
// Simulate a session sign out
|
||||
sessionObserver.onSessionDeleted(A_SESSION_ID.value)
|
||||
// Verify we asked to cancel the notification with summaryId
|
||||
cancelNotificationResult.assertions().isCalledExactly(1).withSequence(
|
||||
listOf(value(null), value(summaryId)),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when the application is in background, all events trigger a notification`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID),
|
||||
isInForeground = false,
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aFallbackNotifiableEvent(sessionId = A_SESSION_ID),
|
||||
aFallbackNotifiableEvent(sessionId = A_SESSION_ID_2),
|
||||
anInviteNotifiableEvent(sessionId = A_SESSION_ID),
|
||||
anInviteNotifiableEvent(sessionId = A_SESSION_ID_2),
|
||||
aSimpleNotifiableEvent(sessionId = A_SESSION_ID),
|
||||
aSimpleNotifiableEvent(sessionId = A_SESSION_ID_2),
|
||||
aNotifiableMessageEvent(sessionId = A_SESSION_ID),
|
||||
aNotifiableMessageEvent(sessionId = A_SESSION_ID_2),
|
||||
aNotifiableMessageEvent(sessionId = A_SESSION_ID, threadId = A_THREAD_ID),
|
||||
aNotifiableMessageEvent(sessionId = A_SESSION_ID_2, threadId = A_THREAD_ID_2),
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 2,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `fallback event is ignored when the room list is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID),
|
||||
),
|
||||
notifiableEvents = listOf(aFallbackNotifiableEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `fallback event is not ignored when a room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID),
|
||||
),
|
||||
notifiableEvents = listOf(aFallbackNotifiableEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `fallback event for other session is not ignored when the room list is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(aFallbackNotifiableEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `invite notifiable event is emits a notification when the room list is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID),
|
||||
),
|
||||
notifiableEvents = listOf(anInviteNotifiableEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `invite notifiable event does not emit a notification when the same room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
anInviteNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `invite notifiable event emits a notification when another room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
anInviteNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `simple notifiable event is emits a notification when the room list is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID),
|
||||
),
|
||||
notifiableEvents = listOf(aSimpleNotifiableEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `simple notifiable event does not emit a notification when the same room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aSimpleNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `simple notifiable event emits a notification when another room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aSimpleNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event is emits a notification when the room list is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID),
|
||||
),
|
||||
notifiableEvents = listOf(aNotifiableMessageEvent(sessionId = A_SESSION_ID)),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event does not emit a notification when the same room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event for a thread emits a notification when the same room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
threadId = A_THREAD_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event for a thread does not emit a notification when the same thread is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID, threadId = A_THREAD_ID),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
threadId = A_THREAD_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event for a thread emits a notification when another thread is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID, threadId = A_THREAD_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
threadId = A_THREAD_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event for a thread emits a notification when a thread of another room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID_2, threadId = A_THREAD_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
threadId = A_THREAD_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `notifiable event emits a notification when another room is displayed`() = testOnNotifiableEventReceived(
|
||||
appNavigationState = anAppNavigationState(
|
||||
navigationState = aNavigationState(sessionId = A_SESSION_ID, roomId = A_ROOM_ID_2),
|
||||
),
|
||||
notifiableEvents = listOf(
|
||||
aNotifiableMessageEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
)
|
||||
),
|
||||
shouldEmitNotification = true,
|
||||
extraInvocationsForNotificationSummary = 1,
|
||||
)
|
||||
|
||||
private fun testOnNotifiableEventReceived(
|
||||
appNavigationState: AppNavigationState,
|
||||
notifiableEvents: List<NotifiableEvent>,
|
||||
shouldEmitNotification: Boolean,
|
||||
extraInvocationsForNotificationSummary: Int = 0,
|
||||
) = runTest {
|
||||
val showNotificationResult = lambdaRecorder<String?, Int, Notification, Boolean> { _, _, _ ->
|
||||
true
|
||||
}
|
||||
val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager(
|
||||
appNavigationStateService = FakeAppNavigationStateService(
|
||||
initialAppNavigationState = appNavigationState,
|
||||
),
|
||||
notificationDisplayer = FakeNotificationDisplayer(
|
||||
showNotificationResult = showNotificationResult,
|
||||
)
|
||||
)
|
||||
defaultNotificationDrawerManager.onNotifiableEventsReceived(notifiableEvents)
|
||||
showNotificationResult.assertions().isCalledExactly(
|
||||
if (shouldEmitNotification) {
|
||||
notifiableEvents.size + extraInvocationsForNotificationSummary
|
||||
} else {
|
||||
0
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun TestScope.createDefaultNotificationDrawerManager(
|
||||
notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(),
|
||||
notificationRenderer: NotificationRenderer? = null,
|
||||
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(),
|
||||
roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(),
|
||||
summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(),
|
||||
activeNotificationsProvider: FakeActiveNotificationsProvider = FakeActiveNotificationsProvider(),
|
||||
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
||||
sessionObserver: SessionObserver = FakeSessionObserver(),
|
||||
): DefaultNotificationDrawerManager {
|
||||
return DefaultNotificationDrawerManager(
|
||||
notificationDisplayer = notificationDisplayer,
|
||||
notificationRenderer = notificationRenderer ?: NotificationRenderer(
|
||||
notificationDisplayer = notificationDisplayer,
|
||||
notificationDataFactory = DefaultNotificationDataFactory(
|
||||
notificationCreator = FakeNotificationCreator(),
|
||||
roomGroupMessageCreator = roomGroupMessageCreator,
|
||||
summaryGroupMessageCreator = summaryGroupMessageCreator,
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
),
|
||||
enterpriseService = enterpriseService,
|
||||
sessionStore = sessionStore,
|
||||
),
|
||||
appNavigationStateService = appNavigationStateService,
|
||||
coroutineScope = backgroundScope,
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
imageLoaderHolder = FakeImageLoaderHolder(),
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
sessionObserver = sessionObserver,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,9 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
|
|||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
|
||||
import io.element.android.libraries.matrix.test.notification.aNotificationData
|
||||
import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDataFactory
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver
|
||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
|
|
@ -47,16 +43,10 @@ class DefaultOnMissedCallNotificationHandlerTest {
|
|||
})
|
||||
val defaultOnMissedCallNotificationHandler = DefaultOnMissedCallNotificationHandler(
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
defaultNotificationDrawerManager = DefaultNotificationDrawerManager(
|
||||
notificationDisplayer = FakeNotificationDisplayer(),
|
||||
defaultNotificationDrawerManager = createDefaultNotificationDrawerManager(
|
||||
notificationRenderer = createNotificationRenderer(
|
||||
notificationDataFactory = dataFactory,
|
||||
),
|
||||
appNavigationStateService = FakeAppNavigationStateService(),
|
||||
coroutineScope = backgroundScope,
|
||||
matrixClientProvider = FakeMatrixClientProvider(),
|
||||
imageLoaderHolder = FakeImageLoaderHolder(),
|
||||
activeNotificationsProvider = FakeActiveNotificationsProvider(),
|
||||
),
|
||||
callNotificationEventResolver = FakeCallNotificationEventResolver(resolveEventLambda = { _, _, _ ->
|
||||
Result.success(aNotifiableMessageEvent())
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ class DefaultSummaryGroupMessageCreatorTest {
|
|||
RoomNotification(
|
||||
notification = Notification(),
|
||||
roomId = A_ROOM_ID,
|
||||
summaryLine = "",
|
||||
messageCount = 1,
|
||||
latestTimestamp = A_FAKE_TIMESTAMP + 10,
|
||||
shouldBing = true,
|
||||
|
|
@ -48,7 +47,6 @@ class DefaultSummaryGroupMessageCreatorTest {
|
|||
),
|
||||
invitationNotifications = emptyList(),
|
||||
simpleNotifications = emptyList(),
|
||||
fallbackNotifications = emptyList(),
|
||||
)
|
||||
|
||||
notificationCreator.createSummaryListNotificationResult.assertions()
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotif
|
|||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
|
@ -34,6 +34,7 @@ private val MY_AVATAR_URL: String? = null
|
|||
private val AN_INVITATION_EVENT = anInviteNotifiableEvent(roomId = A_ROOM_ID)
|
||||
private val A_SIMPLE_EVENT = aSimpleNotifiableEvent(eventId = AN_EVENT_ID)
|
||||
private val A_MESSAGE_EVENT = aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID)
|
||||
private val A_FALLBACK_EVENT = aFallbackNotifiableEvent()
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class NotificationDataFactoryTest {
|
||||
|
|
@ -47,7 +48,6 @@ class NotificationDataFactoryTest {
|
|||
roomGroupMessageCreator = fakeRoomGroupMessageCreator,
|
||||
summaryGroupMessageCreator = fakeSummaryGroupMessageCreator,
|
||||
activeNotificationsProvider = activeNotificationsProvider,
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
|
||||
@Test
|
||||
|
|
@ -64,7 +64,6 @@ class NotificationDataFactoryTest {
|
|||
OneShotNotification(
|
||||
notification = expectedNotification,
|
||||
tag = A_ROOM_ID.value,
|
||||
summaryLine = AN_INVITATION_EVENT.description,
|
||||
isNoisy = AN_INVITATION_EVENT.noisy,
|
||||
timestamp = AN_INVITATION_EVENT.timestamp
|
||||
)
|
||||
|
|
@ -72,6 +71,25 @@ class NotificationDataFactoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a fallback invitation when mapping to notification then it's added`() = testWith(notificationDataFactory) {
|
||||
val fallbackEvents = listOf(A_FALLBACK_EVENT)
|
||||
val expectedNotification = notificationCreator.createFallbackNotificationResult(
|
||||
null,
|
||||
aNotificationAccountParams(),
|
||||
fallbackEvents,
|
||||
)
|
||||
val result = toNotification(fallbackEvents, aNotificationAccountParams())
|
||||
assertThat(result).isEqualTo(
|
||||
OneShotNotification(
|
||||
notification = expectedNotification,
|
||||
tag = "FALLBACK",
|
||||
isNoisy = false,
|
||||
timestamp = A_FALLBACK_EVENT.timestamp
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a simple event when mapping to notification then it's added`() = testWith(notificationDataFactory) {
|
||||
val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(
|
||||
|
|
@ -83,7 +101,6 @@ class NotificationDataFactoryTest {
|
|||
OneShotNotification(
|
||||
notification = expectedNotification,
|
||||
tag = AN_EVENT_ID.value,
|
||||
summaryLine = A_SIMPLE_EVENT.description,
|
||||
isNoisy = A_SIMPLE_EVENT.noisy,
|
||||
timestamp = AN_INVITATION_EVENT.timestamp
|
||||
)
|
||||
|
|
@ -105,7 +122,6 @@ class NotificationDataFactoryTest {
|
|||
existingNotification = null,
|
||||
),
|
||||
roomId = A_ROOM_ID,
|
||||
summaryLine = "A room name: Bob Hello world!",
|
||||
messageCount = events.size,
|
||||
latestTimestamp = events.maxOf { it.timestamp },
|
||||
shouldBing = events.any { it.noisy },
|
||||
|
|
@ -161,7 +177,6 @@ class NotificationDataFactoryTest {
|
|||
existingNotification = null,
|
||||
),
|
||||
roomId = A_ROOM_ID,
|
||||
summaryLine = "A room name: Bob Hello world!",
|
||||
messageCount = withRedactedRemoved.size,
|
||||
latestTimestamp = withRedactedRemoved.maxOf { it.timestamp },
|
||||
shouldBing = withRedactedRemoved.any { it.noisy },
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNot
|
|||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -43,7 +42,7 @@ private const val USE_COMPLETE_NOTIFICATION_FORMAT = true
|
|||
|
||||
private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(A_NOTIFICATION)
|
||||
private val ONE_SHOT_NOTIFICATION =
|
||||
OneShotNotification(notification = A_NOTIFICATION, tag = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1)
|
||||
OneShotNotification(notification = A_NOTIFICATION, tag = "ignored", isNoisy = false, timestamp = -1)
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class NotificationRendererTest {
|
||||
|
|
@ -57,7 +56,6 @@ class NotificationRendererTest {
|
|||
roomGroupMessageCreator = roomGroupMessageCreator,
|
||||
summaryGroupMessageCreator = summaryGroupMessageCreator,
|
||||
activeNotificationsProvider = FakeActiveNotificationsProvider(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
private val notificationIdProvider = NotificationIdProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ import io.element.android.libraries.push.impl.notifications.factories.action.Acc
|
|||
import io.element.android.libraries.push.impl.notifications.factories.action.MarkAsReadActionFactory
|
||||
import io.element.android.libraries.push.impl.notifications.factories.action.QuickReplyActionFactory
|
||||
import io.element.android.libraries.push.impl.notifications.factories.action.RejectInvitationActionFactory
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
|
||||
|
|
@ -84,19 +84,11 @@ class DefaultNotificationCreatorTest {
|
|||
fun `test createFallbackNotification`() {
|
||||
val sut = createNotificationCreator()
|
||||
val result = sut.createFallbackNotification(
|
||||
existingNotification = null,
|
||||
notificationAccountParams = aNotificationAccountParams(),
|
||||
FallbackNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
editedEventId = null,
|
||||
description = "description",
|
||||
canBeReplaced = false,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
timestamp = A_FAKE_TIMESTAMP,
|
||||
cause = null,
|
||||
),
|
||||
fallbackNotifiableEvents = listOf(
|
||||
aFallbackNotifiableEvent(),
|
||||
)
|
||||
)
|
||||
result.commonAssertions(
|
||||
expectedCategory = null,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class FakeActiveNotificationsProvider(
|
|||
private val getMembershipNotificationForSessionResult: (SessionId) -> List<StatusBarNotification> = { emptyList() },
|
||||
private val getMembershipNotificationForRoomResult: (SessionId, RoomId) -> List<StatusBarNotification> = { _, _ -> emptyList() },
|
||||
private val getSummaryNotificationResult: (SessionId) -> StatusBarNotification? = { null },
|
||||
private val getFallbackNotificationResult: (SessionId) -> StatusBarNotification? = { null },
|
||||
private val countResult: (SessionId) -> Int = { 0 },
|
||||
) : ActiveNotificationsProvider {
|
||||
override fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId, threadId: ThreadId?): List<StatusBarNotification> {
|
||||
|
|
@ -47,6 +48,10 @@ class FakeActiveNotificationsProvider(
|
|||
return getSummaryNotificationResult(sessionId)
|
||||
}
|
||||
|
||||
override fun getFallbackNotification(sessionId: SessionId): StatusBarNotification? {
|
||||
return getFallbackNotificationResult(sessionId)
|
||||
}
|
||||
|
||||
override fun count(sessionId: SessionId): Int {
|
||||
return countResult(sessionId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiab
|
|||
import io.element.android.tests.testutils.lambda.LambdaFiveParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaListAnyParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.lambdaAnyRecorder
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
|
@ -34,8 +35,8 @@ class FakeNotificationCreator(
|
|||
lambdaRecorder { _, _ -> A_NOTIFICATION },
|
||||
var createSimpleNotificationResult: LambdaTwoParamsRecorder<NotificationAccountParams, SimpleNotifiableEvent, Notification> =
|
||||
lambdaRecorder { _, _ -> A_NOTIFICATION },
|
||||
var createFallbackNotificationResult: LambdaTwoParamsRecorder<NotificationAccountParams, FallbackNotifiableEvent, Notification> =
|
||||
lambdaRecorder { _, _ -> A_NOTIFICATION },
|
||||
var createFallbackNotificationResult: LambdaThreeParamsRecorder<Notification?, NotificationAccountParams, List<FallbackNotifiableEvent>, Notification> =
|
||||
lambdaRecorder { _, _, _ -> A_NOTIFICATION },
|
||||
var createSummaryListNotificationResult: LambdaFiveParamsRecorder<
|
||||
NotificationAccountParams, String, Boolean, Long, NotificationAccountParams, Notification
|
||||
> = lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION },
|
||||
|
|
@ -75,10 +76,15 @@ class FakeNotificationCreator(
|
|||
}
|
||||
|
||||
override fun createFallbackNotification(
|
||||
existingNotification: Notification?,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
fallbackNotifiableEvents: List<FallbackNotifiableEvent>,
|
||||
): Notification {
|
||||
return createFallbackNotificationResult(notificationAccountParams, fallbackNotifiableEvent)
|
||||
return createFallbackNotificationResult(
|
||||
existingNotification,
|
||||
notificationAccountParams,
|
||||
fallbackNotifiableEvents,
|
||||
)
|
||||
}
|
||||
|
||||
override fun createSummaryListNotification(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import io.element.android.libraries.push.impl.notifications.model.FallbackNotifi
|
|||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.tests.testutils.lambda.LambdaFiveParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaFourParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
|
@ -28,18 +28,17 @@ class FakeNotificationDataFactory(
|
|||
var messageEventToNotificationsResult: LambdaThreeParamsRecorder<
|
||||
List<NotifiableMessageEvent>, ImageLoader, NotificationAccountParams, List<RoomNotification>
|
||||
> = lambdaRecorder { _, _, _ -> emptyList() },
|
||||
var summaryToNotificationsResult: LambdaFiveParamsRecorder<
|
||||
var summaryToNotificationsResult: LambdaFourParamsRecorder<
|
||||
List<RoomNotification>,
|
||||
List<OneShotNotification>,
|
||||
List<OneShotNotification>,
|
||||
List<OneShotNotification>,
|
||||
NotificationAccountParams,
|
||||
SummaryNotification
|
||||
> = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
||||
> = lambdaRecorder { _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) },
|
||||
var inviteToNotificationsResult: LambdaOneParamRecorder<List<InviteNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||
var simpleEventToNotificationsResult: LambdaOneParamRecorder<List<SimpleNotifiableEvent>, List<OneShotNotification>> = lambdaRecorder { _ -> emptyList() },
|
||||
var fallbackEventToNotificationsResult: LambdaOneParamRecorder<List<FallbackNotifiableEvent>, List<OneShotNotification>> =
|
||||
lambdaRecorder { _ -> emptyList() },
|
||||
var fallbackEventToNotificationsResult: LambdaOneParamRecorder<List<FallbackNotifiableEvent>, OneShotNotification?> =
|
||||
lambdaRecorder { _ -> null },
|
||||
) : NotificationDataFactory {
|
||||
override suspend fun toNotifications(
|
||||
messages: List<NotifiableMessageEvent>,
|
||||
|
|
@ -69,10 +68,10 @@ class FakeNotificationDataFactory(
|
|||
|
||||
@JvmName("toNotificationFallbackEvents")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
override fun toNotifications(
|
||||
override fun toNotification(
|
||||
fallback: List<FallbackNotifiableEvent>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): List<OneShotNotification> {
|
||||
): OneShotNotification? {
|
||||
return fallbackEventToNotificationsResult(fallback)
|
||||
}
|
||||
|
||||
|
|
@ -80,14 +79,12 @@ class FakeNotificationDataFactory(
|
|||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
): SummaryNotification {
|
||||
return summaryToNotificationsResult(
|
||||
roomNotifications,
|
||||
invitationNotifications,
|
||||
simpleNotifications,
|
||||
fallbackNotifications,
|
||||
notificationAccountParams,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,27 +14,25 @@ import io.element.android.libraries.push.impl.notifications.RoomNotification
|
|||
import io.element.android.libraries.push.impl.notifications.SummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION
|
||||
import io.element.android.tests.testutils.lambda.LambdaFiveParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.LambdaFourParamsRecorder
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeSummaryGroupMessageCreator(
|
||||
var createSummaryNotificationResult: LambdaFiveParamsRecorder<
|
||||
NotificationAccountParams, List<RoomNotification>, List<OneShotNotification>, List<OneShotNotification>, List<OneShotNotification>, Notification> =
|
||||
lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }
|
||||
var createSummaryNotificationResult: LambdaFourParamsRecorder<
|
||||
NotificationAccountParams, List<RoomNotification>, List<OneShotNotification>, List<OneShotNotification>, Notification> =
|
||||
lambdaRecorder { _, _, _, _ -> A_NOTIFICATION }
|
||||
) : SummaryGroupMessageCreator {
|
||||
override fun createSummaryNotification(
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
roomNotifications: List<RoomNotification>,
|
||||
invitationNotifications: List<OneShotNotification>,
|
||||
simpleNotifications: List<OneShotNotification>,
|
||||
fallbackNotifications: List<OneShotNotification>,
|
||||
): Notification {
|
||||
return createSummaryNotificationResult(
|
||||
notificationAccountParams,
|
||||
roomNotifications,
|
||||
invitationNotifications,
|
||||
simpleNotifications,
|
||||
fallbackNotifications,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
|||
import io.element.android.libraries.matrix.test.A_TIMESTAMP
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME_2
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
|
||||
|
||||
fun aSimpleNotifiableEvent(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
|
|
@ -141,3 +143,18 @@ fun aNotifiableCallEvent(
|
|||
senderAvatarUrl = senderAvatarUrl,
|
||||
rtcNotificationType = rtcNotificationType,
|
||||
)
|
||||
|
||||
fun aFallbackNotifiableEvent(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
) = FallbackNotifiableEvent(
|
||||
sessionId = sessionId,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
editedEventId = null,
|
||||
description = "A fallback notification",
|
||||
canBeReplaced = false,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
timestamp = A_FAKE_TIMESTAMP,
|
||||
cause = "Unable to decrypt event",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ import io.element.android.libraries.push.impl.notifications.DefaultNotificationR
|
|||
import io.element.android.libraries.push.impl.notifications.FakeNotifiableEventResolver
|
||||
import io.element.android.libraries.push.impl.notifications.FallbackNotificationFactory
|
||||
import io.element.android.libraries.push.impl.notifications.channels.FakeNotificationChannels
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aFallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
||||
import io.element.android.libraries.push.impl.test.DefaultTestPush
|
||||
|
|
@ -57,7 +57,6 @@ import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.Fa
|
|||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
||||
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
||||
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import io.element.android.tests.testutils.lambda.any
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
|
@ -627,18 +626,7 @@ class DefaultPushHandlerTest {
|
|||
|
||||
@Test
|
||||
fun `when receiving a fallback event, we notify the push history service about it not being resolved`() = runTest {
|
||||
val aNotifiableFallbackEvent = FallbackNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
editedEventId = null,
|
||||
description = "A fallback notification",
|
||||
canBeReplaced = false,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
timestamp = 0L,
|
||||
cause = "Unable to decrypt event",
|
||||
)
|
||||
val aNotifiableFallbackEvent = aFallbackNotifiableEvent()
|
||||
val notifiableEventResult =
|
||||
lambdaRecorder<SessionId, List<NotificationEventRequest>, Result<Map<NotificationEventRequest, Result<ResolvedPushEvent>>>> { _, _ ->
|
||||
val request = NotificationEventRequest(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, A_PUSHER_INFO)
|
||||
|
|
@ -724,7 +712,6 @@ class DefaultPushHandlerTest {
|
|||
appCoroutineScope = backgroundScope,
|
||||
fallbackNotificationFactory = FallbackNotificationFactory(
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
),
|
||||
syncOnNotifiableEvent = syncOnNotifiableEvent,
|
||||
featureFlagService = featureFlagService,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue