diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index ffc30e012f..ae2d18f541 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -55,10 +55,10 @@ fun TimelineItemImageView( .aspectRatio(content.aspectRatio), contentAlignment = Alignment.Center, ) { - val isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) } + val isLoading = rememberSaveable(content.mediaRequestData) { mutableStateOf(true) } val context = LocalContext.current val model = ImageRequest.Builder(context) - .data(content.imageMeta) + .data(content.mediaRequestData) .build() AsyncImage( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index c0b4a4f5cc..57d6489592 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -23,12 +23,12 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent import io.element.android.features.messages.impl.timeline.util.toHtmlDocument -import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.ui.media.MediaRequestData import javax.inject.Inject class TimelineItemContentMessageFactory @Inject constructor() { @@ -49,9 +49,9 @@ class TimelineItemContentMessageFactory @Inject constructor() { } TimelineItemImageContent( body = messageType.body, - imageMeta = MediaResolver.Meta( + mediaRequestData = MediaRequestData( url = messageType.url, - kind = MediaResolver.Kind.Content + kind = MediaRequestData.Kind.Content ), blurhash = messageType.info?.blurhash, aspectRatio = aspectRatio diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt index 8013ca8a65..3f00a3c183 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt @@ -16,11 +16,11 @@ package io.element.android.features.messages.impl.timeline.model.event -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.ui.media.MediaRequestData data class TimelineItemImageContent( val body: String, - val imageMeta: MediaResolver.Meta, + val mediaRequestData: MediaRequestData, val blurhash: String?, val aspectRatio: Float ) : TimelineItemEventContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt index ba79c9988f..e1fb9d06e6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt @@ -17,7 +17,7 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.ui.media.MediaRequestData open class TimelineItemImageContentProvider : PreviewParameterProvider { override val values: Sequence @@ -30,7 +30,7 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider suspend fun createDM(userId: UserId): Result fun startSync() fun stopSync() - fun mediaResolver(): MediaResolver fun sessionVerificationService(): SessionVerificationService fun pushersService(): PushersService fun notificationService(): NotificationService suspend fun logout() suspend fun loadUserDisplayName(): Result suspend fun loadUserAvatarURLString(): Result - suspend fun loadMediaContent(url: String): Result - suspend fun loadMediaThumbnail( - url: String, - width: Long, - height: Long - ): Result fun onSlidingSyncUpdate() diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt new file mode 100644 index 0000000000..4c068ab744 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt @@ -0,0 +1,42 @@ +/* + * 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.api.media + +import java.nio.file.Path + +interface MatrixMediaLoader { + /** + * @param url to fetch the content for. + * @return a [Result] of ByteArray. It contains the binary data for the media. + */ + suspend fun loadMediaContent(url: String): Result + + /** + * @param url to fetch the data for. + * @param width: the desired width for rescaling the media as thumbnail + * @param height: the desired height for rescaling the media as thumbnail + * @return a [Result] of ByteArray. It contains the binary data for the media. + */ + suspend fun loadMediaThumbnail(url: String, width: Long, height: Long): Result + + /** + * @param url to fetch the data for. + * @param mimeType: optional mime type + * @return a [Result] of [Path]. It's the path to the downloaded file. + */ + suspend fun loadMediaFile(url: String, mimeType: String?): Result +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt deleted file mode 100644 index 058c510ffd..0000000000 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaResolver.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022 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.api.media - -interface MediaResolver { - - sealed interface Kind { - data class Thumbnail(val width: Int, val height: Int) : Kind { - constructor(size: Int) : this(size, size) - } - - object Content : Kind - } - - data class Meta( - val url: String?, - val kind: Kind - ) - - suspend fun resolve(url: String?, kind: Kind): Result - -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 63bd4e13a2..e6f6068c0d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -23,14 +23,14 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import io.element.android.libraries.matrix.api.verification.SessionVerificationService -import io.element.android.libraries.matrix.impl.media.RustMediaResolver +import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.notification.RustNotificationService import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.RustMatrixRoom @@ -53,7 +53,6 @@ import org.matrix.rustcomponents.sdk.SlidingSyncListBuilder import org.matrix.rustcomponents.sdk.SlidingSyncMode import org.matrix.rustcomponents.sdk.SlidingSyncRequestListFilters import org.matrix.rustcomponents.sdk.TaskHandle -import org.matrix.rustcomponents.sdk.mediaSourceFromUrl import org.matrix.rustcomponents.sdk.use import timber.log.Timber import java.io.File @@ -172,9 +171,12 @@ class RustMatrixClient constructor( override val invitesDataSource: RoomSummaryDataSource get() = rustInvitesDataSource + private val rustMediaLoader = RustMediaLoader(dispatchers, client) + override val mediaLoader: MatrixMediaLoader + get() = rustMediaLoader + private var slidingSyncObserverToken: TaskHandle? = null - private val mediaResolver = RustMediaResolver(this) private val isSyncing = AtomicBoolean(false) private val roomMembershipObserver = RoomMembershipObserver() @@ -254,8 +256,6 @@ class RustMatrixClient constructor( return createRoom(createRoomParams) } - override fun mediaResolver(): MediaResolver = mediaResolver - override fun sessionVerificationService(): SessionVerificationService = verificationService override fun pushersService(): PushersService = pushersService @@ -310,34 +310,6 @@ class RustMatrixClient constructor( } } - @OptIn(ExperimentalUnsignedTypes::class) - override suspend fun loadMediaContent(url: String): Result = - withContext(dispatchers.io) { - runCatching { - mediaSourceFromUrl(url).use { source -> - client.getMediaContent(source).toUByteArray().toByteArray() - } - } - } - - @OptIn(ExperimentalUnsignedTypes::class) - override suspend fun loadMediaThumbnail( - url: String, - width: Long, - height: Long - ): Result = - withContext(dispatchers.io) { - runCatching { - mediaSourceFromUrl(url).use { mediaSource -> - client.getMediaThumbnail( - mediaSource = mediaSource, - width = width.toULong(), - height = height.toULong() - ).toUByteArray().toByteArray() - } - } - } - override fun onSlidingSyncUpdate() { if (!verificationService.isReady.value) { try { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt new file mode 100644 index 0000000000..d4b8f4f72e --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -0,0 +1,75 @@ +/* + * 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.media + +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader +import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.mediaSourceFromUrl +import org.matrix.rustcomponents.sdk.use +import java.nio.file.Path +import kotlin.io.path.Path + +class RustMediaLoader( + private val dispatchers: CoroutineDispatchers, + private val innerClient: Client +) : MatrixMediaLoader { + + @OptIn(ExperimentalUnsignedTypes::class) + override suspend fun loadMediaContent(url: String): Result = + withContext(dispatchers.io) { + runCatching { + mediaSourceFromUrl(url).use { source -> + innerClient.getMediaContent(source).toUByteArray().toByteArray() + } + } + } + + @OptIn(ExperimentalUnsignedTypes::class) + override suspend fun loadMediaThumbnail( + url: String, + width: Long, + height: Long + ): Result = + withContext(dispatchers.io) { + runCatching { + mediaSourceFromUrl(url).use { mediaSource -> + innerClient.getMediaThumbnail( + mediaSource = mediaSource, + width = width.toULong(), + height = height.toULong() + ).toUByteArray().toByteArray() + } + } + } + + override suspend fun loadMediaFile(url: String, mimeType: String?): Result = + withContext(dispatchers.io) { + runCatching { + mediaSourceFromUrl(url).use { mediaSource -> + innerClient.getMediaFile( + source = mediaSource, + mimeType = mimeType ?: "application/octet-stream" + ).use { + Path(it.path()) + } + } + } + + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt deleted file mode 100644 index a708dc0d31..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaResolver.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.media - -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.media.MediaResolver -import java.lang.IllegalStateException - -internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver { - - override suspend fun resolve(url: String?, kind: MediaResolver.Kind): Result { - if (url.isNullOrEmpty()) return Result.failure(IllegalStateException("The url is null or empty")) - return when (kind) { - is MediaResolver.Kind.Content -> client.loadMediaContent(url) - is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnail( - url, - kind.width.toLong(), - kind.height.toLong() - ) - } - } -} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index d33bd5c939..0167f2f846 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -21,14 +21,14 @@ 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.createroom.CreateRoomParameters -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource import io.element.android.libraries.matrix.api.verification.SessionVerificationService -import io.element.android.libraries.matrix.test.media.FakeMediaResolver +import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.notification.FakeNotificationService import io.element.android.libraries.matrix.test.pushers.FakePushersService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -42,6 +42,7 @@ class FakeMatrixClient( private val userAvatarURLString: Result = Result.success(AN_AVATAR_URL), override val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource(), override val invitesDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource(), + override val mediaLoader: MatrixMediaLoader = FakeMediaLoader(), private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(), private val pushersService: FakePushersService = FakePushersService(), private val notificationService: FakeNotificationService = FakeNotificationService(), @@ -77,10 +78,6 @@ class FakeMatrixClient( override fun stopSync() = Unit - override fun mediaResolver(): MediaResolver { - return FakeMediaResolver() - } - override suspend fun logout() { delay(100) logoutFailure?.let { throw it } @@ -96,14 +93,6 @@ class FakeMatrixClient( return userAvatarURLString } - override suspend fun loadMediaContent(url: String): Result { - return Result.success(ByteArray(0)) - } - - override suspend fun loadMediaThumbnail(url: String, width: Long, height: Long): Result { - return Result.success(ByteArray(0)) - } - override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService override fun pushersService(): PushersService = pushersService diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt new file mode 100644 index 0000000000..fc14de03da --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt @@ -0,0 +1,50 @@ +/* + * 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.test.media + +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader +import java.nio.file.Path +import kotlin.io.path.Path + +class FakeMediaLoader : MatrixMediaLoader { + + var shouldFail = false + + override suspend fun loadMediaContent(url: String): Result { + return if (shouldFail) { + Result.failure(RuntimeException()) + } else { + return Result.success(ByteArray(0)) + } + } + + override suspend fun loadMediaThumbnail(url: String, width: Long, height: Long): Result { + return if (shouldFail) { + Result.failure(RuntimeException()) + } else { + return Result.success(ByteArray(0)) + } + } + + override suspend fun loadMediaFile(url: String, mimeType: String?): Result { + return if (shouldFail) { + Result.failure(RuntimeException()) + } else { + return Result.success(Path("path")) + } + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt deleted file mode 100644 index c1b6e52003..0000000000 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaResolver.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.test.media - -import io.element.android.libraries.matrix.api.media.MediaResolver - -class FakeMediaResolver : MediaResolver { - - private var result: Result = Result.success(ByteArray(0)) - - fun givenResult(result: Result) { - this.result = result - } - - override suspend fun resolve(url: String?, kind: MediaResolver.Kind): Result { - return result - } -} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt similarity index 74% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt index 6301503797..37a7f02922 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt @@ -17,9 +17,13 @@ package io.element.android.libraries.matrix.ui.media import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.media.MediaResolver -import kotlin.math.roundToInt +import kotlin.math.roundToLong -fun AvatarData.toMetadata(): MediaResolver.Meta { - return MediaResolver.Meta(url = url, kind = MediaResolver.Kind.Thumbnail(size.dp.value.roundToInt())) +fun AvatarData.toMediaRequestData(): MediaRequestData? { + return url?.let { + MediaRequestData( + url = it, + kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong()) + ) + } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt similarity index 60% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index 4e1821e8cf..81b47c4022 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -22,20 +22,20 @@ import coil.fetch.Fetcher import coil.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import java.nio.ByteBuffer -internal class MediaFetcher( - private val mediaResolver: MediaResolver, - private val meta: MediaResolver.Meta, +internal class CoilMediaFetcher( + private val mediaLoader: MatrixMediaLoader, + private val mediaData: MediaRequestData?, private val options: Options, private val imageLoader: ImageLoader ) : Fetcher { override suspend fun fetch(): FetchResult? { - return mediaResolver.resolve(meta.url, meta.kind) - .map { byteArray -> - ByteBuffer.wrap(byteArray) + return loadMedia() + .map { data -> + ByteBuffer.wrap(data) }.map { byteBuffer -> imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch() } @@ -45,16 +45,28 @@ internal class MediaFetcher( ) } - class MetaFactory(private val client: MatrixClient) : - Fetcher.Factory { + private suspend fun loadMedia(): Result { + if (mediaData == null) return Result.failure(IllegalStateException("No media data to fetch.")) + return when (mediaData.kind) { + is MediaRequestData.Kind.Content -> mediaLoader.loadMediaContent(url = mediaData.url) + is MediaRequestData.Kind.Thumbnail -> mediaLoader.loadMediaThumbnail( + url = mediaData.url, + width = mediaData.kind.width, + height = mediaData.kind.height + ) + } + } + + class MediaRequestDataFactory(private val client: MatrixClient) : + Fetcher.Factory { override fun create( - data: MediaResolver.Meta, + data: MediaRequestData, options: Options, imageLoader: ImageLoader ): Fetcher { - return MediaFetcher( - mediaResolver = client.mediaResolver(), - meta = data, + return CoilMediaFetcher( + mediaLoader = client.mediaLoader, + mediaData = data, options = options, imageLoader = imageLoader ) @@ -63,14 +75,15 @@ internal class MediaFetcher( class AvatarFactory(private val client: MatrixClient) : Fetcher.Factory { + override fun create( data: AvatarData, options: Options, imageLoader: ImageLoader ): Fetcher { - return MediaFetcher( - mediaResolver = client.mediaResolver(), - meta = data.toMetadata(), + return CoilMediaFetcher( + mediaLoader = client.mediaLoader, + mediaData = data.toMediaRequestData(), options = options, imageLoader = imageLoader ) 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 a52cfd380a..d1fab05544 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 @@ -34,10 +34,10 @@ class LoggedInImageLoaderFactory @Inject constructor( .Builder(context) .okHttpClient(okHttpClient) .components { - add(AvatarKeyer()) - add(MediaKeyer()) - add(MediaFetcher.AvatarFactory(matrixClient)) - add(MediaFetcher.MetaFactory(matrixClient)) + add(AvatarDataKeyer()) + add(MediaRequestDataKeyer()) + add(CoilMediaFetcher.AvatarFactory(matrixClient)) + add(CoilMediaFetcher.MediaRequestDataFactory(matrixClient)) } .build() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt similarity index 62% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt index ca840ee44f..f2f9bf9794 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaResolver.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. @@ -14,23 +14,19 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.media +package io.element.android.libraries.matrix.ui.media -interface MediaResolver { +data class MediaRequestData( + val url: String, + val kind: Kind +) { sealed interface Kind { - data class Thumbnail(val width: Int, val height: Int) : Kind { - constructor(size: Int) : this(size, size) + data class Thumbnail(val width: Long, val height: Long) : Kind { + constructor(size: Long) : this(size, size) } object Content : Kind } - - data class Meta( - val url: String?, - val kind: Kind - ) - - suspend fun resolve(url: String?, kind: Kind): ByteArray? - } + diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt similarity index 72% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt index 2d4ab683b1..56ff15fd44 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaKeyer.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt @@ -19,18 +19,17 @@ package io.element.android.libraries.matrix.ui.media import coil.key.Keyer import coil.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.media.MediaResolver -internal class AvatarKeyer : Keyer { +internal class AvatarDataKeyer : Keyer { override fun key(data: AvatarData, options: Options): String? { - return data.toMetadata().toKey() + return data.toMediaRequestData()?.toKey() } } -internal class MediaKeyer : Keyer { - override fun key(data: MediaResolver.Meta, options: Options): String? { +internal class MediaRequestDataKeyer : Keyer { + override fun key(data: MediaRequestData, options: Options): String? { return data.toKey() } } -private fun MediaResolver.Meta.toKey() = "${url}_${kind}" +private fun MediaRequestData.toKey() = "${url}_${kind}"