Notification: implement a counter in the fallback notification.
This commit is contained in:
parent
cd9a1febc4
commit
fe7e67c683
19 changed files with 126 additions and 97 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,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.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(
|
||||
|
|
@ -46,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
|
||||
}
|
||||
|
|
@ -66,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>,
|
||||
|
|
@ -141,25 +138,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),
|
||||
isNoisy = false,
|
||||
timestamp = event.timestamp
|
||||
)
|
||||
}
|
||||
): OneShotNotification? {
|
||||
if (fallback.isEmpty()) return null
|
||||
val existingNotification = activeNotificationsProvider
|
||||
.getFallbackNotification(notificationAccountParams.user.userId)
|
||||
?.notification
|
||||
val notification = notificationCreator.createFallbackNotification(
|
||||
existingNotification,
|
||||
notificationAccountParams,
|
||||
fallback,
|
||||
)
|
||||
return OneShotNotification(
|
||||
tag = "FALLBACK",
|
||||
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 {
|
||||
|
|
@ -169,7 +172,6 @@ class DefaultNotificationDataFactory(
|
|||
roomNotifications = roomNotifications,
|
||||
invitationNotifications = invitationNotifications,
|
||||
simpleNotifications = simpleNotifications,
|
||||
fallbackNotifications = fallbackNotifications,
|
||||
notificationAccountParams = notificationAccountParams,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
@ -240,11 +241,13 @@ class DefaultNotificationCreator(
|
|||
.addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent))
|
||||
.addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent))
|
||||
// Build the pending intent for when the notification is clicked
|
||||
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(
|
||||
sessionId = inviteNotifiableEvent.sessionId,
|
||||
roomId = inviteNotifiableEvent.roomId,
|
||||
eventId = null,
|
||||
))
|
||||
.setContentIntent(
|
||||
pendingIntentFactory.createOpenRoomPendingIntent(
|
||||
sessionId = inviteNotifiableEvent.sessionId,
|
||||
roomId = inviteNotifiableEvent.roomId,
|
||||
eventId = null,
|
||||
)
|
||||
)
|
||||
.apply {
|
||||
if (inviteNotifiableEvent.noisy) {
|
||||
// Compat
|
||||
|
|
@ -276,12 +279,14 @@ class DefaultNotificationCreator(
|
|||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
.configureWith(notificationAccountParams)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(
|
||||
sessionId = simpleNotifiableEvent.sessionId,
|
||||
roomId = simpleNotifiableEvent.roomId,
|
||||
eventId = null,
|
||||
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
|
||||
))
|
||||
.setContentIntent(
|
||||
pendingIntentFactory.createOpenRoomPendingIntent(
|
||||
sessionId = simpleNotifiableEvent.sessionId,
|
||||
roomId = simpleNotifiableEvent.roomId,
|
||||
eventId = null,
|
||||
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
|
||||
)
|
||||
)
|
||||
.apply {
|
||||
if (simpleNotifiableEvent.noisy) {
|
||||
// Compat
|
||||
|
|
@ -295,28 +300,35 @@ class DefaultNotificationCreator(
|
|||
}
|
||||
|
||||
override fun createFallbackNotification(
|
||||
existingNotification: Notification?,
|
||||
notificationAccountParams: NotificationAccountParams,
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
fallbackNotifiableEvents: List<FallbackNotifiableEvent>,
|
||||
): Notification {
|
||||
val channelId = notificationChannels.getChannelIdForMessage(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()
|
||||
}
|
||||
|
|
@ -503,6 +515,7 @@ class DefaultNotificationCreator(
|
|||
|
||||
companion object {
|
||||
const val MESSAGE_EVENT_ID = "message_event_id"
|
||||
private const val FALLBACK_COUNTER_EXTRA = "COUNTER"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue