Browse Source

Notify of ringing call when there's an active call (#3003)

* Add `CallNotificationEventResolver` to be able to force the new ringing notification to be non-ringing given an existing ringing one.
pull/3007/head
Jorge Martin Espinosa 3 months ago committed by GitHub
parent
commit
f07ec61ecc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 20
      features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt
  2. 24
      features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt
  3. 24
      libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/notification/NotificationDataTest.kt
  4. 43
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt
  5. 90
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/CallNotificationEventResolver.kt
  6. 41
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
  7. 17
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt
  8. 3
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt
  9. 15
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt
  10. 2
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
  11. 4
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt
  12. 30
      libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeCallNotificationEventResolver.kt

20
features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt

@ -89,6 +89,7 @@ class DefaultActiveCallManager @Inject constructor(
override fun registerIncomingCall(notificationData: CallNotificationData) { override fun registerIncomingCall(notificationData: CallNotificationData) {
if (activeCall.value != null) { if (activeCall.value != null) {
displayMissedCallNotification(notificationData)
Timber.w("Already have an active call, ignoring incoming call: $notificationData") Timber.w("Already have an active call, ignoring incoming call: $notificationData")
return return
} }
@ -99,7 +100,6 @@ class DefaultActiveCallManager @Inject constructor(
) )
timedOutCallJob = coroutineScope.launch { timedOutCallJob = coroutineScope.launch {
registerIncomingCall(notificationData)
showIncomingCallNotification(notificationData) showIncomingCallNotification(notificationData)
// Wait for the call to end // Wait for the call to end
@ -115,13 +115,7 @@ class DefaultActiveCallManager @Inject constructor(
cancelIncomingCallNotification() cancelIncomingCallNotification()
coroutineScope.launch { displayMissedCallNotification(notificationData)
onMissedCallNotificationHandler.addMissedCallNotification(
sessionId = previousActiveCall.sessionId,
roomId = previousActiveCall.roomId,
eventId = notificationData.eventId,
)
}
} }
override fun hungUpCall() { override fun hungUpCall() {
@ -174,6 +168,16 @@ class DefaultActiveCallManager @Inject constructor(
private fun cancelIncomingCallNotification() { private fun cancelIncomingCallNotification() {
notificationManagerCompat.cancel(NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.INCOMING_CALL)) notificationManagerCompat.cancel(NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.INCOMING_CALL))
} }
private fun displayMissedCallNotification(notificationData: CallNotificationData) {
coroutineScope.launch {
onMissedCallNotificationHandler.addMissedCallNotification(
sessionId = notificationData.sessionId,
roomId = notificationData.roomId,
eventId = notificationData.eventId,
)
}
}
} }
/** /**

24
features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt

@ -27,6 +27,7 @@ import io.element.android.features.call.test.aCallNotificationData
import io.element.android.libraries.matrix.api.core.EventId 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.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
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
import io.element.android.libraries.matrix.test.A_ROOM_ID_2 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_SESSION_ID
@ -39,10 +40,12 @@ import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolde
import io.element.android.libraries.push.test.notifications.FakeOnMissedCallNotificationHandler import io.element.android.libraries.push.test.notifications.FakeOnMissedCallNotificationHandler
import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -77,9 +80,14 @@ class DefaultActiveCallManagerTest {
verify { notificationManagerCompat.notify(notificationId, any()) } verify { notificationManagerCompat.notify(notificationId, any()) }
} }
@OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `registerIncomingCall - when there is an already active call does nothing`() = runTest { fun `registerIncomingCall - when there is an already active call adds missed call notification`() = runTest {
val manager = createActiveCallManager() val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
val onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
val manager = createActiveCallManager(
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
)
// Register existing call // Register existing call
val callNotificationData = aCallNotificationData() val callNotificationData = aCallNotificationData()
@ -91,6 +99,12 @@ class DefaultActiveCallManagerTest {
assertThat(manager.activeCall.value).isEqualTo(activeCall) assertThat(manager.activeCall.value).isEqualTo(activeCall)
assertThat(manager.activeCall.value?.roomId).isNotEqualTo(A_ROOM_ID_2) assertThat(manager.activeCall.value?.roomId).isNotEqualTo(A_ROOM_ID_2)
advanceTimeBy(1)
addMissedCallNotificationLambda.assertions()
.isCalledOnce()
.with(value(A_SESSION_ID), value(A_ROOM_ID_2), value(AN_EVENT_ID))
} }
@Test @Test
@ -119,12 +133,10 @@ class DefaultActiveCallManagerTest {
assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeCall.value).isNotNull()
manager.incomingCallTimedOut() manager.incomingCallTimedOut()
assertThat(manager.activeCall.value).isNull() advanceTimeBy(1)
runCurrent()
assertThat(manager.activeCall.value).isNull()
addMissedCallNotificationLambda.assertions().isCalledOnce() addMissedCallNotificationLambda.assertions().isCalledOnce()
verify { notificationManagerCompat.cancel(notificationId) } verify { notificationManagerCompat.cancel(notificationId) }
} }

24
libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/notification/NotificationDataTest.kt

@ -17,9 +17,8 @@
package io.element.android.libraries.matrix.api.notification package io.element.android.libraries.matrix.api.notification
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
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_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.notification.aNotificationData
import org.junit.Test import org.junit.Test
class NotificationDataTest { class NotificationDataTest {
@ -49,25 +48,4 @@ class NotificationDataTest {
) )
assertThat(sut.getDisambiguatedDisplayName(A_USER_ID)).isEqualTo("Alice (@alice:server.org)") assertThat(sut.getDisambiguatedDisplayName(A_USER_ID)).isEqualTo("Alice (@alice:server.org)")
} }
private fun aNotificationData(
senderDisplayName: String?,
senderIsNameAmbiguous: Boolean,
): NotificationData {
return NotificationData(
eventId = AN_EVENT_ID,
roomId = A_ROOM_ID,
senderAvatarUrl = null,
senderDisplayName = senderDisplayName,
senderIsNameAmbiguous = senderIsNameAmbiguous,
roomAvatarUrl = null,
roomDisplayName = null,
isDirect = false,
isEncrypted = false,
isNoisy = false,
timestamp = 0L,
content = NotificationContent.MessageLike.RoomEncrypted,
hasMention = false,
)
}
} }

43
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt

@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.test.notification
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
fun aNotificationData(
senderDisplayName: String?,
senderIsNameAmbiguous: Boolean,
): NotificationData {
return NotificationData(
eventId = AN_EVENT_ID,
roomId = A_ROOM_ID,
senderAvatarUrl = null,
senderDisplayName = senderDisplayName,
senderIsNameAmbiguous = senderIsNameAmbiguous,
roomAvatarUrl = null,
roomDisplayName = null,
isDirect = false,
isEncrypted = false,
isNoisy = false,
timestamp = 0L,
content = NotificationContent.MessageLike.RoomEncrypted,
hasMention = false,
)
}

90
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/CallNotificationEventResolver.kt

@ -0,0 +1,90 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.impl.notifications
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
import io.element.android.services.toolbox.api.strings.StringProvider
import javax.inject.Inject
/**
* Helper to resolve a valid [NotifiableEvent] from a [NotificationData].
*/
interface CallNotificationEventResolver {
/**
* Resolve a call notification event from a notification data depending on whether it should be a ringing one or not.
* @param sessionId the current session id
* @param notificationData the notification data
* @param forceNotify `true` to force the notification to be non-ringing, `false` to use the default behaviour. Default is `false`.
* @return a [NotifiableEvent] if the notification data is a call notification, null otherwise
*/
fun resolveEvent(sessionId: SessionId, notificationData: NotificationData, forceNotify: Boolean = false): NotifiableEvent?
}
@ContributesBinding(AppScope::class)
class DefaultCallNotificationEventResolver @Inject constructor(
private val stringProvider: StringProvider,
) : CallNotificationEventResolver {
override fun resolveEvent(sessionId: SessionId, notificationData: NotificationData, forceNotify: Boolean): NotifiableEvent? {
val content = notificationData.content as? NotificationContent.MessageLike.CallNotify ?: return null
return notificationData.run {
if (NotifiableRingingCallEvent.shouldRing(content.type, timestamp) && !forceNotify) {
NotifiableRingingCallEvent(
sessionId = sessionId,
roomId = roomId,
eventId = eventId,
roomName = roomDisplayName,
editedEventId = null,
canBeReplaced = true,
timestamp = this.timestamp,
isRedacted = false,
isUpdated = false,
description = stringProvider.getString(R.string.notification_incoming_call),
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
roomAvatarUrl = roomAvatarUrl,
callNotifyType = content.type,
senderId = content.senderId,
senderAvatarUrl = senderAvatarUrl,
)
} else {
// Create a simple message notification event
buildNotifiableMessageEvent(
sessionId = sessionId,
senderId = content.senderId,
roomId = roomId,
eventId = eventId,
noisy = true,
timestamp = this.timestamp,
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = " ${stringProvider.getString(R.string.notification_incoming_call)}",
roomName = roomDisplayName,
roomIsDirect = isDirect,
roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl,
type = EventType.CALL_NOTIFY,
)
}
}
}
}

41
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt

@ -52,7 +52,6 @@ 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.InviteNotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent 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.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.toolbox.api.strings.StringProvider import io.element.android.services.toolbox.api.strings.StringProvider
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
@ -79,6 +78,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
private val notificationMediaRepoFactory: NotificationMediaRepo.Factory, private val notificationMediaRepoFactory: NotificationMediaRepo.Factory,
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val permalinkParser: PermalinkParser, private val permalinkParser: PermalinkParser,
private val callNotificationEventResolver: CallNotificationEventResolver,
) : NotifiableEventResolver { ) : NotifiableEventResolver {
override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? {
// Restore session // Restore session
@ -174,42 +174,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
) )
} }
is NotificationContent.MessageLike.CallNotify -> { is NotificationContent.MessageLike.CallNotify -> {
if (NotifiableRingingCallEvent.shouldRing(content.type, timestamp)) { callNotificationEventResolver.resolveEvent(userId, this)
NotifiableRingingCallEvent(
sessionId = userId,
roomId = roomId,
eventId = eventId,
roomName = roomDisplayName,
editedEventId = null,
canBeReplaced = true,
timestamp = this.timestamp,
isRedacted = false,
isUpdated = false,
description = stringProvider.getString(R.string.notification_incoming_call),
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
roomAvatarUrl = roomAvatarUrl,
callNotifyType = content.type,
senderId = content.senderId,
senderAvatarUrl = senderAvatarUrl,
)
} else {
// Create a simple message notification event
buildNotifiableMessageEvent(
sessionId = userId,
senderId = content.senderId,
roomId = roomId,
eventId = eventId,
noisy = true,
timestamp = this.timestamp,
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = " ${stringProvider.getString(R.string.notification_incoming_call)}",
roomName = roomDisplayName,
roomIsDirect = isDirect,
roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl,
type = EventType.CALL_NOTIFY,
)
}
} }
NotificationContent.MessageLike.KeyVerificationAccept, NotificationContent.MessageLike.KeyVerificationAccept,
NotificationContent.MessageLike.KeyVerificationCancel, NotificationContent.MessageLike.KeyVerificationCancel,
@ -349,7 +314,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
} }
@Suppress("LongParameterList") @Suppress("LongParameterList")
private fun buildNotifiableMessageEvent( internal fun buildNotifiableMessageEvent(
sessionId: SessionId, sessionId: SessionId,
senderId: UserId, senderId: UserId,
roomId: RoomId, roomId: RoomId,

17
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandler.kt

@ -18,6 +18,7 @@ package io.element.android.libraries.push.impl.notifications
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId 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.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
@ -26,8 +27,9 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultOnMissedCallNotificationHandler @Inject constructor( class DefaultOnMissedCallNotificationHandler @Inject constructor(
private val matrixClientProvider: MatrixClientProvider,
private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager, private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager,
private val notifiableEventResolver: NotifiableEventResolver, private val callNotificationEventResolver: CallNotificationEventResolver,
) : OnMissedCallNotificationHandler { ) : OnMissedCallNotificationHandler {
override suspend fun addMissedCallNotification( override suspend fun addMissedCallNotification(
sessionId: SessionId, sessionId: SessionId,
@ -35,7 +37,18 @@ class DefaultOnMissedCallNotificationHandler @Inject constructor(
eventId: EventId, eventId: EventId,
) { ) {
// Resolve the event and add a notification for it, at this point it should no longer be a ringing one // Resolve the event and add a notification for it, at this point it should no longer be a ringing one
val notifiableEvent = notifiableEventResolver.resolveEvent(sessionId, roomId, eventId) val notificationData = matrixClientProvider.getOrRestore(sessionId).getOrNull()
?.notificationService()
?.getNotification(sessionId, roomId, eventId)
?.getOrNull()
?: return
val notifiableEvent = callNotificationEventResolver.resolveEvent(
sessionId = sessionId,
notificationData = notificationData,
// Make sure the notifiable event is not a ringing one
forceNotify = true,
)
notifiableEvent?.let { defaultNotificationDrawerManager.onNotifiableEventReceived(it) } notifiableEvent?.let { defaultNotificationDrawerManager.onNotifiableEventReceived(it) }
} }
} }

3
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt

@ -660,6 +660,9 @@ class DefaultNotifiableEventResolverTest {
notificationMediaRepoFactory = notificationMediaRepoFactory, notificationMediaRepoFactory = notificationMediaRepoFactory,
context = context, context = context,
permalinkParser = FakePermalinkParser(), permalinkParser = FakePermalinkParser(),
callNotificationEventResolver = DefaultCallNotificationEventResolver(
stringProvider = AndroidStringProvider(context.resources)
),
) )
} }

