From bc9ec5a28a67250441d13a2cb840c3412fef0d48 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 10:59:11 +0200 Subject: [PATCH] Notifications: render current user name and avatar. --- .../NotificationDrawerManager.kt | 45 ++++++++++------ .../impl/notifications/NotificationFactory.kt | 14 ++--- .../notifications/NotificationRenderer.kt | 54 +++++++++++-------- .../notifications/RoomGroupMessageCreator.kt | 12 ++--- .../SummaryGroupMessageCreator.kt | 12 ++--- .../factories/NotificationFactory.kt | 12 ++--- 6 files changed, 83 insertions(+), 66 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt index cf0307fbd9..c3c5275eb5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt @@ -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 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 */ @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( 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( * 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( } } + @WorkerThread private fun renderEvents(eventsToRender: List>) { // Group by sessionId val eventsForSessions = eventsToRender.groupBy { @@ -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) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt index 4bb49e168f..96f5e5c81b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt +++ b/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 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( ) { fun Map.toNotifications( - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String? + currentUser: MatrixUser, ): List { return map { (roomId, events) -> when { @@ -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( } fun createSummaryNotification( - sessionId: SessionId, + currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, @@ -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, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 277dc3b822..33e15510f1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/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 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( @WorkerThread fun render( - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String?, + currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, eventsToProcess: List> ) { 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( // 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( 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( 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( 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 ) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 00222728bf..8024e0c6cb 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -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( ) { fun createRoomMessage( - sessionId: SessionId, + currentUser: MatrixUser, events: List, 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( notificationFactory.createMessagesListNotification( style, RoomEventGroupInfo( - sessionId = sessionId, + sessionId = currentUser.userId, roomId = roomId, roomDisplayName = roomName, isDirect = !roomIsGroup, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index a400c2b7a3..78c6ab3ee0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/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 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( ) { fun createSummaryNotification( - sessionId: SessionId, + currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, @@ -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( ) } else { processSimpleGroupSummary( - sessionId, + currentUser, summaryIsNoisy, messageCount, simpleNotifications.size, @@ -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( } } return notificationFactory.createSummaryListNotification( - sessionId = sessionId, + currentUser = currentUser, style = null, compatSummary = privacyTitle, noisy = summaryIsNoisy, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt index 5795ea5f5f..18bfdd8292 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt +++ b/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 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( * 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( // 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( priority = NotificationCompat.PRIORITY_LOW } } - .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(sessionId)) - .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(sessionId)) + .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(currentUser.userId)) + .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(currentUser.userId)) .build() }