Browse Source

Introduce MatrixMediaSource

feature/jme/open-room-member-details-when-clicking-on-user-data
ganfra 1 year ago
parent
commit
4236b69705
  1. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
  2. 17
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt
  3. 81
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
  4. 30
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
  5. 8
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt
  6. 7
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt
  7. 33
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt
  8. 42
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt
  9. 10
      features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultRoomLastMessageFormatterTests.kt
  10. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/FileInfo.kt
  11. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/ImageInfo.kt
  12. 10
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt
  13. 25
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaSource.kt
  14. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt
  15. 13
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
  16. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt
  17. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt
  18. 5
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt
  19. 20
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
  20. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt
  21. 9
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
  22. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt
  23. 13
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt
  24. 13
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt
  25. 12
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt
  26. 4
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt
  27. 7
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt

@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
private fun Modifier.defaultContentPadding(): Modifier = padding(
horizontal = 12.dp, vertical = 6.dp
@ -64,5 +65,9 @@ fun TimelineItemEventContentView( @@ -64,5 +65,9 @@ fun TimelineItemEventContentView(
content = content,
modifier = modifier,
)
is TimelineItemVideoContent -> TimelineItemVideoView(
content = content,
modifier = modifier
)
}
}

17
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt

@ -18,39 +18,38 @@ package io.element.android.features.messages.impl.timeline.components.event @@ -18,39 +18,38 @@ package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
import io.element.android.libraries.designsystem.components.blurhash.BlurHashAsyncImage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import kotlin.math.min
@Composable
fun TimelineItemImageView(
content: TimelineItemImageContent,
modifier: Modifier = Modifier,
) {
val widthPercent = if (content.aspectRatio > 1f) {
1f
} else {
0.7f
}
val maxHeight = min(300, content.height ?: Int.MAX_VALUE)
Box(
modifier = modifier
.fillMaxWidth(widthPercent)
.heightIn(max = maxHeight.dp)
.aspectRatio(content.aspectRatio),
contentAlignment = Alignment.Center,
) {
BlurHashAsyncImage(
blurHash = content.blurhash,
model = content.mediaRequestData,
contentScale = ContentScale.Crop,
model = MediaRequestData(content.mediaSource, MediaRequestData.Kind.Content),
contentScale = ContentScale.Fit,
)
}
}

