diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index 7f36a7a51a..423228c110 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -33,13 +33,12 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.di.SessionComponentFactory import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.ui.di.MatrixUIBindings +import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder import kotlinx.parcelize.Parcelize /** @@ -52,6 +51,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, sessionComponentFactory: SessionComponentFactory, + private val imageLoaderHolder: ImageLoaderHolder, ) : ParentNode( navModel = PermanentNavModel( navTargets = setOf(NavTarget), @@ -78,8 +78,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor( super.onBuilt() lifecycle.subscribe( onCreate = { - val imageLoaderFactory = bindings().loggedInImageLoaderFactory() - Coil.setImageLoader(imageLoaderFactory) + Coil.setImageLoader(imageLoaderHolder.get(inputs.matrixClient)) }, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index c495558fd9..4c5309e2bd 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -28,8 +28,8 @@ import okhttp3.OkHttpClient import javax.inject.Inject import javax.inject.Provider -class LoggedInImageLoaderFactory @Inject constructor( - @ApplicationContext private val context: Context, +class LoggedInImageLoaderFactory( + private val context: Context, private val matrixClient: MatrixClient, private val okHttpClient: Provider, ) : ImageLoaderFactory { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt new file mode 100644 index 0000000000..fc027d0e5c --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -0,0 +1,71 @@ +/* + * 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.ui.media + +import android.content.Context +import coil.ImageLoader +import com.squareup.anvil.annotations.ContributesBinding +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.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import okhttp3.OkHttpClient +import javax.inject.Inject +import javax.inject.Provider + +interface ImageLoaderHolder { + fun get(client: MatrixClient): ImageLoader +} + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +class DefaultImageLoaderHolder @Inject constructor( + @ApplicationContext private val context: Context, + private val okHttpClient: Provider, + private val sessionObserver: SessionObserver, +) : ImageLoaderHolder { + private val map = mutableMapOf() + + init { + observeSessions() + } + + private fun observeSessions() { + sessionObserver.addListener(object : SessionListener { + override suspend fun onSessionCreated(userId: String) = Unit + + override suspend fun onSessionDeleted(userId: String) { + map.remove(SessionId(userId)) + } + }) + } + + override fun get(client: MatrixClient): ImageLoader { + return synchronized(map) { + map.getOrPut(client.sessionId) { + LoggedInImageLoaderFactory( + context = context, + matrixClient = client, + okHttpClient = okHttpClient, + ).newImageLoader() + } + } + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 09f622e978..d4070b8ec6 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -61,6 +61,7 @@ class DefaultNotificationDrawerManager @Inject constructor( private val dispatchers: CoroutineDispatchers, private val buildMeta: BuildMeta, private val matrixClientProvider: MatrixClientProvider, + private val imageLoaderHolder: ImageLoaderHolder, ) : NotificationDrawerManager { private var appNavigationStateObserver: Job? = null @@ -288,10 +289,11 @@ class DefaultNotificationDrawerManager @Inject constructor( } eventsForSessions.forEach { (sessionId, notifiableEvents) -> + val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow() + val imageLoader = imageLoaderHolder.get(client) val currentUser = tryOrNull( onError = { Timber.tag(loggerTag.value).e(it, "Unable to retrieve info for user ${sessionId.value}") }, operation = { - val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow() // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash val myUserDisplayName = client.loadUserDisplayName().getOrNull() ?: sessionId.value val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() @@ -307,7 +309,7 @@ class DefaultNotificationDrawerManager @Inject constructor( avatarUrl = null ) - notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents) + notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents, imageLoader) } } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ImageLoaderHolder.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ImageLoaderHolder.kt new file mode 100644 index 0000000000..3f48922ca0 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ImageLoaderHolder.kt @@ -0,0 +1,72 @@ +/* + * 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 + +import android.content.Context +import coil.ImageLoader +import com.squareup.anvil.annotations.ContributesBinding +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.MatrixClient +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.ui.media.LoggedInImageLoaderFactory +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import okhttp3.OkHttpClient +import javax.inject.Inject +import javax.inject.Provider + +interface ImageLoaderHolder { + fun get(client: MatrixClient): ImageLoader +} + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +class DefaultImageLoaderHolder @Inject constructor( + @ApplicationContext private val context: Context, + private val okHttpClient: Provider, + private val sessionObserver: SessionObserver, +) : ImageLoaderHolder { + private val map = mutableMapOf() + + init { + observeSessions() + } + + private fun observeSessions() { + sessionObserver.addListener(object : SessionListener { + override suspend fun onSessionCreated(userId: String) = Unit + + override suspend fun onSessionDeleted(userId: String) { + map.remove(SessionId(userId)) + } + }) + } + + override fun get(client: MatrixClient): ImageLoader { + return synchronized(map) { + map.getOrPut(client.sessionId) { + LoggedInImageLoaderFactory( + context = context, + matrixClient = client, + okHttpClient = okHttpClient, + ).newImageLoader() + } + } + } +} 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 e78ce37639..4c1e3043ad 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,7 @@ import android.graphics.Bitmap import android.os.Build import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap -import coil.imageLoader +import coil.ImageLoader import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext @@ -39,21 +39,22 @@ class NotificationBitmapLoader @Inject constructor( /** * Get icon of a room. * @param path mxc url + * @param imageLoader Coil image loader */ - suspend fun getRoomBitmap(path: String?): Bitmap? { + suspend fun getRoomBitmap(path: String?, imageLoader: ImageLoader): Bitmap? { if (path == null) { return null } - return loadRoomBitmap(path) + return loadRoomBitmap(path, imageLoader) } - private suspend fun loadRoomBitmap(path: String): Bitmap? { + private suspend fun loadRoomBitmap(path: String, imageLoader: ImageLoader): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val result = context.imageLoader.execute(imageRequest) + val result = imageLoader.execute(imageRequest) result.drawable?.toBitmap() } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") @@ -65,22 +66,23 @@ 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 + * @param imageLoader Coil image loader */ - suspend fun getUserIcon(path: String?): IconCompat? { + suspend fun getUserIcon(path: String?, imageLoader: ImageLoader): IconCompat? { if (path == null || sdkIntProvider.get() < Build.VERSION_CODES.P) { return null } - return loadUserIcon(path) + return loadUserIcon(path, imageLoader) } - private suspend fun loadUserIcon(path: String): IconCompat? { + private suspend fun loadUserIcon(path: String, imageLoader: ImageLoader): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val result = context.imageLoader.execute(imageRequest) + val result = imageLoader.execute(imageRequest) val bitmap = result.drawable?.toBitmap() return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Throwable) { 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 15c236eac9..4abdc03123 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 @@ -17,6 +17,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import coil.ImageLoader 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.factories.NotificationCreator @@ -36,6 +37,7 @@ class NotificationFactory @Inject constructor( suspend fun Map.toNotifications( currentUser: MatrixUser, + imageLoader: ImageLoader, ): List { return map { (roomId, events) -> when { @@ -46,6 +48,7 @@ class NotificationFactory @Inject constructor( currentUser = currentUser, events = messageEvents, roomId = roomId, + imageLoader = imageLoader, ) } } 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 713392c695..d05f3a1d03 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,6 +16,7 @@ package io.element.android.libraries.push.impl.notifications +import coil.ImageLoader import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -38,11 +39,12 @@ class NotificationRenderer @Inject constructor( suspend fun render( currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, - eventsToProcess: List> + eventsToProcess: List>, + imageLoader: ImageLoader, ) { val groupedEvents = eventsToProcess.groupByType() with(notificationFactory) { - val roomNotifications = groupedEvents.roomEvents.toNotifications(currentUser) + val roomNotifications = groupedEvents.roomEvents.toNotifications(currentUser, imageLoader) val invitationNotifications = groupedEvents.invitationEvents.toNotifications() val simpleNotifications = groupedEvents.simpleEvents.toNotifications() val fallbackNotifications = groupedEvents.fallbackEvents.toNotifications() 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 cde4f489a2..015b32c1d0 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 @@ -23,6 +23,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.Person import androidx.core.text.buildSpannedString import androidx.core.text.inSpans +import coil.ImageLoader 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 @@ -42,6 +43,7 @@ class RoomGroupMessageCreator @Inject constructor( currentUser: MatrixUser, events: List, roomId: RoomId, + imageLoader: ImageLoader, ): RoomNotification.Message { val lastKnownRoomEvent = events.last() val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: "Room name (${roomId.value.take(8)}…)" @@ -49,13 +51,13 @@ class RoomGroupMessageCreator @Inject constructor( val style = NotificationCompat.MessagingStyle( Person.Builder() .setName(currentUser.displayName?.annotateForDebug(50)) - .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl)) + .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl, imageLoader)) .setKey(lastKnownRoomEvent.sessionId.value) .build() ).also { it.conversationTitle = roomName.takeIf { roomIsGroup }?.annotateForDebug(51) it.isGroupConversation = roomIsGroup - it.addMessagesFromEvents(events) + it.addMessagesFromEvents(events, imageLoader) } val tickerText = if (roomIsGroup) { @@ -64,7 +66,7 @@ class RoomGroupMessageCreator @Inject constructor( stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderName, events.last().description) } - val largeBitmap = getRoomBitmap(events) + val largeBitmap = getRoomBitmap(events, imageLoader) val lastMessageTimestamp = events.last().timestamp val smartReplyErrors = events.filter { it.isSmartReplyError() } @@ -98,14 +100,17 @@ class RoomGroupMessageCreator @Inject constructor( ) } - private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { + private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents( + events: List, + imageLoader: ImageLoader, + ) { events.forEach { event -> val senderPerson = if (event.outGoingMessage) { null } else { Person.Builder() .setName(event.senderName?.annotateForDebug(70)) - .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath)) + .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath, imageLoader)) .setKey(event.senderId.value) .build() } @@ -167,10 +172,13 @@ class RoomGroupMessageCreator @Inject constructor( } } - private suspend fun getRoomBitmap(events: List): Bitmap? { + private suspend fun getRoomBitmap( + events: List, + imageLoader: ImageLoader, + ): Bitmap? { // Use the last event (most recent?) return events.reversed().firstNotNullOfOrNull { it.roomAvatarPath } - ?.let { bitmapLoader.getRoomBitmap(it) } + ?.let { bitmapLoader.getRoomBitmap(it, imageLoader) } } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index 6b4ea24fd4..e641029c0d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoaderHolder import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent @@ -129,6 +130,7 @@ class DefaultNotificationDrawerManagerTest { dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), buildMeta = aBuildMeta(), matrixClientProvider = FakeMatrixClientProvider(), + imageLoaderHolder = FakeImageLoaderHolder(), ) } } 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 18d8870ac3..e9f0516aa2 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 @@ -23,6 +23,7 @@ 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 import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent @@ -131,7 +132,8 @@ class NotificationFactoryTest { val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT))) val result = roomWithMessage.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = FakeImageLoader(), ) assertThat(result).isEqualTo(listOf(expectedNotification)) @@ -143,7 +145,8 @@ class NotificationFactoryTest { val emptyRoom = mapOf(A_ROOM_ID to events) val result = emptyRoom.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = FakeImageLoader(), ) assertThat(result).isEqualTo( @@ -160,7 +163,8 @@ class NotificationFactoryTest { val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)))) val result = redactedRoom.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = FakeImageLoader(), ) assertThat(result).isEqualTo( @@ -190,7 +194,8 @@ class NotificationFactoryTest { ) val result = roomWithRedactedMessage.toNotifications( - MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + imageLoader = FakeImageLoader(), ) 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 80875406c7..46dcc0035f 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 @@ -21,6 +21,7 @@ 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 +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent @@ -197,7 +198,8 @@ class NotificationRendererTest { notificationRenderer.render( MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, - eventsToProcess = AN_EVENT_LIST + eventsToProcess = AN_EVENT_LIST, + imageLoader = FakeImageLoader(), ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt index 46c41454d3..cb25e14c40 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreatorTest.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.push.impl.notifications.factories.createNotificationCreator +import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import io.element.android.services.toolbox.impl.strings.AndroidStringProvider @@ -58,6 +59,7 @@ class RoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, + imageLoader = FakeImageLoader(), ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() @@ -84,6 +86,7 @@ class RoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, + imageLoader = FakeImageLoader(), ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() @@ -170,6 +173,7 @@ class RoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, + imageLoader = imageLoader, ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() @@ -196,6 +200,7 @@ class RoomGroupMessageCreatorTest { aNotifiableMessageEvent(timestamp = A_TIMESTAMP + 10), ), roomId = A_ROOM_ID, + imageLoader = FakeImageLoader(), ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() @@ -223,6 +228,7 @@ class RoomGroupMessageCreatorTest { ), ), roomId = A_ROOM_ID, + imageLoader = FakeImageLoader(), ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() @@ -249,6 +255,7 @@ class RoomGroupMessageCreatorTest { ), ), roomId = A_ROOM_ID, + imageLoader = FakeImageLoader(), ) val resultMetaWithoutFormatting = result.meta.copy( summaryLine = result.meta.summaryLine.toString() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt new file mode 100644 index 0000000000..2467d15232 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoader.kt @@ -0,0 +1,47 @@ +/* + * 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.fake + +import coil.ComponentRegistry +import coil.ImageLoader +import coil.disk.DiskCache +import coil.memory.MemoryCache +import coil.request.DefaultRequestOptions +import coil.request.Disposable +import coil.request.ImageRequest +import coil.request.ImageResult + +class FakeImageLoader : ImageLoader { + override val components: ComponentRegistry = error("Fake class") + override val defaults: DefaultRequestOptions = error("Fake class") + override val diskCache: DiskCache? = null + override val memoryCache: MemoryCache? = null + + override fun enqueue(request: ImageRequest): Disposable { + error("Fake class") + } + + override suspend fun execute(request: ImageRequest): ImageResult { + error("Fake class") + } + + override fun newBuilder(): ImageLoader.Builder { + error("Fake class") + } + + override fun shutdown() = Unit +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt similarity index 61% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt rename to libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt index 919971b307..b6b3088a25 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/di/MatrixUIBindings.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeImageLoaderHolder.kt @@ -14,13 +14,14 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.ui.di +package io.element.android.libraries.push.impl.notifications.fake -import com.squareup.anvil.annotations.ContributesTo -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.ui.media.LoggedInImageLoaderFactory +import coil.ImageLoader +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.push.impl.notifications.ImageLoaderHolder -@ContributesTo(SessionScope::class) -interface MatrixUIBindings { - fun loggedInImageLoaderFactory(): LoggedInImageLoaderFactory +class FakeImageLoaderHolder : ImageLoaderHolder { + override fun get(client: MatrixClient): ImageLoader { + return FakeImageLoader() + } } 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 60b9e10c3d..9c7755aa9d 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 @@ -40,7 +40,7 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - coEvery { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications + coEvery { groupedEvents.roomEvents.toNotifications(matrixUser, any()) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications every { groupedEvents.fallbackEvents.toNotifications() } returns fallbackNotifications 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 b896737e6f..946b0ab0db 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 @@ -39,6 +39,7 @@ class FakeRoomGroupMessageCreator { currentUser = matrixUser, events = events, roomId = roomId, + imageLoader = any(), ) } returns mockMessage return mockMessage