15
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt

@ -19,11 +19,16 @@ package io.element.android.libraries.push.impl.notifications
import io.element.android.libraries.matrix.test.AN_EVENT_ID 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
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider 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.push.impl.notifications.fake.FakeActiveNotificationsProvider 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.FakeNotificationDataFactory
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer 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.impl.notifications.fixtures.aNotifiableMessageEvent
import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver
import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -47,7 +52,15 @@ class DefaultOnMissedCallNotificationHandlerTest {
val dataFactory = FakeNotificationDataFactory( val dataFactory = FakeNotificationDataFactory(
messageEventToNotificationsResult = lambdaRecorder { _, _, _ -> emptyList() } messageEventToNotificationsResult = lambdaRecorder { _, _, _ -> emptyList() }
) )
// Create a fake matrix client provider that returns a fake matrix client with a fake notification service that returns a valid notification data
val matrixClientProvider = FakeMatrixClientProvider(getClient = {
val notificationService = FakeNotificationService().apply {
givenGetNotificationResult(Result.success(aNotificationData(senderDisplayName = A_USER_NAME, senderIsNameAmbiguous = false)))
}
Result.success(FakeMatrixClient(notificationService = notificationService))
})
val defaultOnMissedCallNotificationHandler = DefaultOnMissedCallNotificationHandler( val defaultOnMissedCallNotificationHandler = DefaultOnMissedCallNotificationHandler(
matrixClientProvider = matrixClientProvider,
defaultNotificationDrawerManager = DefaultNotificationDrawerManager( defaultNotificationDrawerManager = DefaultNotificationDrawerManager(
notificationManager = mockk(relaxed = true), notificationManager = mockk(relaxed = true),
notificationRenderer = NotificationRenderer( notificationRenderer = NotificationRenderer(
@ -60,7 +73,7 @@ class DefaultOnMissedCallNotificationHandlerTest {
imageLoaderHolder = FakeImageLoaderHolder(), imageLoaderHolder = FakeImageLoaderHolder(),
activeNotificationsProvider = FakeActiveNotificationsProvider(), activeNotificationsProvider = FakeActiveNotificationsProvider(),
), ),
notifiableEventResolver = FakeNotifiableEventResolver(notifiableEventResult = { _, _, _ -> aNotifiableMessageEvent() }), callNotificationEventResolver = FakeCallNotificationEventResolver(resolveEventLambda = { _, _, _ -> aNotifiableMessageEvent() }),
) )
defaultOnMissedCallNotificationHandler.addMissedCallNotification( defaultOnMissedCallNotificationHandler.addMissedCallNotification(

2
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt

@ -104,7 +104,7 @@ fun aNotifiableMessageEvent(
type = type, type = type,
) )
fun anNotifiableCallEvent( fun aNotifiableCallEvent(
sessionId: SessionId = A_SESSION_ID, sessionId: SessionId = A_SESSION_ID,
roomId: RoomId = A_ROOM_ID, roomId: RoomId = A_ROOM_ID,
eventId: EventId = AN_EVENT_ID, eventId: EventId = AN_EVENT_ID,

4
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt

@ -37,8 +37,8 @@ import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationSer
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.push.impl.notifications.FakeNotifiableEventResolver import io.element.android.libraries.push.impl.notifications.FakeNotifiableEventResolver
import io.element.android.libraries.push.impl.notifications.channels.FakeNotificationChannels import io.element.android.libraries.push.impl.notifications.channels.FakeNotificationChannels
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.fixtures.aNotifiableMessageEvent
import io.element.android.libraries.push.impl.notifications.fixtures.anNotifiableCallEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.test.DefaultTestPush import io.element.android.libraries.push.impl.test.DefaultTestPush
import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler
@ -240,7 +240,7 @@ class DefaultPushHandlerTest {
val elementCallEntryPoint = FakeElementCallEntryPoint(handleIncomingCallResult = handleIncomingCallLambda) val elementCallEntryPoint = FakeElementCallEntryPoint(handleIncomingCallResult = handleIncomingCallLambda)
val defaultPushHandler = createDefaultPushHandler( val defaultPushHandler = createDefaultPushHandler(
elementCallEntryPoint = elementCallEntryPoint, elementCallEntryPoint = elementCallEntryPoint,
notifiableEventResult = { _, _, _ -> anNotifiableCallEvent(callNotifyType = CallNotifyType.RING, timestamp = Instant.now().toEpochMilli()) }, notifiableEventResult = { _, _, _ -> aNotifiableCallEvent(callNotifyType = CallNotifyType.RING, timestamp = Instant.now().toEpochMilli()) },
incrementPushCounterResult = {}, incrementPushCounterResult = {},
pushClientSecret = FakePushClientSecret( pushClientSecret = FakePushClientSecret(
getUserIdFromSecretResult = { A_USER_ID } getUserIdFromSecretResult = { A_USER_ID }

30
libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeCallNotificationEventResolver.kt

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.test.notifications
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.push.impl.notifications.CallNotificationEventResolver
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
class FakeCallNotificationEventResolver(
var resolveEventLambda: (sessionId: SessionId, notificationData: NotificationData, forceNotify: Boolean) -> NotifiableEvent? = { _, _, _ -> null },
) : CallNotificationEventResolver {
override fun resolveEvent(sessionId: SessionId, notificationData: NotificationData, forceNotify: Boolean): NotifiableEvent? {
return resolveEventLambda(sessionId, notificationData, forceNotify)
}
}
Loading…
Cancel
Save