81
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
/*
* 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.features.messages.impl.timeline.components.event
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.libraries.designsystem.components.blurhash.BlurHashAsyncImage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import kotlin.math.min
@Composable
fun TimelineItemVideoView(
content: TimelineItemVideoContent,
modifier: Modifier = Modifier,
) {
val maxHeight = min(300, content.height ?: Int.MAX_VALUE)
Box(
modifier = modifier
.heightIn(max = maxHeight.dp)
.aspectRatio(content.aspectRatio),
contentAlignment = Alignment.Center,
) {
BlurHashAsyncImage(
blurHash = content.blurhash,
model = MediaRequestData(content.thumbnailSource, MediaRequestData.Kind.Content),
contentScale = ContentScale.Fit,
)
Image(
painterResource(id = androidx.media3.ui.R.drawable.exo_ic_play_circle_filled),
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
)
}
}
@Preview
@Composable
internal fun TimelineItemVideoViewLightPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) =
ElementPreviewLight { ContentToPreview(content) }
@Preview
@Composable
internal fun TimelineItemVideoViewDarkPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) =
ElementPreviewDark { ContentToPreview(content) }
@Composable
private fun ContentToPreview(content: TimelineItemImageContent) {
TimelineItemImageView(content)
}

30
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt

@ -22,13 +22,14 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -22,13 +22,14 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent
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.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
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 io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import javax.inject.Inject
class TimelineItemContentMessageFactory @Inject constructor() {
@ -49,10 +50,29 @@ class TimelineItemContentMessageFactory @Inject constructor() { @@ -49,10 +50,29 @@ class TimelineItemContentMessageFactory @Inject constructor() {
}
TimelineItemImageContent(
body = messageType.body,
mediaRequestData = MediaRequestData(
url = messageType.url,
kind = MediaRequestData.Kind.Content
),
height = messageType.info?.height?.toInt(),
width = messageType.info?.width?.toInt(),
mediaSource = messageType.source,
blurhash = messageType.info?.blurhash,
aspectRatio = aspectRatio
)
}
is VideoMessageType -> {
val height = messageType.info?.height?.toFloat()
val width = messageType.info?.width?.toFloat()
val aspectRatio = if (height != null && width != null) {
width / height
} else {
0.7f
}
TimelineItemVideoContent(
body = messageType.body,
thumbnailSource = messageType.info?.thumbnailSource,
videoSource = messageType.source,
mimetype = messageType.info?.mimetype,
width = messageType.info?.width?.toInt(),
height = messageType.info?.height?.toInt(),
duration = messageType.info?.duration ?: 0L,
blurhash = messageType.info?.blurhash,
aspectRatio = aspectRatio
)

8
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt

@ -16,13 +16,15 @@ @@ -16,13 +16,15 @@
package io.element.android.features.messages.impl.timeline.model.event
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
data class TimelineItemImageContent(
val body: String,
val mediaRequestData: MediaRequestData,
val mediaSource: MatrixMediaSource,
val blurhash: String?,
val width: Int?,
val height: Int?,
val aspectRatio: Float
) : TimelineItemEventContent{
) : TimelineItemEventContent {
override val type: String = "TimelineItemImageContent"
}

7
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContentProvider.kt

@ -17,6 +17,7 @@ @@ -17,6 +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.MatrixMediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> {
@ -30,7 +31,9 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineI @@ -30,7 +31,9 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineI
fun aTimelineItemImageContent() = TimelineItemImageContent(
body = "a body",
mediaRequestData = MediaRequestData(url = "", kind = MediaRequestData.Kind.Content),
blurhash = null,
mediaSource = MatrixMediaSource(""),
blurhash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
aspectRatio = 0.5f,
height = null,
width = null
)

33
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContent.kt

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
/*
* 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.features.messages.impl.timeline.model.event
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
data class TimelineItemVideoContent(
val body: String,
val duration: Long,
val videoSource: MatrixMediaSource,
val thumbnailSource: MatrixMediaSource?,
val aspectRatio: Float,
val blurhash: String?,
val height: Int?,
val width: Int?,
val mimetype: String?,
) : TimelineItemEventContent {
override val type: String = "TimelineItemImageContent"
}

42
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVideoContentProvider.kt

@ -0,0 +1,42 @@ @@ -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.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineItemVideoContent> {
override val values: Sequence<TimelineItemVideoContent>
get() = sequenceOf(
aTimelineItemVideoContent(),
aTimelineItemVideoContent().copy(aspectRatio = 1.0f),
aTimelineItemVideoContent().copy(aspectRatio = 1.5f),
)
}
fun aTimelineItemVideoContent() = TimelineItemVideoContent(
body = "a video",
thumbnailSource = MatrixMediaSource(url = ""),
blurhash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
aspectRatio = 0.5f,
duration = 0,
videoSource = MatrixMediaSource(""),
height = null,
width = null,
mimetype = null
)

10
features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/DefaultRoomLastMessageFormatterTests.kt

@ -21,6 +21,7 @@ import androidx.compose.ui.text.AnnotatedString @@ -21,6 +21,7 @@ import androidx.compose.ui.text.AnnotatedString
import com.google.common.truth.Truth
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
@ -145,12 +146,13 @@ class DefaultRoomLastMessageFormatterTests { @@ -145,12 +146,13 @@ class DefaultRoomLastMessageFormatterTests {
fun createMessageContent(type: MessageType): MessageContent {
return MessageContent(body, null, false, type)
}
val sharedContentMessagesTypes = arrayOf(
TextMessageType(body, null),
VideoMessageType(body, "url", null),
AudioMessageType(body, "url", null),
ImageMessageType(body, "url", null),
FileMessageType(body, "url", null),
VideoMessageType(body, MatrixMediaSource("url"), null),
AudioMessageType(body, MatrixMediaSource("url"), null),
ImageMessageType(body, MatrixMediaSource("url"), null),
FileMessageType(body, MatrixMediaSource("url"), null),
NoticeMessageType(body, null),
EmoteMessageType(body, null),
)

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/FileInfo.kt

@ -20,5 +20,5 @@ data class FileInfo( @@ -20,5 +20,5 @@ data class FileInfo(
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?
val thumbnailSource: MatrixMediaSource?
)

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/ImageInfo.kt

@ -22,6 +22,6 @@ data class ImageInfo( @@ -22,6 +22,6 @@ data class ImageInfo(
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?,
val thumbnailSource: MatrixMediaSource?,
val blurhash: String?
)

10
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaLoader.kt

@ -16,14 +16,14 @@ @@ -16,14 +16,14 @@
package io.element.android.libraries.matrix.api.media
import java.nio.file.Path
import android.net.Uri
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<ByteArray>
suspend fun loadMediaContent(source: MatrixMediaSource): Result<ByteArray>
/**
* @param url to fetch the data for.
@ -31,12 +31,12 @@ interface MatrixMediaLoader { @@ -31,12 +31,12 @@ interface MatrixMediaLoader {
* @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<ByteArray>
suspend fun loadMediaThumbnail(source: MatrixMediaSource, width: Long, height: Long): Result<ByteArray>
/**
* @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.
* @return a [Result] of [Uri]. It's the uri of the downloaded file.
*/
suspend fun loadMediaFile(url: String, mimeType: String?): Result<Path>
suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri>
}

