Skip to content

Commit 911fba7

Browse files
authored
Merge pull request #4889 from element-hq/feature/bma/genericNotification
Show generic notification when Event cannot be resolved
2 parents a8d96a6 + 3d8a641 commit 911fba7

File tree

17 files changed

+197
-12
lines changed

17 files changed

+197
-12
lines changed

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class DefaultClearCacheUseCase @Inject constructor(
5959
seenInvitesStore.clear()
6060
// Ensure any error will be displayed again
6161
pushService.setIgnoreRegistrationError(matrixClient.sessionId, false)
62+
pushService.resetBatteryOptimizationState()
6263
// Ensure the app is restarted
6364
defaultCacheService.onClearedCache(matrixClient.sessionId)
6465
}

features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/tasks/DefaultClearCacheUseCaseTest.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ class DefaultClearCacheUseCaseTest {
4646
resetLambda = resetFtueLambda,
4747
)
4848
val setIgnoreRegistrationErrorLambda = lambdaRecorder<SessionId, Boolean, Unit> { _, _ -> }
49+
val resetBatteryOptimizationStateResult = lambdaRecorder<Unit> { }
4950
val pushService = FakePushService(
50-
setIgnoreRegistrationErrorLambda = setIgnoreRegistrationErrorLambda
51+
setIgnoreRegistrationErrorLambda = setIgnoreRegistrationErrorLambda,
52+
resetBatteryOptimizationStateResult = resetBatteryOptimizationStateResult,
5153
)
5254
val seenInvitesStore = InMemorySeenInvitesStore(setOf(A_ROOM_ID))
5355
assertThat(seenInvitesStore.seenRoomIds().first()).isNotEmpty()
@@ -68,6 +70,7 @@ class DefaultClearCacheUseCaseTest {
6870
resetFtueLambda.assertions().isCalledOnce()
6971
setIgnoreRegistrationErrorLambda.assertions().isCalledOnce()
7072
.with(value(matrixClient.sessionId), value(false))
73+
resetBatteryOptimizationStateResult.assertions().isCalledOnce()
7174
assertThat(awaitItem()).isEqualTo(matrixClient.sessionId)
7275
assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty()
7376
assertThat(activeRoomsHolder.getActiveRoom(A_SESSION_ID)).isNull()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ sealed interface NotificationContent {
6868
) : MessageLike
6969

7070
data object RoomEncrypted : MessageLike
71+
data object UnableToResolve : MessageLike
7172
data class RoomMessage(
7273
val senderId: UserId,
7374
val messageType: MessageType

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
1212
import io.element.android.libraries.matrix.api.core.EventId
1313
import io.element.android.libraries.matrix.api.core.RoomId
1414
import io.element.android.libraries.matrix.api.core.SessionId
15+
import io.element.android.libraries.matrix.api.notification.NotificationContent
1516
import io.element.android.libraries.matrix.api.notification.NotificationData
1617
import io.element.android.libraries.matrix.api.notification.NotificationService
1718
import io.element.android.services.toolbox.api.systemclock.SystemClock
@@ -24,7 +25,7 @@ class RustNotificationService(
2425
private val sessionId: SessionId,
2526
private val notificationClient: NotificationClient,
2627
private val dispatchers: CoroutineDispatchers,
27-
clock: SystemClock,
28+
private val clock: SystemClock,
2829
) : NotificationService {
2930
private val notificationMapper: NotificationMapper = NotificationMapper(clock)
3031

@@ -43,11 +44,32 @@ class RustNotificationService(
4344
val eventIds = requests.flatMap { it.eventIds }
4445
for (eventId in eventIds) {
4546
val item = items[eventId]
47+
val roomId = RoomId(requests.find { it.eventIds.contains(eventId) }?.roomId!!)
4648
if (item != null) {
47-
val roomId = RoomId(requests.find { it.eventIds.contains(eventId) }?.roomId!!)
4849
put(EventId(eventId), notificationMapper.map(sessionId, EventId(eventId), roomId, item))
4950
} else {
5051
Timber.e("Could not retrieve event for notification with $eventId")
52+
put(
53+
EventId(eventId),
54+
NotificationData(
55+
sessionId = sessionId,
56+
eventId = EventId(eventId),
57+
threadId = null,
58+
roomId = roomId,
59+
senderAvatarUrl = null,
60+
senderDisplayName = null,
61+
senderIsNameAmbiguous = false,
62+
roomAvatarUrl = null,
63+
roomDisplayName = null,
64+
isDirect = false,
65+
isDm = false,
66+
isEncrypted = false,
67+
isNoisy = false,
68+
timestamp = clock.epochMillis(),
69+
content = NotificationContent.MessageLike.UnableToResolve,
70+
hasMention = false
71+
)
72+
)
5173
}
5274
}
5375
}

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiNotificationClient.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ import org.matrix.rustcomponents.sdk.NotificationItemsRequest
1414

1515
class FakeFfiNotificationClient(
1616
var notificationItemResult: Map<String, NotificationItem> = emptyMap(),
17+
val closeResult: () -> Unit = { }
1718
) : NotificationClient(NoPointer) {
1819
override suspend fun getNotifications(requests: List<NotificationItemsRequest>): Map<String, NotificationItem> {
1920
return notificationItemResult
2021
}
22+
23+
override fun close() = closeResult()
2124
}

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationServiceTest.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
1919
import io.element.android.libraries.matrix.test.A_USER_ID_2
2020
import io.element.android.services.toolbox.api.systemclock.SystemClock
2121
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
22+
import io.element.android.tests.testutils.lambda.lambdaRecorder
2223
import io.element.android.tests.testutils.testCoroutineDispatchers
2324
import kotlinx.coroutines.test.TestScope
2425
import kotlinx.coroutines.test.runTest
@@ -47,6 +48,33 @@ class RustNotificationServiceTest {
4748
)
4849
}
4950

51+
@Test
52+
fun `test unable to resolve event`() = runTest {
53+
val notificationClient = FakeFfiNotificationClient(
54+
notificationItemResult = emptyMap(),
55+
)
56+
val sut = createRustNotificationService(
57+
notificationClient = notificationClient,
58+
)
59+
val result = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID))).getOrThrow()[AN_EVENT_ID]!!
60+
assertThat(result.content).isEqualTo(
61+
NotificationContent.MessageLike.UnableToResolve
62+
)
63+
}
64+
65+
@Test
66+
fun `close should invoke the close method of the service`() = runTest {
67+
val closeResult = lambdaRecorder<Unit> { }
68+
val notificationClient = FakeFfiNotificationClient(
69+
closeResult = closeResult,
70+
)
71+
val sut = createRustNotificationService(
72+
notificationClient = notificationClient,
73+
)
74+
sut.close()
75+
closeResult.assertions().isCalledOnce()
76+
}
77+
5078
private fun TestScope.createRustNotificationService(
5179
notificationClient: NotificationClient = FakeFfiNotificationClient(),
5280
clock: SystemClock = FakeSystemClock(),

libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,9 @@ interface PushService {
6767
* Reset the push history, including the push counter.
6868
*/
6969
suspend fun resetPushHistory()
70+
71+
/**
72+
* Reset the battery optimization state.
73+
*/
74+
suspend fun resetBatteryOptimizationState()
7075
}

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.core.SessionId
1515
import io.element.android.libraries.push.api.GetCurrentPushProvider
1616
import io.element.android.libraries.push.api.PushService
1717
import io.element.android.libraries.push.api.history.PushHistoryItem
18+
import io.element.android.libraries.push.impl.push.MutableBatteryOptimizationStore
1819
import io.element.android.libraries.push.impl.store.PushDataStore
1920
import io.element.android.libraries.push.impl.test.TestPush
2021
import io.element.android.libraries.pushproviders.api.Distributor
@@ -37,6 +38,7 @@ class DefaultPushService @Inject constructor(
3738
private val sessionObserver: SessionObserver,
3839
private val pushClientSecretStore: PushClientSecretStore,
3940
private val pushDataStore: PushDataStore,
41+
private val mutableBatteryOptimizationStore: MutableBatteryOptimizationStore,
4042
) : PushService, SessionListener {
4143
init {
4244
observeSessions()
@@ -138,4 +140,8 @@ class DefaultPushService @Inject constructor(
138140
override suspend fun resetPushHistory() {
139141
pushDataStore.reset()
140142
}
143+
144+
override suspend fun resetBatteryOptimizationState() {
145+
mutableBatteryOptimizationStore.reset()
146+
}
141147
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ class DefaultNotifiableEventResolver @Inject constructor(
225225
val fallbackNotifiableEvent = fallbackNotifiableEvent(userId, roomId, eventId)
226226
ResolvedPushEvent.Event(fallbackNotifiableEvent)
227227
}
228+
NotificationContent.MessageLike.UnableToResolve -> {
229+
Timber.tag(loggerTag.value).w("Unable to resolve notification -> fallback")
230+
val fallbackNotifiableEvent = fallbackNotifiableEvent(userId, roomId, eventId)
231+
ResolvedPushEvent.Event(fallbackNotifiableEvent)
232+
}
228233
is NotificationContent.MessageLike.RoomRedaction -> {
229234
// Note: this case will be handled below
230235
val redactedEventId = content.redactedEventId

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import io.element.android.libraries.push.impl.history.onUnableToRetrieveSession
2525
import io.element.android.libraries.push.impl.notifications.NotificationEventRequest
2626
import io.element.android.libraries.push.impl.notifications.NotificationResolverQueue
2727
import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels
28+
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
2829
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
2930
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
3031
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
@@ -87,13 +88,24 @@ class DefaultPushHandler @Inject constructor(
8788
} else {
8889
result.fold(
8990
onSuccess = {
90-
pushHistoryService.onSuccess(
91-
providerInfo = request.providerInfo,
92-
eventId = request.eventId,
93-
roomId = request.roomId,
94-
sessionId = request.sessionId,
95-
comment = "Push handled successfully",
96-
)
91+
if (it is ResolvedPushEvent.Event && it.notifiableEvent is FallbackNotifiableEvent) {
92+
pushHistoryService.onUnableToResolveEvent(
93+
providerInfo = request.providerInfo,
94+
eventId = request.eventId,
95+
roomId = request.roomId,
96+
sessionId = request.sessionId,
97+
reason = "Showing fallback notification",
98+
)
99+
mutableBatteryOptimizationStore.showBatteryOptimizationBanner()
100+
} else {
101+
pushHistoryService.onSuccess(
102+
providerInfo = request.providerInfo,
103+
eventId = request.eventId,
104+
roomId = request.roomId,
105+
sessionId = request.sessionId,
106+
comment = "Push handled successfully",
107+
)
108+
}
97109
},
98110
onFailure = { exception ->
99111
pushHistoryService.onUnableToResolveEvent(

0 commit comments

Comments
 (0)