Update dependency org.matrix.rustcomponents:sdk-android to v25.7.7 (#4989)

Make sure we distinguish between notification events that were filtered out and those that couldn't be resolved.

---

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
renovate[bot] 2025-07-07 17:56:51 +02:00 committed by GitHub
parent ca206617c4
commit 04a1c00b94
18 changed files with 313 additions and 200 deletions

View file

@ -0,0 +1,28 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.exception
/**
* Exceptions that can occur while resolving the events associated to push notifications.
*/
sealed class NotificationResolverException : Exception() {
/**
* The event was not found by the notification service.
*/
data object EventNotFound : NotificationResolverException()
/**
* The event was found but it was filtered out by the notification service.
*/
data object EventFilteredOut : NotificationResolverException()
/**
* An unexpected error occurred while trying to resolve the event.
*/
data class UnknownError(override val message: String) : NotificationResolverException()
}

View file

@ -68,7 +68,6 @@ sealed interface NotificationContent {
) : MessageLike
data object RoomEncrypted : MessageLike
data object UnableToResolve : MessageLike
data class RoomMessage(
val senderId: UserId,
val messageType: MessageType

View file

@ -10,6 +10,19 @@ package io.element.android.libraries.matrix.api.notification
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
/**
* Represents the resolution state of an attempt to retrieve notification data for a set of event ids.
* The outer [Result] indicates the success or failure of the setup to retrieve notifications.
* The inner [Result] for each [EventId] in the map indicates whether the notification data was successfully retrieved or if there was an error.
*/
typealias GetNotificationDataResult = Result<Map<EventId, Result<NotificationData>>>
/**
* Service to retrieve notifications for a given set of event ids in specific rooms.
*/
interface NotificationService {
suspend fun getNotifications(ids: Map<RoomId, List<EventId>>): Result<Map<EventId, NotificationData>>
/**
* Fetch notifications for the specified event ids in the given rooms.
*/
suspend fun getNotifications(ids: Map<RoomId, List<EventId>>): GetNotificationDataResult
}

View file

@ -12,26 +12,29 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
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.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.exception.NotificationResolverException
import io.element.android.libraries.matrix.api.notification.GetNotificationDataResult
import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.BatchNotificationResult
import org.matrix.rustcomponents.sdk.NotificationClient
import org.matrix.rustcomponents.sdk.NotificationItemsRequest
import org.matrix.rustcomponents.sdk.NotificationStatus
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
class RustNotificationService(
private val sessionId: SessionId,
private val notificationClient: NotificationClient,
private val dispatchers: CoroutineDispatchers,
private val clock: SystemClock,
clock: SystemClock,
) : NotificationService {
private val notificationMapper: NotificationMapper = NotificationMapper(clock)
override suspend fun getNotifications(
ids: Map<RoomId, List<EventId>>
): Result<Map<EventId, NotificationData>> = withContext(dispatchers.io) {
): GetNotificationDataResult = withContext(dispatchers.io) {
runCatchingExceptions {
val requests = ids.map { (roomId, eventIds) ->
NotificationItemsRequest(
@ -42,34 +45,41 @@ class RustNotificationService(
val items = notificationClient.getNotifications(requests)
buildMap {
val eventIds = requests.flatMap { it.eventIds }
for (eventId in eventIds) {
val item = items[eventId]
val roomId = RoomId(requests.find { it.eventIds.contains(eventId) }?.roomId!!)
if (item != null) {
put(EventId(eventId), notificationMapper.map(sessionId, EventId(eventId), roomId, item))
} else {
Timber.e("Could not retrieve event for notification with $eventId")
put(
EventId(eventId),
NotificationData(
sessionId = sessionId,
eventId = EventId(eventId),
threadId = null,
roomId = roomId,
senderAvatarUrl = null,
senderDisplayName = null,
senderIsNameAmbiguous = false,
roomAvatarUrl = null,
roomDisplayName = null,
isDirect = false,
isDm = false,
isEncrypted = false,
isNoisy = false,
timestamp = clock.epochMillis(),
content = NotificationContent.MessageLike.UnableToResolve,
hasMention = false
)
)
for (rawEventId in eventIds) {
val roomId = RoomId(requests.find { it.eventIds.contains(rawEventId) }?.roomId!!)
val eventId = EventId(rawEventId)
items[rawEventId].use { result ->
when (result) {
is BatchNotificationResult.Ok -> {
when (val status = result.status) {
is NotificationStatus.Event -> {
put(eventId, Result.success(notificationMapper.map(sessionId, eventId, roomId, status.item)))
}
is NotificationStatus.EventNotFound -> {
Timber.e("Could not retrieve event for notification with $eventId - event not found")
put(eventId, Result.failure(NotificationResolverException.EventNotFound))
}
is NotificationStatus.EventFilteredOut -> {
Timber.d("Could not retrieve event for notification with $eventId - event filtered out")
put(eventId, Result.failure(NotificationResolverException.EventFilteredOut))
}
}
}
is BatchNotificationResult.Error -> {
Timber.e("Error while retrieving notification with $rawEventId - ${result.message}")
put(
eventId,
Result.failure(NotificationResolverException.UnknownError(result.message))
)
}
null -> {
Timber.e("The notification data for $rawEventId was not in the retrieved results. This is unexpected.")
put(
eventId,
Result.failure(NotificationResolverException.UnknownError("Notification data not found"))
)
}
}
}
}
}

View file

@ -16,6 +16,7 @@ import org.matrix.rustcomponents.sdk.NotificationEvent
import org.matrix.rustcomponents.sdk.NotificationItem
import org.matrix.rustcomponents.sdk.NotificationRoomInfo
import org.matrix.rustcomponents.sdk.NotificationSenderInfo
import org.matrix.rustcomponents.sdk.NotificationStatus
import org.matrix.rustcomponents.sdk.TimelineEvent
fun aRustNotificationItem(
@ -34,6 +35,12 @@ fun aRustNotificationItem(
threadId = threadId?.value,
)
fun aRustBatchNotificationResult(
notificationStatus: NotificationStatus = NotificationStatus.Event(aRustNotificationItem()),
) = org.matrix.rustcomponents.sdk.BatchNotificationResult.Ok(
status = notificationStatus,
)
fun aRustNotificationSenderInfo(
displayName: String? = A_USER_NAME,
avatarUrl: String? = null,

View file

@ -7,16 +7,16 @@
package io.element.android.libraries.matrix.impl.fixtures.fakes
import org.matrix.rustcomponents.sdk.BatchNotificationResult
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.NotificationClient
import org.matrix.rustcomponents.sdk.NotificationItem
import org.matrix.rustcomponents.sdk.NotificationItemsRequest
class FakeFfiNotificationClient(
var notificationItemResult: Map<String, NotificationItem> = emptyMap(),
var notificationItemResult: Map<String, BatchNotificationResult> = emptyMap(),
val closeResult: () -> Unit = { }
) : NotificationClient(NoPointer) {
override suspend fun getNotifications(requests: List<NotificationItemsRequest>): Map<String, NotificationItem> {
override suspend fun getNotifications(requests: List<NotificationItemsRequest>): Map<String, BatchNotificationResult> {
return notificationItemResult
}

View file

@ -8,9 +8,10 @@
package io.element.android.libraries.matrix.impl.notification
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.exception.NotificationResolverException
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustNotificationItem
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustBatchNotificationResult
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiNotificationClient
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_MESSAGE
@ -30,12 +31,12 @@ class RustNotificationServiceTest {
@Test
fun test() = runTest {
val notificationClient = FakeFfiNotificationClient(
notificationItemResult = mapOf(AN_EVENT_ID.value to aRustNotificationItem()),
notificationItemResult = mapOf(AN_EVENT_ID.value to aRustBatchNotificationResult()),
)
val sut = createRustNotificationService(
notificationClient = notificationClient,
)
val result = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID))).getOrThrow()[AN_EVENT_ID]!!
val result = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID))).getOrThrow()[AN_EVENT_ID]!!.getOrThrow()
assertThat(result.isEncrypted).isTrue()
assertThat(result.content).isEqualTo(
NotificationContent.MessageLike.RoomMessage(
@ -56,10 +57,8 @@ class RustNotificationServiceTest {
val sut = createRustNotificationService(
notificationClient = notificationClient,
)
val result = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID))).getOrThrow()[AN_EVENT_ID]!!
assertThat(result.content).isEqualTo(
NotificationContent.MessageLike.UnableToResolve
)
val exception = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID))).getOrThrow()[AN_EVENT_ID]!!.exceptionOrNull()
assertThat(exception).isInstanceOf(NotificationResolverException::class.java)
}
@Test

View file

@ -13,13 +13,13 @@ import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.notification.NotificationService
class FakeNotificationService : NotificationService {
private var getNotificationsResult: Result<Map<EventId, NotificationData>> = Result.success(emptyMap())
private var getNotificationsResult: Result<Map<EventId, Result<NotificationData>>> = Result.success(emptyMap())
fun givenGetNotificationsResult(result: Result<Map<EventId, NotificationData>>) {
fun givenGetNotificationsResult(result: Result<Map<EventId, Result<NotificationData>>>) {
getNotificationsResult = result
}
override suspend fun getNotifications(ids: Map<RoomId, List<EventId>>): Result<Map<EventId, NotificationData>> {
override suspend fun getNotifications(ids: Map<RoomId, List<EventId>>): Result<Map<EventId, Result<NotificationData>>> {
return getNotificationsResult
}
}