Skip to content

Commit 346a24c

Browse files
authored
Merge pull request #6181 from element-hq/feature/bma/notificationFallbackCounter
Notification fallback counter
2 parents eb24053 + 3a86605 commit 346a24c

20 files changed

+155
-172
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface ActiveNotificationsProvider {
3434
fun getMembershipNotificationForSession(sessionId: SessionId): List<StatusBarNotification>
3535
fun getMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification>
3636
fun getSummaryNotification(sessionId: SessionId): StatusBarNotification?
37+
fun getFallbackNotification(sessionId: SessionId): StatusBarNotification?
3738
fun count(sessionId: SessionId): Int
3839
}
3940

@@ -76,6 +77,11 @@ class DefaultActiveNotificationsProvider(
7677
return getNotificationsForSession(sessionId).find { it.id == summaryId }
7778
}
7879

80+
override fun getFallbackNotification(sessionId: SessionId): StatusBarNotification? {
81+
val fallbackId = NotificationIdProvider.getFallbackNotificationId(sessionId)
82+
return getNotificationsForSession(sessionId).find { it.id == fallbackId }
83+
}
84+
7985
override fun count(sessionId: SessionId): Int {
8086
return getNotificationsForSession(sessionId).size
8187
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,12 @@ import dev.zacsweers.metro.Inject
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.push.impl.R
1615
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
17-
import io.element.android.services.toolbox.api.strings.StringProvider
1816
import io.element.android.services.toolbox.api.systemclock.SystemClock
1917

2018
@Inject
2119
class FallbackNotificationFactory(
2220
private val clock: SystemClock,
23-
private val stringProvider: StringProvider,
2421
) {
2522
fun create(
2623
sessionId: SessionId,
@@ -36,7 +33,7 @@ class FallbackNotificationFactory(
3633
isRedacted = false,
3734
isUpdated = false,
3835
timestamp = clock.epochMillis(),
39-
description = stringProvider.getString(R.string.notification_fallback_content),
36+
description = "",
4037
cause = cause,
4138
)
4239
}

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

Lines changed: 19 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,18 @@
99
package io.element.android.libraries.push.impl.notifications
1010

1111
import android.app.Notification
12-
import android.graphics.Typeface
13-
import android.text.style.StyleSpan
14-
import androidx.core.text.buildSpannedString
15-
import androidx.core.text.inSpans
1612
import coil3.ImageLoader
1713
import dev.zacsweers.metro.AppScope
1814
import dev.zacsweers.metro.ContributesBinding
1915
import io.element.android.libraries.matrix.api.core.RoomId
2016
import io.element.android.libraries.matrix.api.core.SessionId
2117
import io.element.android.libraries.matrix.api.core.ThreadId
22-
import io.element.android.libraries.push.impl.R
2318
import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams
2419
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
2520
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
2621
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
2722
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
2823
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
29-
import io.element.android.services.toolbox.api.strings.StringProvider
3024

3125
interface NotificationDataFactory {
3226
suspend fun toNotifications(
@@ -51,16 +45,15 @@ interface NotificationDataFactory {
5145

5246
@JvmName("toNotificationFallbackEvents")
5347
@Suppress("INAPPLICABLE_JVM_NAME")
54-
fun toNotifications(
48+
fun toNotification(
5549
fallback: List<FallbackNotifiableEvent>,
5650
notificationAccountParams: NotificationAccountParams,
57-
): List<OneShotNotification>
51+
): OneShotNotification?
5852

5953
fun createSummaryNotification(
6054
roomNotifications: List<RoomNotification>,
6155
invitationNotifications: List<OneShotNotification>,
6256
simpleNotifications: List<OneShotNotification>,
63-
fallbackNotifications: List<OneShotNotification>,
6457
notificationAccountParams: NotificationAccountParams,
6558
): SummaryNotification
6659
}
@@ -71,7 +64,6 @@ class DefaultNotificationDataFactory(
7164
private val roomGroupMessageCreator: RoomGroupMessageCreator,
7265
private val summaryGroupMessageCreator: SummaryGroupMessageCreator,
7366
private val activeNotificationsProvider: ActiveNotificationsProvider,
74-
private val stringProvider: StringProvider,
7567
) : NotificationDataFactory {
7668
override suspend fun toNotifications(
7769
messages: List<NotifiableMessageEvent>,
@@ -81,10 +73,7 @@ class DefaultNotificationDataFactory(
8173
val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() }
8274
.groupBy { it.roomId }
8375
return messagesToDisplay.flatMap { (roomId, events) ->
84-
val roomName = events.lastOrNull()?.roomName ?: roomId.value
85-
val isDm = events.lastOrNull()?.roomIsDm ?: false
8676
val eventsByThreadId = events.groupBy { it.threadId }
87-
8877
eventsByThreadId.map { (threadId, events) ->
8978
val notification = roomGroupMessageCreator.createRoomMessage(
9079
events = events,
@@ -98,7 +87,6 @@ class DefaultNotificationDataFactory(
9887
notification = notification,
9988
roomId = roomId,
10089
threadId = threadId,
101-
summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, isDm),
10290
messageCount = events.size,
10391
latestTimestamp = events.maxOf { it.timestamp },
10492
shouldBing = events.any { it.noisy }
@@ -123,7 +111,6 @@ class DefaultNotificationDataFactory(
123111
OneShotNotification(
124112
tag = event.roomId.value,
125113
notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event),
126-
summaryLine = event.description,
127114
isNoisy = event.noisy,
128115
timestamp = event.timestamp
129116
)
@@ -140,7 +127,6 @@ class DefaultNotificationDataFactory(
140127
OneShotNotification(
141128
tag = event.eventId.value,
142129
notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event),
143-
summaryLine = event.description,
144130
isNoisy = event.noisy,
145131
timestamp = event.timestamp
146132
)
@@ -149,26 +135,31 @@ class DefaultNotificationDataFactory(
149135

150136
@JvmName("toNotificationFallbackEvents")
151137
@Suppress("INAPPLICABLE_JVM_NAME")
152-
override fun toNotifications(
138+
override fun toNotification(
153139
fallback: List<FallbackNotifiableEvent>,
154140
notificationAccountParams: NotificationAccountParams,
155-
): List<OneShotNotification> {
156-
return fallback.map { event ->
157-
OneShotNotification(
158-
tag = event.eventId.value,
159-
notification = notificationCreator.createFallbackNotification(notificationAccountParams, event),
160-
summaryLine = event.description.orEmpty(),
161-
isNoisy = false,
162-
timestamp = event.timestamp
163-
)
164-
}
141+
): OneShotNotification? {
142+
if (fallback.isEmpty()) return null
143+
val existingNotification = activeNotificationsProvider
144+
.getFallbackNotification(notificationAccountParams.user.userId)
145+
?.notification
146+
val notification = notificationCreator.createFallbackNotification(
147+
existingNotification,
148+
notificationAccountParams,
149+
fallback,
150+
)
151+
return OneShotNotification(
152+
tag = "FALLBACK",
153+
notification = notification,
154+
isNoisy = false,
155+
timestamp = fallback.first().timestamp
156+
)
165157
}
166158

167159
override fun createSummaryNotification(
168160
roomNotifications: List<RoomNotification>,
169161
invitationNotifications: List<OneShotNotification>,
170162
simpleNotifications: List<OneShotNotification>,
171-
fallbackNotifications: List<OneShotNotification>,
172163
notificationAccountParams: NotificationAccountParams,
173164
): SummaryNotification {
174165
return when {
@@ -178,59 +169,17 @@ class DefaultNotificationDataFactory(
178169
roomNotifications = roomNotifications,
179170
invitationNotifications = invitationNotifications,
180171
simpleNotifications = simpleNotifications,
181-
fallbackNotifications = fallbackNotifications,
182172
notificationAccountParams = notificationAccountParams,
183173
)
184174
)
185175
}
186176
}
187-
188-
private fun createRoomMessagesGroupSummaryLine(events: List<NotifiableMessageEvent>, roomName: String, roomIsDm: Boolean): CharSequence {
189-
return when (events.size) {
190-
1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDm)
191-
else -> {
192-
stringProvider.getQuantityString(
193-
R.plurals.notification_compat_summary_line_for_room,
194-
events.size,
195-
roomName,
196-
events.size
197-
)
198-
}
199-
}
200-
}
201-
202-
private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDm: Boolean): CharSequence {
203-
return if (roomIsDm) {
204-
buildSpannedString {
205-
event.senderDisambiguatedDisplayName?.let {
206-
inSpans(StyleSpan(Typeface.BOLD)) {
207-
append(it)
208-
append(": ")
209-
}
210-
}
211-
append(event.description)
212-
}
213-
} else {
214-
buildSpannedString {
215-
inSpans(StyleSpan(Typeface.BOLD)) {
216-
append(roomName)
217-
append(": ")
218-
event.senderDisambiguatedDisplayName?.let {
219-
append(it)
220-
append(" ")
221-
}
222-
}
223-
append(event.description)
224-
}
225-
}
226-
}
227177
}
228178

229179
data class RoomNotification(
230180
val notification: Notification,
231181
val roomId: RoomId,
232182
val threadId: ThreadId?,
233-
val summaryLine: CharSequence,
234183
val messageCount: Int,
235184
val latestTimestamp: Long,
236185
val shouldBing: Boolean,
@@ -239,7 +188,6 @@ data class RoomNotification(
239188
return notification == other.notification &&
240189
roomId == other.roomId &&
241190
threadId == other.threadId &&
242-
summaryLine.toString() == other.summaryLine.toString() &&
243191
messageCount == other.messageCount &&
244192
latestTimestamp == other.latestTimestamp &&
245193
shouldBing == other.shouldBing
@@ -249,7 +197,6 @@ data class RoomNotification(
249197
data class OneShotNotification(
250198
val notification: Notification,
251199
val tag: String,
252-
val summaryLine: CharSequence,
253200
val isNoisy: Boolean,
254201
val timestamp: Long,
255202
)

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,11 @@ class NotificationRenderer(
5555
val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, imageLoader, notificationAccountParams)
5656
val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, notificationAccountParams)
5757
val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, notificationAccountParams)
58-
val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, notificationAccountParams)
58+
val fallbackNotification = notificationDataFactory.toNotification(groupedEvents.fallbackEvents, notificationAccountParams)
5959
val summaryNotification = notificationDataFactory.createSummaryNotification(
6060
roomNotifications = roomNotifications,
6161
invitationNotifications = invitationNotifications,
6262
simpleNotifications = simpleNotifications,
63-
fallbackNotifications = fallbackNotifications,
6463
notificationAccountParams = notificationAccountParams,
6564
)
6665

@@ -107,13 +106,12 @@ class NotificationRenderer(
107106
}
108107
}
109108

110-
// Show only the first fallback notification
111-
if (fallbackNotifications.isNotEmpty()) {
112-
Timber.tag(loggerTag.value).d("Showing fallback notification")
109+
if (fallbackNotification != null) {
110+
Timber.tag(loggerTag.value).d("Showing or updating fallback notification")
113111
notificationDisplayer.showNotification(
114-
tag = "FALLBACK",
112+
tag = fallbackNotification.tag,
115113
id = NotificationIdProvider.getFallbackNotificationId(currentUser.userId),
116-
notification = fallbackNotifications.first().notification
114+
notification = fallbackNotification.notification,
117115
)
118116
}
119117

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ interface SummaryGroupMessageCreator {
2222
roomNotifications: List<RoomNotification>,
2323
invitationNotifications: List<OneShotNotification>,
2424
simpleNotifications: List<OneShotNotification>,
25-
fallbackNotifications: List<OneShotNotification>,
2625
): Notification
2726
}
2827

@@ -45,7 +44,6 @@ class DefaultSummaryGroupMessageCreator(
4544
roomNotifications: List<RoomNotification>,
4645
invitationNotifications: List<OneShotNotification>,
4746
simpleNotifications: List<OneShotNotification>,
48-
fallbackNotifications: List<OneShotNotification>,
4947
): Notification {
5048
val summaryIsNoisy = roomNotifications.any { it.shouldBing } ||
5149
invitationNotifications.any { it.isNoisy } ||

0 commit comments

Comments
 (0)