From d0d6be3912607a1a2af4a719884d201ce19fd790 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 23 May 2023 18:53:22 +0200 Subject: [PATCH 01/12] Map some info for the notifications. --- .../api/notification/NotificationData.kt | 18 ++- .../impl/notification/NotificationMapper.kt | 8 +- .../notification/RustNotificationService.kt | 5 - .../impl/notification/TimelineEventMapper.kt | 111 ++++++++++++++++++ .../notifications/NotifiableEventResolver.kt | 24 ++-- 5 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 991f8dd117..60d889d046 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -20,15 +20,25 @@ 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.UserId -//TODO add content data class NotificationData( val senderId: UserId, val eventId: EventId, val roomId: RoomId, - val senderAvatarUrl: String? = null, - val senderDisplayName: String? = null, - val roomAvatarUrl: String? = null, + val senderAvatarUrl: String?, + val senderDisplayName: String?, + val roomAvatarUrl: String?, + val roomDisplayName: String?, val isDirect: Boolean, val isEncrypted: Boolean, val isNoisy: Boolean, + val event: NotificationEvent, +) + +data class NotificationEvent( + val eventId: EventId, + val senderId: UserId, + val timestamp: Long, + val content: String, + // For images for instance + val contentUrl: String? ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 4b121db9bf..e6125cf69b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -23,9 +23,9 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationData import org.matrix.rustcomponents.sdk.NotificationItem import org.matrix.rustcomponents.sdk.use -import javax.inject.Inject -class NotificationMapper @Inject constructor() { +class NotificationMapper { + private val timelineEventMapper = TimelineEventMapper() fun map(notificationItem: NotificationItem): NotificationData { return notificationItem.use { @@ -36,9 +36,11 @@ class NotificationMapper @Inject constructor() { senderAvatarUrl = it.senderAvatarUrl, senderDisplayName = it.senderDisplayName, roomAvatarUrl = it.roomAvatarUrl, + roomDisplayName = it.roomDisplayName, isDirect = it.isDirect, isEncrypted = it.isEncrypted.orFalse(), - isNoisy = it.isNoisy + isNoisy = it.isNoisy, + event = it.event.use { event -> timelineEventMapper.map(event) } ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index bd94de21fc..8b630cd64a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -16,18 +16,13 @@ package io.element.android.libraries.matrix.impl.notification -import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.api.MatrixClient 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.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService -import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.use -import java.io.File class RustNotificationService( private val client: Client, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt new file mode 100644 index 0000000000..3d2759c6d1 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.notification + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.notification.NotificationEvent +import org.matrix.rustcomponents.sdk.MessageLikeEventContent +import org.matrix.rustcomponents.sdk.MessageType +import org.matrix.rustcomponents.sdk.StateEventContent +import org.matrix.rustcomponents.sdk.TimelineEvent +import org.matrix.rustcomponents.sdk.TimelineEventType +import org.matrix.rustcomponents.sdk.use +import javax.inject.Inject + +class TimelineEventMapper @Inject constructor() { + + fun map(timelineEvent: TimelineEvent): NotificationEvent { + return timelineEvent.use { + NotificationEvent( + eventId = EventId(it.eventId()), + senderId = UserId(it.senderId()), + timestamp = it.timestamp().toLong(), + content = it.eventType().toContent(), + contentUrl = null // TODO it.eventType().toContentUrl(), + ) + } + } +} + +private fun TimelineEventType.toContent(): String { + return when (this) { + is TimelineEventType.MessageLike -> content.toContent() + is TimelineEventType.State -> content.toContent() + } +} + +private fun StateEventContent.toContent(): String { + return when (this) { + StateEventContent.PolicyRuleRoom -> "PolicyRuleRoom" + StateEventContent.PolicyRuleServer -> "PolicyRuleServer" + StateEventContent.PolicyRuleUser -> "PolicyRuleUser" + StateEventContent.RoomAliases -> "RoomAliases" + StateEventContent.RoomAvatar -> "RoomAvatar" + StateEventContent.RoomCanonicalAlias -> "RoomCanonicalAlias" + StateEventContent.RoomCreate -> "RoomCreate" + StateEventContent.RoomEncryption -> "RoomEncryption" + StateEventContent.RoomGuestAccess -> "RoomGuestAccess" + StateEventContent.RoomHistoryVisibility -> "RoomHistoryVisibility" + StateEventContent.RoomJoinRules -> "RoomJoinRules" + is StateEventContent.RoomMemberContent -> "$userId is now $membershipState" + StateEventContent.RoomName -> "RoomName" + StateEventContent.RoomPinnedEvents -> "RoomPinnedEvents" + StateEventContent.RoomPowerLevels -> "RoomPowerLevels" + StateEventContent.RoomServerAcl -> "RoomServerAcl" + StateEventContent.RoomThirdPartyInvite -> "RoomThirdPartyInvite" + StateEventContent.RoomTombstone -> "RoomTombstone" + StateEventContent.RoomTopic -> "RoomTopic" + StateEventContent.SpaceChild -> "SpaceChild" + StateEventContent.SpaceParent -> "SpaceParent" + } +} + +private fun MessageLikeEventContent.toContent(): String { + return use { + when (it) { + MessageLikeEventContent.CallAnswer -> "CallAnswer" + MessageLikeEventContent.CallCandidates -> "CallCandidates" + MessageLikeEventContent.CallHangup -> "CallHangup" + MessageLikeEventContent.CallInvite -> "CallInvite" + MessageLikeEventContent.KeyVerificationAccept -> "KeyVerificationAccept" + MessageLikeEventContent.KeyVerificationCancel -> "KeyVerificationCancel" + MessageLikeEventContent.KeyVerificationDone -> "KeyVerificationDone" + MessageLikeEventContent.KeyVerificationKey -> "KeyVerificationKey" + MessageLikeEventContent.KeyVerificationMac -> "KeyVerificationMac" + MessageLikeEventContent.KeyVerificationReady -> "KeyVerificationReady" + MessageLikeEventContent.KeyVerificationStart -> "KeyVerificationStart" + is MessageLikeEventContent.ReactionContent -> "Reacted to ${it.relatedEventId.take(8)}…" + MessageLikeEventContent.RoomEncrypted -> "RoomEncrypted" + is MessageLikeEventContent.RoomMessage -> it.messageType.toContent() + MessageLikeEventContent.RoomRedaction -> "RoomRedaction" + MessageLikeEventContent.Sticker -> "Sticker" + } + } +} + +private fun MessageType.toContent(): String { + return when (this) { + is MessageType.Audio -> content.use { it.body } + is MessageType.Emote -> content.body + is MessageType.File -> content.use { it.body } + is MessageType.Image -> content.use { it.body } + is MessageType.Notice -> content.body + is MessageType.Text -> content.body + is MessageType.Video -> content.use { it.body } + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index de168090f4..840a38350e 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -24,11 +24,11 @@ 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.UserId import io.element.android.libraries.matrix.api.notification.NotificationData +import io.element.android.libraries.matrix.api.notification.NotificationEvent import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider -import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject @@ -44,7 +44,6 @@ class NotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, // private val noticeEventFormatter: NoticeEventFormatter, // private val displayableEventFormatter: DisplayableEventFormatter, - private val clock: SystemClock, private val matrixAuthenticationService: MatrixAuthenticationService, private val buildMeta: BuildMeta, ) { @@ -80,13 +79,13 @@ class NotifiableEventResolver @Inject constructor( editedEventId = null, canBeReplaced = true, noisy = isNoisy, - timestamp = clock.epochMillis(), + timestamp = event.timestamp, senderName = senderDisplayName, senderId = senderId.value, - body = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", - imageUriString = null, + body = event.content, + imageUriString = event.contentUrl, threadId = null, - roomName = null, + roomName = roomDisplayName, roomIsDirect = false, roomAvatarPath = roomAvatarUrl, senderAvatarPath = senderAvatarUrl, @@ -107,8 +106,19 @@ private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): Notif eventId = eventId, senderId = UserId("@user:domain"), roomId = roomId, + senderAvatarUrl = null, + senderDisplayName = null, + roomAvatarUrl = null, + roomDisplayName = null, isNoisy = false, isEncrypted = false, - isDirect = false + isDirect = false, + event = NotificationEvent( + eventId = eventId, + senderId = UserId("@user:domain"), + timestamp = System.currentTimeMillis(), + content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", + contentUrl = null + ) ) } From afa06fb03e8873b0cbf6ca5c93418645df0ad867 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 10:28:39 +0200 Subject: [PATCH 02/12] Notifications: render room and user icons. --- libraries/push/impl/build.gradle.kts | 1 + .../notifications/NotificationBitmapLoader.kt | 50 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 2951ca0e25..da9122ef52 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(libs.androidx.security.crypto) implementation(libs.network.retrofit) implementation(libs.serialization.json) + implementation(libs.coil) implementation(projects.libraries.architecture) implementation(projects.libraries.core) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 7bd76f9f42..9ceed4296c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -21,7 +21,13 @@ import android.graphics.Bitmap import android.os.Build import androidx.annotation.WorkerThread import androidx.core.graphics.drawable.IconCompat +import androidx.core.graphics.drawable.toBitmap +import coil.imageLoader +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.matrix.api.media.MediaResolver +import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -31,6 +37,7 @@ class NotificationBitmapLoader @Inject constructor( /** * Get icon of a room. + * @param path mxc url */ @WorkerThread fun getRoomBitmap(path: String?): Bitmap? { @@ -43,18 +50,15 @@ class NotificationBitmapLoader @Inject constructor( @WorkerThread private fun loadRoomBitmap(path: String): Bitmap? { return try { - null - /* TODO Notification - Glide.with(context) - .asBitmap() - .load(path) - .format(DecodeFormat.PREFER_ARGB_8888) - .signature(ObjectKey("room-icon-notification")) - .submit() - .get() - */ + val imageRequest = ImageRequest.Builder(context) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .build() + runBlocking { + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() + } } catch (e: Exception) { - Timber.e(e, "decodeFile failed") + Timber.e(e, "Unable to load room bitmap") null } } @@ -62,6 +66,7 @@ class NotificationBitmapLoader @Inject constructor( /** * Get icon of a user. * Before Android P, this does nothing because the icon won't be used + * @param path mxc url */ @WorkerThread fun getUserIcon(path: String?): IconCompat? { @@ -75,20 +80,17 @@ class NotificationBitmapLoader @Inject constructor( @WorkerThread private fun loadUserIcon(path: String): IconCompat? { return try { - null - /* TODO Notification - val bitmap = Glide.with(context) - .asBitmap() - .load(path) - .transform(CircleCrop()) - .format(DecodeFormat.PREFER_ARGB_8888) - .signature(ObjectKey("user-icon-notification")) - .submit() - .get() - IconCompat.createWithBitmap(bitmap) - */ + val imageRequest = ImageRequest.Builder(context) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .transformations(CircleCropTransformation()) + .build() + val bitmap = runBlocking { + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() + } + return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Exception) { - Timber.e(e, "decodeFile failed") + Timber.e(e, "Unable to load user bitmap") null } } From 7e889e5c0a373319c9cd8e286333d6b9702fc4e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 10:59:11 +0200 Subject: [PATCH 03/12] 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() } From e3b2a30f5868fce208dca78ea9ae0935f94d3db3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 14:47:32 +0200 Subject: [PATCH 04/12] Notifications: add prefix to debug notification display. --- .../notifications/NotifiableEventResolver.kt | 2 +- .../notifications/NotificationBitmapLoader.kt | 8 +++---- .../notifications/RoomGroupMessageCreator.kt | 13 ++++++++---- .../SummaryGroupMessageCreator.kt | 16 +++++++------- .../notifications/debug/DebugNotification.kt | 21 +++++++++++++++++++ .../factories/NotificationFactory.kt | 21 ++++++++++--------- 6 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 840a38350e..16abf69874 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -86,7 +86,7 @@ class NotifiableEventResolver @Inject constructor( imageUriString = event.contentUrl, threadId = null, roomName = roomDisplayName, - roomIsDirect = false, + roomIsDirect = isDirect, roomAvatarPath = roomAvatarUrl, senderAvatarPath = senderAvatarUrl, soundName = null, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 9ceed4296c..a965c6aaa5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -51,13 +51,13 @@ class NotificationBitmapLoader @Inject constructor( private fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) .build() runBlocking { val result = context.imageLoader.execute(imageRequest) result.drawable?.toBitmap() } - } catch (e: Exception) { + } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") null } @@ -81,7 +81,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) .transformations(CircleCropTransformation()) .build() val bitmap = runBlocking { @@ -89,7 +89,7 @@ class NotificationBitmapLoader @Inject constructor( result.drawable?.toBitmap() } return bitmap?.let { IconCompat.createWithBitmap(it) } - } catch (e: Exception) { + } catch (e: Throwable) { Timber.e(e, "Unable to load user bitmap") null } 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 8024e0c6cb..a03fd00dc1 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 @@ -22,6 +22,7 @@ import androidx.core.app.Person import io.element.android.libraries.matrix.api.core.RoomId 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.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider @@ -46,12 +47,12 @@ class RoomGroupMessageCreator @Inject constructor( val roomIsGroup = !lastKnownRoomEvent.roomIsDirect val style = NotificationCompat.MessagingStyle( Person.Builder() - .setName(currentUser.displayName) + .setName(currentUser.displayName?.annotateForDebug(50)) .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl)) .setKey(lastKnownRoomEvent.sessionId.value) .build() ).also { - it.conversationTitle = roomName.takeIf { roomIsGroup } + it.conversationTitle = roomName.takeIf { roomIsGroup }?.annotateForDebug(51) it.isGroupConversation = roomIsGroup it.addMessagesFromEvents(events) } @@ -103,7 +104,7 @@ class RoomGroupMessageCreator @Inject constructor( null } else { Person.Builder() - .setName(event.senderName) + .setName(event.senderName?.annotateForDebug(70)) .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath)) .setKey(event.senderId) .build() @@ -115,7 +116,11 @@ class RoomGroupMessageCreator @Inject constructor( senderPerson ) else -> { - val message = NotificationCompat.MessagingStyle.Message(event.body, event.timestamp, senderPerson).also { message -> + val message = NotificationCompat.MessagingStyle.Message( + event.body?.annotateForDebug(71), + event.timestamp, + senderPerson + ).also { message -> event.imageUri?.let { message.setData("image/", it) } 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 78c6ab3ee0..5a7f3d36e8 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 @@ -20,6 +20,7 @@ import android.app.Notification import androidx.core.app.NotificationCompat 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.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.services.toolbox.api.strings.StringProvider import javax.inject.Inject @@ -40,7 +41,7 @@ import javax.inject.Inject */ class SummaryGroupMessageCreator @Inject constructor( private val stringProvider: StringProvider, - private val notificationFactory: NotificationFactory + private val notificationFactory: NotificationFactory, ) { fun createSummaryNotification( @@ -51,9 +52,9 @@ class SummaryGroupMessageCreator @Inject constructor( useCompleteNotificationFormat: Boolean ): Notification { val summaryInboxStyle = NotificationCompat.InboxStyle().also { style -> - roomNotifications.forEach { style.addLine(it.summaryLine) } - invitationNotifications.forEach { style.addLine(it.summaryLine) } - simpleNotifications.forEach { style.addLine(it.summaryLine) } + roomNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(40)) } + invitationNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(41)) } + simpleNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(42)) } } val summaryIsNoisy = roomNotifications.any { it.shouldBing } || @@ -69,9 +70,10 @@ class SummaryGroupMessageCreator @Inject constructor( // FIXME roomIdToEventMap.size is not correct, this is the number of rooms val nbEvents = roomNotifications.size + simpleNotifications.size val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) - summaryInboxStyle.setBigContentTitle(sumTitle) - // TODO get latest event? - .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) + summaryInboxStyle.setBigContentTitle(sumTitle.annotateForDebug(43)) + //.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents).annotateForDebug(44)) + // Use account name now, for multi-session + .setSummaryText(currentUser.userId.value.annotateForDebug(44)) return if (useCompleteNotificationFormat) { notificationFactory.createSummaryListNotification( currentUser, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt new file mode 100644 index 0000000000..eca99394f6 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.notifications.debug + +fun CharSequence.annotateForDebug(prefix: Int): CharSequence { + return "$prefix-$this" +} 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 18bfdd8292..9da47a6569 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 @@ -31,6 +31,7 @@ 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 +import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.action.AcceptInvitationActionFactory import io.element.android.libraries.push.impl.notifications.factories.action.MarkAsReadActionFactory import io.element.android.libraries.push.impl.notifications.factories.action.QuickReplyActionFactory @@ -84,16 +85,16 @@ class NotificationFactory @Inject constructor( // ID of the corresponding shortcut, for conversation features under API 30+ .setShortcutId(roomInfo.roomId.value) // Title for API < 16 devices. - .setContentTitle(roomInfo.roomDisplayName) + .setContentTitle(roomInfo.roomDisplayName.annotateForDebug(1)) // Content for API < 16 devices. - .setContentText(stringProvider.getString(R.string.notification_new_messages)) + .setContentText(stringProvider.getString(R.string.notification_new_messages).annotateForDebug(2)) // Number of new notifications for API <24 (M and below) devices. .setSubText( stringProvider.getQuantityString( R.plurals.notification_new_messages_for_room, messageStyle.messages.size, messageStyle.messages.size - ) + ).annotateForDebug(3) ) // Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) // devices and all Wear devices. But we want a custom grouping, so we specify the groupID @@ -135,7 +136,7 @@ class NotificationFactory @Inject constructor( } setDeleteIntent(pendingIntentFactory.createDismissRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId)) } - .setTicker(tickerText) + .setTicker(tickerText.annotateForDebug(4)) .build() } @@ -147,8 +148,8 @@ class NotificationFactory @Inject constructor( val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) - .setContentTitle(inviteNotifiableEvent.roomName ?: buildMeta.applicationName) - .setContentText(inviteNotifiableEvent.description) + .setContentTitle((inviteNotifiableEvent.roomName ?: buildMeta.applicationName).annotateForDebug(5)) + .setContentText(inviteNotifiableEvent.description.annotateForDebug(6)) .setGroup(inviteNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .setSmallIcon(smallIcon) @@ -196,8 +197,8 @@ class NotificationFactory @Inject constructor( val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) - .setContentTitle(buildMeta.applicationName) - .setContentText(simpleNotifiableEvent.description) + .setContentTitle(buildMeta.applicationName.annotateForDebug(7)) + .setContentText(simpleNotifiableEvent.description.annotateForDebug(8)) .setGroup(simpleNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .setSmallIcon(smallIcon) @@ -240,11 +241,11 @@ class NotificationFactory @Inject constructor( // used in compat < N, after summary is built based on child notifications .setWhen(lastMessageTimestamp) .setStyle(style) - .setContentTitle(currentUser.userId.value) + .setContentTitle(currentUser.userId.value.annotateForDebug(9)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setSmallIcon(smallIcon) // set content text to support devices running API level < 24 - .setContentText(compatSummary) + .setContentText(compatSummary.annotateForDebug(10)) .setGroup(currentUser.userId.value) // set this notification as the summary for the group .setGroupSummary(true) From 2fb835ec772298ae3ed77b510578abc7c869652c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 16:34:24 +0200 Subject: [PATCH 05/12] Bigger image - WIP --- .../push/impl/notifications/NotificationBitmapLoader.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index a965c6aaa5..0302a9ced6 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -51,7 +51,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .build() runBlocking { val result = context.imageLoader.execute(imageRequest) @@ -81,7 +81,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() val bitmap = runBlocking { From 44c0466b09b3f83c81b5f827ff94fdd948270b3a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 16:35:06 +0200 Subject: [PATCH 06/12] Disable debugging of notification --- .../push/impl/notifications/debug/DebugNotification.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt index eca99394f6..37f33e1188 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt @@ -16,6 +16,6 @@ package io.element.android.libraries.push.impl.notifications.debug -fun CharSequence.annotateForDebug(prefix: Int): CharSequence { - return "$prefix-$this" +fun CharSequence.annotateForDebug(@Suppress("UNUSED_PARAMETER") prefix: Int): CharSequence { + return this // "$prefix-$this" } From 8b190d776afe49621abd3bb8f479165071741034 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:13:24 +0200 Subject: [PATCH 07/12] Fix test --- .../notifications/NotificationFactoryTest.kt | 23 ++++++++++++------- .../notifications/NotificationRendererTest.kt | 9 +++----- .../fake/FakeNotificationFactory.kt | 10 ++++---- .../fake/FakeRoomGroupMessageCreator.kt | 14 +++++++---- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt index 9fbd723071..016d90952e 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.push.impl.notifications import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -124,11 +125,13 @@ class NotificationFactoryTest { fun `given room with message when mapping to notification then delegates to room group message creator`() = testWith(notificationFactory) { val events = listOf(A_MESSAGE_EVENT) val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor( - A_SESSION_ID, events, A_ROOM_ID, A_SESSION_ID.value, MY_AVATAR_URL + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), events, A_ROOM_ID ) val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT))) - val result = roomWithMessage.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = roomWithMessage.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo(listOf(expectedNotification)) } @@ -138,7 +141,9 @@ class NotificationFactoryTest { val events = listOf(ProcessedEvent(ProcessedEvent.Type.REMOVE, A_MESSAGE_EVENT)) val emptyRoom = mapOf(A_ROOM_ID to events) - val result = emptyRoom.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = emptyRoom.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo( listOf( @@ -153,7 +158,9 @@ class NotificationFactoryTest { fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationFactory) { val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)))) - val result = redactedRoom.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = redactedRoom.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo( listOf( @@ -176,14 +183,14 @@ class NotificationFactoryTest { ) val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted"))) val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor( - A_SESSION_ID, + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), withRedactedRemoved, A_ROOM_ID, - A_SESSION_ID.value, - MY_AVATAR_URL ) - val result = roomWithRedactedMessage.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = roomWithRedactedMessage.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo(listOf(expectedNotification)) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 79c6dfdb02..a94ef3f02d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -193,9 +194,7 @@ class NotificationRendererTest { private fun renderEventsAsNotifications() { notificationRenderer.render( - sessionId = A_SESSION_ID, - myUserDisplayName = MY_USER_DISPLAY_NAME, - myUserAvatarUrl = MY_USER_AVATAR_URL, + MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, eventsToProcess = AN_EVENT_LIST ) @@ -214,9 +213,7 @@ class NotificationRendererTest { ) { notificationFactory.givenNotificationsFor( groupedEvents = A_PROCESSED_EVENTS, - sessionId = A_SESSION_ID, - myUserDisplayName = MY_USER_DISPLAY_NAME, - myUserAvatarUrl = MY_USER_AVATAR_URL, + matrixUser = MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = useCompleteNotificationFormat, roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt index 7d7812e6cb..999cd6f54a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.push.impl.notifications.fake -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.GroupedNotificationEvents import io.element.android.libraries.push.impl.notifications.NotificationFactory import io.element.android.libraries.push.impl.notifications.OneShotNotification @@ -30,9 +30,7 @@ class FakeNotificationFactory { fun givenNotificationsFor( groupedEvents: GroupedNotificationEvents, - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String?, + matrixUser: MatrixUser, useCompleteNotificationFormat: Boolean, roomNotifications: List, invitationNotifications: List, @@ -40,13 +38,13 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - every { groupedEvents.roomEvents.toNotifications(sessionId, myUserDisplayName, myUserAvatarUrl) } returns roomNotifications + every { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications every { createSummaryNotification( - sessionId, + matrixUser, roomNotifications, invitationNotifications, simpleNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index df0b5ad42b..bba9fde202 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.push.impl.notifications.fake 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.RoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -29,14 +29,18 @@ class FakeRoomGroupMessageCreator { val instance = mockk() fun givenCreatesRoomMessageFor( - sessionId: SessionId, + matrixUser: MatrixUser, events: List, roomId: RoomId, - userDisplayName: String, - userAvatarUrl: String? ): RoomNotification.Message { val mockMessage = mockk() - every { instance.createRoomMessage(sessionId, events, roomId, userDisplayName, userAvatarUrl) } returns mockMessage + every { + instance.createRoomMessage( + currentUser = matrixUser, + events = events, + roomId = roomId, + ) + } returns mockMessage return mockMessage } } From 3d77083aa74d0daef25d24ab3eb787e461d1edad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:32:33 +0200 Subject: [PATCH 08/12] Cleanup --- .../libraries/matrix/api/notification/NotificationData.kt | 2 -- .../libraries/matrix/impl/notification/TimelineEventMapper.kt | 2 -- .../push/impl/notifications/NotifiableEventResolver.kt | 2 -- .../push/impl/notifications/model/NotifiableMessageEvent.kt | 3 +++ 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 60d889d046..eb6e9998ac 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -35,8 +35,6 @@ data class NotificationData( ) data class NotificationEvent( - val eventId: EventId, - val senderId: UserId, val timestamp: Long, val content: String, // For images for instance diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt index 3d2759c6d1..adb9dcce72 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt @@ -32,8 +32,6 @@ class TimelineEventMapper @Inject constructor() { fun map(timelineEvent: TimelineEvent): NotificationEvent { return timelineEvent.use { NotificationEvent( - eventId = EventId(it.eventId()), - senderId = UserId(it.senderId()), timestamp = it.timestamp().toLong(), content = it.eventType().toContent(), contentUrl = null // TODO it.eventType().toContentUrl(), diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 16abf69874..8e274b2124 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -114,8 +114,6 @@ private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): Notif isEncrypted = false, isDirect = false, event = NotificationEvent( - eventId = eventId, - senderId = UserId("@user:domain"), timestamp = System.currentTimeMillis(), content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", contentUrl = null diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index d7af528ce3..1216e0fe12 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -57,6 +57,9 @@ data class NotifiableMessageEvent( val description: String = body ?: "" val title: String = senderName ?: "" + // TODO EAx The image has to be downloaded and expose using the file provider. + // Example of value from Element Android: + // content://im.vector.app.debug.mx-sdk.fileprovider/downloads/downloads/816abf76d806c768760568952b1862c8/F/72c33edd23dee3b95f4d5a18aa25fa54/image.png val imageUri: Uri? get() = imageUriString?.let { Uri.parse(it) } } From fe85a7cc920005b5594a03eabf6bb8268ddb4c44 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:58:16 +0200 Subject: [PATCH 09/12] Use coroutine dispatcher instead of WorkerThread --- .../notifications/NotificationBitmapLoader.kt | 26 +++----- .../NotificationDrawerManager.kt | 66 ++++++++----------- .../impl/notifications/NotificationFactory.kt | 2 +- .../notifications/NotificationRenderer.kt | 4 +- .../notifications/RoomGroupMessageCreator.kt | 6 +- .../notifications/NotificationFactoryTest.kt | 7 +- .../fake/FakeNotificationFactory.kt | 3 +- .../fake/FakeRoomGroupMessageCreator.kt | 4 +- 8 files changed, 48 insertions(+), 70 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 0302a9ced6..a3644e737f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.push.impl.notifications import android.content.Context import android.graphics.Bitmap import android.os.Build -import androidx.annotation.WorkerThread import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap import coil.imageLoader @@ -27,7 +26,6 @@ import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.MediaResolver -import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -39,24 +37,20 @@ class NotificationBitmapLoader @Inject constructor( * Get icon of a room. * @param path mxc url */ - @WorkerThread - fun getRoomBitmap(path: String?): Bitmap? { + suspend fun getRoomBitmap(path: String?): Bitmap? { if (path == null) { return null } return loadRoomBitmap(path) } - @WorkerThread - private fun loadRoomBitmap(path: String): Bitmap? { + private suspend fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .build() - runBlocking { - val result = context.imageLoader.execute(imageRequest) - result.drawable?.toBitmap() - } + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") null @@ -68,8 +62,7 @@ class NotificationBitmapLoader @Inject constructor( * Before Android P, this does nothing because the icon won't be used * @param path mxc url */ - @WorkerThread - fun getUserIcon(path: String?): IconCompat? { + suspend fun getUserIcon(path: String?): IconCompat? { if (path == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { return null } @@ -77,17 +70,14 @@ class NotificationBitmapLoader @Inject constructor( return loadUserIcon(path) } - @WorkerThread - private fun loadUserIcon(path: String): IconCompat? { + private suspend fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val bitmap = runBlocking { - val result = context.imageLoader.execute(imageRequest) - result.drawable?.toBitmap() - } + val result = context.imageLoader.execute(imageRequest) + val bitmap = result.drawable?.toBitmap() return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Throwable) { Timber.e(e, "Unable to load user bitmap") 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 c3c5275eb5..87d37e7e33 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,11 +16,9 @@ package io.element.android.libraries.push.impl.notifications -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.coroutine.CoroutineDispatchers import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope @@ -37,8 +35,9 @@ import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMe import io.element.android.services.appnavstate.api.AppNavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject @@ -56,13 +55,10 @@ class NotificationDrawerManager @Inject constructor( private val filteredEventDetector: FilteredEventDetector, private val appNavigationStateService: AppNavigationStateService, private val coroutineScope: CoroutineScope, + private val dispatchers: CoroutineDispatchers, private val buildMeta: BuildMeta, private val matrixAuthenticationService: MatrixAuthenticationService, ) { - - private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) - private var backgroundHandler: Handler - /** * Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events. */ @@ -74,8 +70,6 @@ class NotificationDrawerManager @Inject constructor( private var useCompleteNotificationFormat = true init { - handlerThread.start() - backgroundHandler = Handler(handlerThread.looper) // Observe application state coroutineScope.launch { appNavigationStateService.appNavigationStateFlow @@ -193,30 +187,25 @@ class NotificationDrawerManager @Inject constructor( notificationState.updateQueuedEvents(this) { queuedEvents, _ -> action(queuedEvents) } - refreshNotificationDrawer() + coroutineScope.refreshNotificationDrawer() } - private fun refreshNotificationDrawer() { + private fun CoroutineScope.refreshNotificationDrawer() = launch { // Implement last throttler val canHandle = firstThrottler.canHandle() Timber.v("refreshNotificationDrawer(), delay: ${canHandle.waitMillis()} ms") - backgroundHandler.removeCallbacksAndMessages(null) - - backgroundHandler.postDelayed( - { - try { - refreshNotificationDrawerBg() - } catch (throwable: Throwable) { - // It can happen if for instance session has been destroyed. It's a bit ugly to try catch like this, but it's safer - Timber.w(throwable, "refreshNotificationDrawerBg failure") - } - }, - canHandle.waitMillis() - ) + withContext(dispatchers.io) { + delay(canHandle.waitMillis()) + try { + refreshNotificationDrawerBg() + } catch (throwable: Throwable) { + // It can happen if for instance session has been destroyed. It's a bit ugly to try catch like this, but it's safer + Timber.w(throwable, "refreshNotificationDrawerBg failure") + } + } } - @WorkerThread - private fun refreshNotificationDrawerBg() { + private suspend fun refreshNotificationDrawerBg() { Timber.v("refreshNotificationDrawerBg()") val eventsToRender = notificationState.updateQueuedEvents(this) { queuedEvents, renderedEvents -> notifiableEventProcessor.process(queuedEvents.rawEvents(), currentAppNavigationState, renderedEvents).also { @@ -239,8 +228,7 @@ class NotificationDrawerManager @Inject constructor( } } - @WorkerThread - private fun renderEvents(eventsToRender: List>) { + private suspend fun renderEvents(eventsToRender: List>) { // Group by sessionId val eventsForSessions = eventsToRender.groupBy { it.event.sessionId @@ -250,18 +238,16 @@ class NotificationDrawerManager @Inject constructor( val currentUser = tryOrNull( onError = { Timber.e(it, "Unable to retrieve info for user ${sessionId.value}") }, operation = { - runBlocking { - val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull() + 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 - ) - } + // 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, 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 96f5e5c81b..79173611dc 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 @@ -34,7 +34,7 @@ class NotificationFactory @Inject constructor( private val summaryGroupMessageCreator: SummaryGroupMessageCreator ) { - fun Map.toNotifications( + suspend fun Map.toNotifications( currentUser: MatrixUser, ): List { return map { (roomId, events) -> 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 33e15510f1..428420211b 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 @@ -16,7 +16,6 @@ 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.user.MatrixUser import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -32,8 +31,7 @@ class NotificationRenderer @Inject constructor( private val notificationFactory: NotificationFactory, ) { - @WorkerThread - fun render( + suspend fun render( currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, eventsToProcess: List> 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 a03fd00dc1..5656b81dd9 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 @@ -37,7 +37,7 @@ class RoomGroupMessageCreator @Inject constructor( private val notificationFactory: NotificationFactory ) { - fun createRoomMessage( + suspend fun createRoomMessage( currentUser: MatrixUser, events: List, roomId: RoomId, @@ -98,7 +98,7 @@ class RoomGroupMessageCreator @Inject constructor( ) } - private fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { + private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { events.forEach { event -> val senderPerson = if (event.outGoingMessage) { null @@ -171,7 +171,7 @@ class RoomGroupMessageCreator @Inject constructor( } } - private fun getRoomBitmap(events: List): Bitmap? { + private suspend fun getRoomBitmap(events: List): Bitmap? { // Use the last event (most recent?) return events.lastOrNull() ?.roomAvatarPath diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt index 016d90952e..18d8870ac3 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt @@ -28,6 +28,7 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGrou import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent +import kotlinx.coroutines.test.runTest import org.junit.Test private val MY_AVATAR_URL: String? = null @@ -196,6 +197,8 @@ class NotificationFactoryTest { } } -fun testWith(receiver: T, block: T.() -> Unit) { - receiver.block() +fun testWith(receiver: T, block: suspend T.() -> Unit) { + runTest { + receiver.block() + } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt index 999cd6f54a..09957e2cf2 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.push.impl.notifications.NotificationFactory import io.element.android.libraries.push.impl.notifications.OneShotNotification import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.SummaryNotification +import io.mockk.coEvery import io.mockk.every import io.mockk.mockk @@ -38,7 +39,7 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - every { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications + coEvery { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index bba9fde202..b896737e6f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent -import io.mockk.every +import io.mockk.coEvery import io.mockk.mockk class FakeRoomGroupMessageCreator { @@ -34,7 +34,7 @@ class FakeRoomGroupMessageCreator { roomId: RoomId, ): RoomNotification.Message { val mockMessage = mockk() - every { + coEvery { instance.createRoomMessage( currentUser = matrixUser, events = events, From cede53579ccaebad248f98c7bc0ed785ea4bf608 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 14:24:42 +0200 Subject: [PATCH 10/12] Fix test --- .../notifications/NotificationRendererTest.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index a94ef3f02d..c109edb40a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeNotificatio import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.mockk.mockk +import kotlinx.coroutines.test.runTest import org.junit.Test private const val MY_USER_DISPLAY_NAME = "display-name" @@ -54,7 +55,7 @@ class NotificationRendererTest { ) @Test - fun `given no notifications when rendering then cancels summary notification`() { + fun `given no notifications when rendering then cancels summary notification`() = runTest { givenNoNotifications() renderEventsAsNotifications() @@ -64,7 +65,7 @@ class NotificationRendererTest { } @Test - fun `given last room message group notification is removed when rendering then remove the summary and then remove message notification`() { + fun `given last room message group notification is removed when rendering then remove the summary and then remove message notification`() = runTest { givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -76,7 +77,7 @@ class NotificationRendererTest { } @Test - fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() { + fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() = runTest { givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID))) renderEventsAsNotifications() @@ -88,7 +89,7 @@ class NotificationRendererTest { } @Test - fun `given a room message group notification is added when rendering then show the message notification and update summary`() { + fun `given a room message group notification is added when rendering then show the message notification and update summary`() = runTest { givenNotifications( roomNotifications = listOf( RoomNotification.Message( @@ -107,7 +108,7 @@ class NotificationRendererTest { } @Test - fun `given last simple notification is removed when rendering then remove the summary and then remove simple notification`() { + fun `given last simple notification is removed when rendering then remove the summary and then remove simple notification`() = runTest { givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID.value)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -119,7 +120,7 @@ class NotificationRendererTest { } @Test - fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() { + fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() = runTest { givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID.value))) renderEventsAsNotifications() @@ -131,7 +132,7 @@ class NotificationRendererTest { } @Test - fun `given a simple notification is added when rendering then show the simple notification and update summary`() { + fun `given a simple notification is added when rendering then show the simple notification and update summary`() = runTest { givenNotifications( simpleNotifications = listOf( OneShotNotification.Append( @@ -150,7 +151,7 @@ class NotificationRendererTest { } @Test - fun `given last invitation notification is removed when rendering then remove the summary and then remove invitation notification`() { + fun `given last invitation notification is removed when rendering then remove the summary and then remove invitation notification`() = runTest { givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID.value)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -162,7 +163,7 @@ class NotificationRendererTest { } @Test - fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() { + fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() = runTest { givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID.value))) renderEventsAsNotifications() @@ -174,7 +175,7 @@ class NotificationRendererTest { } @Test - fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() { + fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() = runTest { givenNotifications( simpleNotifications = listOf( OneShotNotification.Append( @@ -192,7 +193,7 @@ class NotificationRendererTest { } } - private fun renderEventsAsNotifications() { + private suspend fun renderEventsAsNotifications() { notificationRenderer.render( MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, From 42bafbdc1af31b0743604a3eafe50bff779184ef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Jun 2023 12:27:11 +0200 Subject: [PATCH 11/12] Do not use System.currentTimeMillis() --- .../notifications/NotifiableEventResolver.kt | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 8e274b2124..fb3fcfc61f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider +import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject @@ -46,6 +47,7 @@ class NotifiableEventResolver @Inject constructor( // private val displayableEventFormatter: DisplayableEventFormatter, private val matrixAuthenticationService: MatrixAuthenticationService, private val buildMeta: BuildMeta, + private val clock: SystemClock, ) { suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { @@ -96,27 +98,27 @@ class NotifiableEventResolver @Inject constructor( isUpdated = false ) } -} -/** - * TODO This is a temporary method for EAx. - */ -private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): NotificationData { - return this ?: NotificationData( - eventId = eventId, - senderId = UserId("@user:domain"), - roomId = roomId, - senderAvatarUrl = null, - senderDisplayName = null, - roomAvatarUrl = null, - roomDisplayName = null, - isNoisy = false, - isEncrypted = false, - isDirect = false, - event = NotificationEvent( - timestamp = System.currentTimeMillis(), - content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", - contentUrl = null + /** + * TODO This is a temporary method for EAx. + */ + private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): NotificationData { + return this ?: NotificationData( + eventId = eventId, + senderId = UserId("@user:domain"), + roomId = roomId, + senderAvatarUrl = null, + senderDisplayName = null, + roomAvatarUrl = null, + roomDisplayName = null, + isNoisy = false, + isEncrypted = false, + isDirect = false, + event = NotificationEvent( + timestamp = clock.epochMillis(), + content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", + contentUrl = null + ) ) - ) + } } From 99204ce9e66e1ab81fadf86e6c79795445d0637a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Jun 2023 17:02:48 +0200 Subject: [PATCH 12/12] Fix compilation issue after rebase. --- libraries/push/impl/build.gradle.kts | 1 + .../push/impl/notifications/NotificationBitmapLoader.kt | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index da9122ef52..725961a248 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.network) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) api(projects.libraries.pushproviders.api) api(projects.libraries.pushstore.api) api(projects.libraries.push.api) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index a3644e737f..c2cdfc5677 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -25,7 +25,8 @@ import coil.imageLoader import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.ui.media.MediaRequestData import timber.log.Timber import javax.inject.Inject @@ -47,7 +48,7 @@ class NotificationBitmapLoader @Inject constructor( private suspend fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) + .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .build() val result = context.imageLoader.execute(imageRequest) result.drawable?.toBitmap() @@ -73,7 +74,7 @@ class NotificationBitmapLoader @Inject constructor( private suspend fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) + .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() val result = context.imageLoader.execute(imageRequest)