25
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MatrixMediaSource.kt

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
/*
* 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 android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class MatrixMediaSource(
val url: String
) : Parcelable

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/VideoInfo.kt

@ -23,6 +23,6 @@ data class VideoInfo( @@ -23,6 +23,6 @@ data class VideoInfo(
val mimetype: String?,
val size: Long?,
val thumbnailInfo: ThumbnailInfo?,
val thumbnailUrl: String?,
val thumbnailSource: MatrixMediaSource?,
val blurhash: String?
)

13
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt

@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.UserId @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
import io.element.android.libraries.matrix.api.media.VideoInfo
sealed interface EventContent
@ -106,25 +107,25 @@ data class EmoteMessageType( @@ -106,25 +107,25 @@ data class EmoteMessageType(
data class ImageMessageType(
val body: String,
val url: String,
val source: MatrixMediaSource,
val info: ImageInfo?
) : MessageType
data class AudioMessageType(
var body: String,
var url: String,
var info: AudioInfo?
val body: String,
val source: MatrixMediaSource,
val info: AudioInfo?
) : MessageType
data class VideoMessageType(
val body: String,
val url: String,
val source: MatrixMediaSource,
val info: VideoInfo?
) : MessageType
data class FileMessageType(
val body: String,
val url: String,
val source: MatrixMediaSource,
val info: FileInfo?
) : MessageType

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/FileInfo.kt

@ -17,13 +17,11 @@ @@ -17,13 +17,11 @@
package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
import org.matrix.rustcomponents.sdk.FileInfo as RustFileInfo
import org.matrix.rustcomponents.sdk.ThumbnailInfo as RustThumbnailInfo
fun RustFileInfo.map(): FileInfo = FileInfo(
mimetype = mimetype,
size = size?.toLong(),
thumbnailInfo = thumbnailInfo?.map(),
thumbnailUrl = thumbnailSource?.useUrl()
thumbnailSource = thumbnailSource?.map()
)

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt

@ -25,6 +25,6 @@ fun RustImageInfo.map(): ImageInfo = ImageInfo( @@ -25,6 +25,6 @@ fun RustImageInfo.map(): ImageInfo = ImageInfo(
mimetype = mimetype,
size = size?.toLong(),
thumbnailInfo = thumbnailInfo?.map(),
thumbnailUrl = thumbnailSource?.useUrl(),
thumbnailSource = thumbnailSource?.map(),
blurhash = blurhash
)

5
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/MediaSource.kt

@ -16,7 +16,8 @@ @@ -16,7 +16,8 @@
package io.element.android.libraries.matrix.impl.media
import org.matrix.rustcomponents.sdk.MediaSource
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.MediaSource as RustMediaSource
fun MediaSource.useUrl(): String = use { it.url() }
fun RustMediaSource.map(): MatrixMediaSource = use { MatrixMediaSource(it.url()) }

20
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt

@ -16,14 +16,15 @@ @@ -16,14 +16,15 @@
package io.element.android.libraries.matrix.impl.media
import android.net.Uri
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
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
import java.io.File
class RustMediaLoader(
private val dispatchers: CoroutineDispatchers,
@ -31,10 +32,10 @@ class RustMediaLoader( @@ -31,10 +32,10 @@ class RustMediaLoader(
) : MatrixMediaLoader {
@OptIn(ExperimentalUnsignedTypes::class)
override suspend fun loadMediaContent(url: String): Result<ByteArray> =
override suspend fun loadMediaContent(source: MatrixMediaSource): Result<ByteArray> =
withContext(dispatchers.io) {
runCatching {
mediaSourceFromUrl(url).use { source ->
mediaSourceFromUrl(source.url).use { source ->
innerClient.getMediaContent(source).toUByteArray().toByteArray()
}
}
@ -42,13 +43,13 @@ class RustMediaLoader( @@ -42,13 +43,13 @@ class RustMediaLoader(
@OptIn(ExperimentalUnsignedTypes::class)
override suspend fun loadMediaThumbnail(
url: String,
source: MatrixMediaSource,
width: Long,
height: Long
): Result<ByteArray> =
withContext(dispatchers.io) {
runCatching {
mediaSourceFromUrl(url).use { mediaSource ->
mediaSourceFromUrl(source.url).use { mediaSource ->
innerClient.getMediaThumbnail(
mediaSource = mediaSource,
width = width.toULong(),
@ -58,15 +59,16 @@ class RustMediaLoader( @@ -58,15 +59,16 @@ class RustMediaLoader(
}
}
override suspend fun loadMediaFile(url: String, mimeType: String?): Result<Path> =
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri> =
withContext(dispatchers.io) {
runCatching {
mediaSourceFromUrl(url).use { mediaSource ->
mediaSourceFromUrl(source.url).use { mediaSource ->
innerClient.getMediaFile(
mediaSource = mediaSource,
mimeType = mimeType ?: "application/octet-stream"
).use {
Path(it.path())
val file = File(it.path())
Uri.fromFile(file)
}
}
}

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/VideoInfo.kt

@ -26,6 +26,6 @@ fun RustVideoInfo.map(): VideoInfo = VideoInfo( @@ -26,6 +26,6 @@ fun RustVideoInfo.map(): VideoInfo = VideoInfo(
mimetype = mimetype,
size = size?.toLong(),
thumbnailInfo = thumbnailInfo?.map(),
thumbnailUrl = thumbnailSource?.useUrl(),
thumbnailSource = thumbnailSource?.map(),
blurhash = blurhash
)

9
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt

@ -29,7 +29,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy @@ -29,7 +29,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.media.useUrl
import org.matrix.rustcomponents.sdk.Message
import org.matrix.rustcomponents.sdk.MessageType
import org.matrix.rustcomponents.sdk.use
@ -42,13 +41,13 @@ class EventMessageMapper { @@ -42,13 +41,13 @@ class EventMessageMapper {
val type = message.msgtype().use { type ->
when (type) {
is MessageType.Audio -> {
AudioMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
AudioMessageType(type.content.body, type.content.source.map(), type.content.info?.map())
}
is MessageType.File -> {
FileMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
FileMessageType(type.content.body, type.content.source.map(), type.content.info?.map())
}
is MessageType.Image -> {
ImageMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
ImageMessageType(type.content.body, type.content.source.map(), type.content.info?.map())
}
is MessageType.Notice -> {
NoticeMessageType(type.content.body, type.content.formatted?.map())
@ -60,7 +59,7 @@ class EventMessageMapper { @@ -60,7 +59,7 @@ class EventMessageMapper {
EmoteMessageType(type.content.body, type.content.formatted?.map())
}
is MessageType.Video -> {
VideoMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
VideoMessageType(type.content.body, type.content.source.map(), type.content.info?.map())
}
null -> {
UnknownMessageType

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.impl.timeline.item.event
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
@ -26,7 +27,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.RedactedConte @@ -26,7 +27,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.RedactedConte
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import io.element.android.libraries.matrix.impl.media.map
@ -88,7 +88,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap @@ -88,7 +88,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
StickerContent(
body = kind.body,
info = kind.info.map(),
url = kind.url
url = kind.url,
)
}
is TimelineItemContentKind.UnableToDecrypt -> {

13
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt

@ -16,15 +16,16 @@ @@ -16,15 +16,16 @@
package io.element.android.libraries.matrix.test.media
import android.net.Uri
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import java.nio.file.Path
import kotlin.io.path.Path
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
import java.io.File
class FakeMediaLoader : MatrixMediaLoader {
var shouldFail = false
override suspend fun loadMediaContent(url: String): Result<ByteArray> {
override suspend fun loadMediaContent(source: MatrixMediaSource): Result<ByteArray> {
return if (shouldFail) {
Result.failure(RuntimeException())
} else {
@ -32,7 +33,7 @@ class FakeMediaLoader : MatrixMediaLoader { @@ -32,7 +33,7 @@ class FakeMediaLoader : MatrixMediaLoader {
}
}
override suspend fun loadMediaThumbnail(url: String, width: Long, height: Long): Result<ByteArray> {
override suspend fun loadMediaThumbnail(source: MatrixMediaSource, width: Long, height: Long): Result<ByteArray> {
return if (shouldFail) {
Result.failure(RuntimeException())
} else {
@ -40,11 +41,11 @@ class FakeMediaLoader : MatrixMediaLoader { @@ -40,11 +41,11 @@ class FakeMediaLoader : MatrixMediaLoader {
}
}
override suspend fun loadMediaFile(url: String, mimeType: String?): Result<Path> {
override suspend fun loadMediaFile(source: MatrixMediaSource, mimeType: String?): Result<Uri> {
return if (shouldFail) {
Result.failure(RuntimeException())
} else {
return Result.success(Path("path"))
return Result.success(Uri.fromFile(File("path")))
}
}
}

13
libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt

@ -17,13 +17,12 @@ @@ -17,13 +17,12 @@
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.MatrixMediaSource
import kotlin.math.roundToLong
fun AvatarData.toMediaRequestData(): MediaRequestData? {
return url?.let {
MediaRequestData(
url = it,
kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong())
)
}
fun AvatarData.toMediaRequestData(): MediaRequestData {
return MediaRequestData(
source = url?.let { MatrixMediaSource(it) },
kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong())
)
}

12
libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt

@ -38,19 +38,15 @@ internal class CoilMediaFetcher( @@ -38,19 +38,15 @@ internal class CoilMediaFetcher(
ByteBuffer.wrap(data)
}.map { byteBuffer ->
imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
}
.fold(
{ result -> result },
{ failure -> throw failure }
)
}.getOrThrow()
}
private suspend fun loadMedia(): Result<ByteArray> {
if (mediaData == null) return Result.failure(IllegalStateException("No media data to fetch."))
if (mediaData?.source == 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.Content -> mediaLoader.loadMediaContent(source = mediaData.source)
is MediaRequestData.Kind.Thumbnail -> mediaLoader.loadMediaThumbnail(
url = mediaData.url,
source = mediaData.source,
width = mediaData.kind.width,
height = mediaData.kind.height
)

4
libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt

@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
package io.element.android.libraries.matrix.ui.media
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
data class MediaRequestData(
val url: String,
val source: MatrixMediaSource?,
val kind: Kind
) {

7
libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt

@ -22,7 +22,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -22,7 +22,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
internal class AvatarDataKeyer : Keyer<AvatarData> {
override fun key(data: AvatarData, options: Options): String? {
return data.toMediaRequestData()?.toKey()
return data.toMediaRequestData().toKey()
}
}
@ -32,4 +32,7 @@ internal class MediaRequestDataKeyer : Keyer<MediaRequestData> { @@ -32,4 +32,7 @@ internal class MediaRequestDataKeyer : Keyer<MediaRequestData> {
}
}
private fun MediaRequestData.toKey() = "${url}_${kind}"
private fun MediaRequestData.toKey(): String? {
if (source == null) return null
return "${source.url}_${kind}"
}

Loading…
Cancel
Save