Browse Source

Notifications: render current user name and avatar.

feature/fga/small_timeline_improvements
Benoit Marty 1 year ago committed by Benoit Marty
parent
commit
bc9ec5a28a
  1. 45
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt
  2. 14
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt
  3. 54
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt
  4. 12
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
  5. 12
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt
  6. 12
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt

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

@ -16,21 +16,21 @@ @@ -16,21 +16,21 @@
package io.element.android.libraries.push.impl.notifications
import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import androidx.annotation.WorkerThread
import io.element.android.libraries.androidutils.throttler.FirstThrottler
import io.element.android.libraries.core.cache.CircularCache
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
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.ThreadId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.push.api.store.PushDataStore
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.NotifiableMessageEvent
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMessageEventInRoom
@ -38,6 +38,7 @@ import io.element.android.services.appnavstate.api.AppNavigationState @@ -38,6 +38,7 @@ import io.element.android.services.appnavstate.api.AppNavigationState
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import javax.inject.Inject
@ -48,7 +49,6 @@ import javax.inject.Inject @@ -48,7 +49,6 @@ import javax.inject.Inject
*/
@SingleIn(AppScope::class)
class NotificationDrawerManager @Inject constructor(
@ApplicationContext context: Context,
private val pushDataStore: PushDataStore,
private val notifiableEventProcessor: NotifiableEventProcessor,
private val notificationRenderer: NotificationRenderer,
@ -57,6 +57,7 @@ class NotificationDrawerManager @Inject constructor( @@ -57,6 +57,7 @@ class NotificationDrawerManager @Inject constructor(
private val appNavigationStateService: AppNavigationStateService,
private val coroutineScope: CoroutineScope,
private val buildMeta: BuildMeta,
private val matrixAuthenticationService: MatrixAuthenticationService,
) {
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
@ -66,7 +67,6 @@ class NotificationDrawerManager @Inject constructor( @@ -66,7 +67,6 @@ class NotificationDrawerManager @Inject constructor(
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events.
*/
private val notificationState by lazy { createInitialNotificationState() }
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
private var currentAppNavigationState: AppNavigationState? = null
private val firstThrottler = FirstThrottler(200)
@ -239,6 +239,7 @@ class NotificationDrawerManager @Inject constructor( @@ -239,6 +239,7 @@ class NotificationDrawerManager @Inject constructor(
}
}
@WorkerThread
private fun renderEvents(eventsToRender: List<ProcessedEvent<NotifiableEvent>>) {
// Group by sessionId
val eventsForSessions = eventsToRender.groupBy {
@ -246,17 +247,29 @@ class NotificationDrawerManager @Inject constructor( @@ -246,17 +247,29 @@ class NotificationDrawerManager @Inject constructor(
}
eventsForSessions.forEach { (sessionId, notifiableEvents) ->
// TODO EAx val user = session.getUserOrDefault(session.myUserId)
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
val myUserDisplayName = "Todo display name" // user.toMatrixItem().getBestName()
// TODO EAx avatar URL
val myUserAvatarUrl = null // session.contentUrlResolver().resolveThumbnail(
// contentUrl = user.avatarUrl,
// width = avatarSize,
// height = avatarSize,
// method = ContentUrlResolver.ThumbnailMethod.SCALE
//)
notificationRenderer.render(sessionId, myUserDisplayName, myUserAvatarUrl, useCompleteNotificationFormat, notifiableEvents)
val currentUser = tryOrNull(
onError = { Timber.e(it, "Unable to retrieve info for user ${sessionId.value}") },
operation = {
runBlocking {
val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull()
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
val myUserDisplayName = client?.loadUserDisplayName()?.getOrNull() ?: sessionId.value
val userAvatarUrl = client?.loadUserAvatarURLString()?.getOrNull()
MatrixUser(
userId = sessionId,
displayName = myUserDisplayName,
avatarUrl = userAvatarUrl
)
}
}
) ?: MatrixUser(
userId = sessionId,
displayName = sessionId.value,
avatarUrl = null
)
notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents)
}
}

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

@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications
import android.app.Notification
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.user.MatrixUser
import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
@ -35,9 +35,7 @@ class NotificationFactory @Inject constructor( @@ -35,9 +35,7 @@ class NotificationFactory @Inject constructor(
) {
fun Map<RoomId, ProcessedMessageEvents>.toNotifications(
sessionId: SessionId,
myUserDisplayName: String,
myUserAvatarUrl: String?
currentUser: MatrixUser,
): List<RoomNotification> {
return map { (roomId, events) ->
when {
@ -45,11 +43,9 @@ class NotificationFactory @Inject constructor( @@ -45,11 +43,9 @@ class NotificationFactory @Inject constructor(
else -> {
val messageEvents = events.onlyKeptEvents().filterNot { it.isRedacted }
roomGroupMessageCreator.createRoomMessage(
sessionId = sessionId,
currentUser = currentUser,
events = messageEvents,
roomId = roomId,
userDisplayName = myUserDisplayName,
userAvatarUrl = myUserAvatarUrl
)
}
}
@ -99,7 +95,7 @@ class NotificationFactory @Inject constructor( @@ -99,7 +95,7 @@ class NotificationFactory @Inject constructor(
}
fun createSummaryNotification(
sessionId: SessionId,
currentUser: MatrixUser,
roomNotifications: List<RoomNotification>,
invitationNotifications: List<OneShotNotification>,
simpleNotifications: List<OneShotNotification>,
@ -112,7 +108,7 @@ class NotificationFactory @Inject constructor( @@ -112,7 +108,7 @@ class NotificationFactory @Inject constructor(
roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed
else -> SummaryNotification.Update(
summaryGroupMessageCreator.createSummaryNotification(
sessionId = sessionId,
currentUser = currentUser,
roomNotifications = roomMeta,
invitationNotifications = invitationMeta,
simpleNotifications = simpleMeta,

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

@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications
import androidx.annotation.WorkerThread
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.user.MatrixUser
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.NotifiableMessageEvent
@ -34,19 +34,17 @@ class NotificationRenderer @Inject constructor( @@ -34,19 +34,17 @@ class NotificationRenderer @Inject constructor(
@WorkerThread
fun render(
sessionId: SessionId,
myUserDisplayName: String,
myUserAvatarUrl: String?,
currentUser: MatrixUser,
useCompleteNotificationFormat: Boolean,
eventsToProcess: List<ProcessedEvent<NotifiableEvent>>
) {
val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType()
with(notificationFactory) {
val roomNotifications = roomEvents.toNotifications(sessionId, myUserDisplayName, myUserAvatarUrl)
val roomNotifications = roomEvents.toNotifications(currentUser)
val invitationNotifications = invitationEvents.toNotifications()
val simpleNotifications = simpleEvents.toNotifications()
val summaryNotification = createSummaryNotification(
sessionId = sessionId,
currentUser = currentUser,
roomNotifications = roomNotifications,
invitationNotifications = invitationNotifications,
simpleNotifications = simpleNotifications,
@ -56,21 +54,27 @@ class NotificationRenderer @Inject constructor( @@ -56,21 +54,27 @@ class NotificationRenderer @Inject constructor(
// Remove summary first to avoid briefly displaying it after dismissing the last notification
if (summaryNotification == SummaryNotification.Removed) {
Timber.d("Removing summary notification")
notificationDisplayer.cancelNotificationMessage(null, notificationIdProvider.getSummaryNotificationId(sessionId))
notificationDisplayer.cancelNotificationMessage(
tag = null,
id = notificationIdProvider.getSummaryNotificationId(currentUser.userId)
)
}
roomNotifications.forEach { wrapper ->
when (wrapper) {
is RoomNotification.Removed -> {
Timber.d("Removing room messages notification ${wrapper.roomId}")
notificationDisplayer.cancelNotificationMessage(wrapper.roomId.value, notificationIdProvider.getRoomMessagesNotificationId(sessionId))
notificationDisplayer.cancelNotificationMessage(
tag = wrapper.roomId.value,
id = notificationIdProvider.getRoomMessagesNotificationId(currentUser.userId)
)
}
is RoomNotification.Message -> if (useCompleteNotificationFormat) {
Timber.d("Updating room messages notification ${wrapper.meta.roomId}")
notificationDisplayer.showNotificationMessage(
wrapper.meta.roomId.value,
notificationIdProvider.getRoomMessagesNotificationId(sessionId),
wrapper.notification
tag = wrapper.meta.roomId.value,
id = notificationIdProvider.getRoomMessagesNotificationId(currentUser.userId),
notification = wrapper.notification
)
}
}
@ -80,14 +84,17 @@ class NotificationRenderer @Inject constructor( @@ -80,14 +84,17 @@ class NotificationRenderer @Inject constructor(
when (wrapper) {
is OneShotNotification.Removed -> {
Timber.d("Removing invitation notification ${wrapper.key}")
notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomInvitationNotificationId(sessionId))
notificationDisplayer.cancelNotificationMessage(
tag = wrapper.key,
id = notificationIdProvider.getRoomInvitationNotificationId(currentUser.userId)
)
}
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
Timber.d("Updating invitation notification ${wrapper.meta.key}")
notificationDisplayer.showNotificationMessage(
wrapper.meta.key,
notificationIdProvider.getRoomInvitationNotificationId(sessionId),
wrapper.notification
tag = wrapper.meta.key,
id = notificationIdProvider.getRoomInvitationNotificationId(currentUser.userId),
notification = wrapper.notification
)
}
}
@ -97,14 +104,17 @@ class NotificationRenderer @Inject constructor( @@ -97,14 +104,17 @@ class NotificationRenderer @Inject constructor(
when (wrapper) {
is OneShotNotification.Removed -> {
Timber.d("Removing simple notification ${wrapper.key}")
notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomEventNotificationId(sessionId))
notificationDisplayer.cancelNotificationMessage(
tag = wrapper.key,
id = notificationIdProvider.getRoomEventNotificationId(currentUser.userId)
)
}
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
Timber.d("Updating simple notification ${wrapper.meta.key}")
notificationDisplayer.showNotificationMessage(
wrapper.meta.key,
notificationIdProvider.getRoomEventNotificationId(sessionId),
wrapper.notification
tag = wrapper.meta.key,
id = notificationIdProvider.getRoomEventNotificationId(currentUser.userId),
notification = wrapper.notification
)
}
}
@ -114,9 +124,9 @@ class NotificationRenderer @Inject constructor( @@ -114,9 +124,9 @@ class NotificationRenderer @Inject constructor(
if (summaryNotification is SummaryNotification.Update) {
Timber.d("Updating summary notification")
notificationDisplayer.showNotificationMessage(
null,
notificationIdProvider.getSummaryNotificationId(sessionId),
summaryNotification.notification
tag = null,
id = notificationIdProvider.getSummaryNotificationId(currentUser.userId),
notification = summaryNotification.notification
)
}
}

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

@ -20,7 +20,7 @@ import android.graphics.Bitmap @@ -20,7 +20,7 @@ import android.graphics.Bitmap
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
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.user.MatrixUser
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
@ -37,19 +37,17 @@ class RoomGroupMessageCreator @Inject constructor( @@ -37,19 +37,17 @@ class RoomGroupMessageCreator @Inject constructor(
) {
fun createRoomMessage(
sessionId: SessionId,
currentUser: MatrixUser,
events: List<NotifiableMessageEvent>,
roomId: RoomId,
userDisplayName: String,
userAvatarUrl: String?
): RoomNotification.Message {
val lastKnownRoomEvent = events.last()
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: "Room name (${roomId.value.take(8)}…)"
val roomIsGroup = !lastKnownRoomEvent.roomIsDirect
val style = NotificationCompat.MessagingStyle(
Person.Builder()
.setName(userDisplayName)
.setIcon(bitmapLoader.getUserIcon(userAvatarUrl))
.setName(currentUser.displayName)
.setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl))
.setKey(lastKnownRoomEvent.sessionId.value)
.build()
).also {
@ -80,7 +78,7 @@ class RoomGroupMessageCreator @Inject constructor( @@ -80,7 +78,7 @@ class RoomGroupMessageCreator @Inject constructor(
notificationFactory.createMessagesListNotification(
style,
RoomEventGroupInfo(
sessionId = sessionId,
sessionId = currentUser.userId,
roomId = roomId,
roomDisplayName = roomName,
isDirect = !roomIsGroup,

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

@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications
import android.app.Notification
import androidx.core.app.NotificationCompat
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory
import io.element.android.services.toolbox.api.strings.StringProvider
@ -44,7 +44,7 @@ class SummaryGroupMessageCreator @Inject constructor( @@ -44,7 +44,7 @@ class SummaryGroupMessageCreator @Inject constructor(
) {
fun createSummaryNotification(
sessionId: SessionId,
currentUser: MatrixUser,
roomNotifications: List<RoomNotification.Message.Meta>,
invitationNotifications: List<OneShotNotification.Append.Meta>,
simpleNotifications: List<OneShotNotification.Append.Meta>,
@ -74,7 +74,7 @@ class SummaryGroupMessageCreator @Inject constructor( @@ -74,7 +74,7 @@ class SummaryGroupMessageCreator @Inject constructor(
.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
return if (useCompleteNotificationFormat) {
notificationFactory.createSummaryListNotification(
sessionId,
currentUser,
summaryInboxStyle,
sumTitle,
noisy = summaryIsNoisy,
@ -82,7 +82,7 @@ class SummaryGroupMessageCreator @Inject constructor( @@ -82,7 +82,7 @@ class SummaryGroupMessageCreator @Inject constructor(
)
} else {
processSimpleGroupSummary(
sessionId,
currentUser,
summaryIsNoisy,
messageCount,
simpleNotifications.size,
@ -94,7 +94,7 @@ class SummaryGroupMessageCreator @Inject constructor( @@ -94,7 +94,7 @@ class SummaryGroupMessageCreator @Inject constructor(
}
private fun processSimpleGroupSummary(
sessionId: SessionId,
currentUser: MatrixUser,
summaryIsNoisy: Boolean,
messageEventsCount: Int,
simpleEventsCount: Int,
@ -167,7 +167,7 @@ class SummaryGroupMessageCreator @Inject constructor( @@ -167,7 +167,7 @@ class SummaryGroupMessageCreator @Inject constructor(
}
}
return notificationFactory.createSummaryListNotification(
sessionId = sessionId,
currentUser = currentUser,
style = null,
compatSummary = privacyTitle,
noisy = summaryIsNoisy,

12
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt

@ -26,8 +26,8 @@ import androidx.core.content.ContextCompat @@ -26,8 +26,8 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo
import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels
@ -226,7 +226,7 @@ class NotificationFactory @Inject constructor( @@ -226,7 +226,7 @@ class NotificationFactory @Inject constructor(
* Create the summary notification.
*/
fun createSummaryListNotification(
sessionId: SessionId,
currentUser: MatrixUser,
style: NotificationCompat.InboxStyle?,
compatSummary: String,
noisy: Boolean,
@ -240,12 +240,12 @@ class NotificationFactory @Inject constructor( @@ -240,12 +240,12 @@ class NotificationFactory @Inject constructor(
// used in compat < N, after summary is built based on child notifications
.setWhen(lastMessageTimestamp)
.setStyle(style)
.setContentTitle(sessionId.value)
.setContentTitle(currentUser.userId.value)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setSmallIcon(smallIcon)
// set content text to support devices running API level < 24
.setContentText(compatSummary)
.setGroup(sessionId.value)
.setGroup(currentUser.userId.value)
// set this notification as the summary for the group
.setGroupSummary(true)
.setColor(accentColor)
@ -264,8 +264,8 @@ class NotificationFactory @Inject constructor( @@ -264,8 +264,8 @@ class NotificationFactory @Inject constructor(
priority = NotificationCompat.PRIORITY_LOW
}
}
.setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(sessionId))
.setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(sessionId))
.setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(currentUser.userId))
.setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(currentUser.userId))
.build()
}

Loading…
Cancel
Save