Introduce MatrixMediaSource
This commit is contained in:
parent
affb928801
commit
72dea2f817
27 changed files with 298 additions and 84 deletions
|
|
@ -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.TimelineItemRedactedContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
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.TimelineItemUnknownContent
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
|
|
||||||
private fun Modifier.defaultContentPadding(): Modifier = padding(
|
private fun Modifier.defaultContentPadding(): Modifier = padding(
|
||||||
horizontal = 12.dp, vertical = 6.dp
|
horizontal = 12.dp, vertical = 6.dp
|
||||||
|
|
@ -64,5 +65,9 @@ fun TimelineItemEventContentView(
|
||||||
content = content,
|
content = content,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
is TimelineItemVideoContent -> TimelineItemVideoView(
|
||||||
|
content = content,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.Box
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
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.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
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.TimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
|
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.components.blurhash.BlurHashAsyncImage
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemImageView(
|
fun TimelineItemImageView(
|
||||||
content: TimelineItemImageContent,
|
content: TimelineItemImageContent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val widthPercent = if (content.aspectRatio > 1f) {
|
val maxHeight = min(300, content.height ?: Int.MAX_VALUE)
|
||||||
1f
|
|
||||||
} else {
|
|
||||||
0.7f
|
|
||||||
}
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth(widthPercent)
|
.heightIn(max = maxHeight.dp)
|
||||||
.aspectRatio(content.aspectRatio),
|
.aspectRatio(content.aspectRatio),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
BlurHashAsyncImage(
|
BlurHashAsyncImage(
|
||||||
blurHash = content.blurhash,
|
blurHash = content.blurhash,
|
||||||
model = content.mediaRequestData,
|
model = MediaRequestData(content.mediaSource, MediaRequestData.Kind.Content),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Fit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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.TimelineItemNoticeContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
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.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.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.EmoteMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
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.MessageContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
|
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.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
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineItemContentMessageFactory @Inject constructor() {
|
class TimelineItemContentMessageFactory @Inject constructor() {
|
||||||
|
|
@ -49,10 +50,29 @@ class TimelineItemContentMessageFactory @Inject constructor() {
|
||||||
}
|
}
|
||||||
TimelineItemImageContent(
|
TimelineItemImageContent(
|
||||||
body = messageType.body,
|
body = messageType.body,
|
||||||
mediaRequestData = MediaRequestData(
|
height = messageType.info?.height?.toInt(),
|
||||||
url = messageType.url,
|
width = messageType.info?.width?.toInt(),
|
||||||
kind = MediaRequestData.Kind.Content
|
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,
|
blurhash = messageType.info?.blurhash,
|
||||||
aspectRatio = aspectRatio
|
aspectRatio = aspectRatio
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,15 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
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(
|
data class TimelineItemImageContent(
|
||||||
val body: String,
|
val body: String,
|
||||||
val mediaRequestData: MediaRequestData,
|
val mediaSource: MatrixMediaSource,
|
||||||
val blurhash: String?,
|
val blurhash: String?,
|
||||||
|
val width: Int?,
|
||||||
|
val height: Int?,
|
||||||
val aspectRatio: Float
|
val aspectRatio: Float
|
||||||
) : TimelineItemEventContent{
|
) : TimelineItemEventContent {
|
||||||
override val type: String = "TimelineItemImageContent"
|
override val type: String = "TimelineItemImageContent"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
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
|
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||||
|
|
||||||
open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> {
|
open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> {
|
||||||
|
|
@ -30,7 +31,9 @@ open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineI
|
||||||
|
|
||||||
fun aTimelineItemImageContent() = TimelineItemImageContent(
|
fun aTimelineItemImageContent() = TimelineItemImageContent(
|
||||||
body = "a body",
|
body = "a body",
|
||||||
mediaRequestData = MediaRequestData(url = "", kind = MediaRequestData.Kind.Content),
|
mediaSource = MatrixMediaSource(""),
|
||||||
blurhash = null,
|
blurhash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
|
||||||
aspectRatio = 0.5f,
|
aspectRatio = 0.5f,
|
||||||
|
height = null,
|
||||||
|
width = null
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.ui.text.AnnotatedString
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
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.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.AudioMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
||||||
|
|
@ -145,12 +146,13 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
fun createMessageContent(type: MessageType): MessageContent {
|
fun createMessageContent(type: MessageType): MessageContent {
|
||||||
return MessageContent(body, null, false, type)
|
return MessageContent(body, null, false, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedContentMessagesTypes = arrayOf(
|
val sharedContentMessagesTypes = arrayOf(
|
||||||
TextMessageType(body, null),
|
TextMessageType(body, null),
|
||||||
VideoMessageType(body, "url", null),
|
VideoMessageType(body, MatrixMediaSource("url"), null),
|
||||||
AudioMessageType(body, "url", null),
|
AudioMessageType(body, MatrixMediaSource("url"), null),
|
||||||
ImageMessageType(body, "url", null),
|
ImageMessageType(body, MatrixMediaSource("url"), null),
|
||||||
FileMessageType(body, "url", null),
|
FileMessageType(body, MatrixMediaSource("url"), null),
|
||||||
NoticeMessageType(body, null),
|
NoticeMessageType(body, null),
|
||||||
EmoteMessageType(body, null),
|
EmoteMessageType(body, null),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,5 @@ data class FileInfo(
|
||||||
val mimetype: String?,
|
val mimetype: String?,
|
||||||
val size: Long?,
|
val size: Long?,
|
||||||
val thumbnailInfo: ThumbnailInfo?,
|
val thumbnailInfo: ThumbnailInfo?,
|
||||||
val thumbnailUrl: String?
|
val thumbnailSource: MatrixMediaSource?
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,6 @@ data class ImageInfo(
|
||||||
val mimetype: String?,
|
val mimetype: String?,
|
||||||
val size: Long?,
|
val size: Long?,
|
||||||
val thumbnailInfo: ThumbnailInfo?,
|
val thumbnailInfo: ThumbnailInfo?,
|
||||||
val thumbnailUrl: String?,
|
val thumbnailSource: MatrixMediaSource?,
|
||||||
val blurhash: String?
|
val blurhash: String?
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.api.media
|
package io.element.android.libraries.matrix.api.media
|
||||||
|
|
||||||
import java.nio.file.Path
|
import android.net.Uri
|
||||||
|
|
||||||
interface MatrixMediaLoader {
|
interface MatrixMediaLoader {
|
||||||
/**
|
/**
|
||||||
* @param url to fetch the content for.
|
* @param url to fetch the content for.
|
||||||
* @return a [Result] of ByteArray. It contains the binary data for the media.
|
* @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.
|
* @param url to fetch the data for.
|
||||||
|
|
@ -31,12 +31,12 @@ interface MatrixMediaLoader {
|
||||||
* @param height: the desired height 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.
|
* @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 url to fetch the data for.
|
||||||
* @param mimeType: optional mime type
|
* @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>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -23,6 +23,6 @@ data class VideoInfo(
|
||||||
val mimetype: String?,
|
val mimetype: String?,
|
||||||
val size: Long?,
|
val size: Long?,
|
||||||
val thumbnailInfo: ThumbnailInfo?,
|
val thumbnailInfo: ThumbnailInfo?,
|
||||||
val thumbnailUrl: String?,
|
val thumbnailSource: MatrixMediaSource?,
|
||||||
val blurhash: String?
|
val blurhash: String?
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.AudioInfo
|
||||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
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.ImageInfo
|
||||||
|
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
|
|
||||||
sealed interface EventContent
|
sealed interface EventContent
|
||||||
|
|
@ -106,25 +107,25 @@ data class EmoteMessageType(
|
||||||
|
|
||||||
data class ImageMessageType(
|
data class ImageMessageType(
|
||||||
val body: String,
|
val body: String,
|
||||||
val url: String,
|
val source: MatrixMediaSource,
|
||||||
val info: ImageInfo?
|
val info: ImageInfo?
|
||||||
) : MessageType
|
) : MessageType
|
||||||
|
|
||||||
data class AudioMessageType(
|
data class AudioMessageType(
|
||||||
var body: String,
|
val body: String,
|
||||||
var url: String,
|
val source: MatrixMediaSource,
|
||||||
var info: AudioInfo?
|
val info: AudioInfo?
|
||||||
) : MessageType
|
) : MessageType
|
||||||
|
|
||||||
data class VideoMessageType(
|
data class VideoMessageType(
|
||||||
val body: String,
|
val body: String,
|
||||||
val url: String,
|
val source: MatrixMediaSource,
|
||||||
val info: VideoInfo?
|
val info: VideoInfo?
|
||||||
) : MessageType
|
) : MessageType
|
||||||
|
|
||||||
data class FileMessageType(
|
data class FileMessageType(
|
||||||
val body: String,
|
val body: String,
|
||||||
val url: String,
|
val source: MatrixMediaSource,
|
||||||
val info: FileInfo?
|
val info: FileInfo?
|
||||||
) : MessageType
|
) : MessageType
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,11 @@
|
||||||
package io.element.android.libraries.matrix.impl.media
|
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.FileInfo
|
||||||
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
|
|
||||||
import org.matrix.rustcomponents.sdk.FileInfo as RustFileInfo
|
import org.matrix.rustcomponents.sdk.FileInfo as RustFileInfo
|
||||||
import org.matrix.rustcomponents.sdk.ThumbnailInfo as RustThumbnailInfo
|
|
||||||
|
|
||||||
fun RustFileInfo.map(): FileInfo = FileInfo(
|
fun RustFileInfo.map(): FileInfo = FileInfo(
|
||||||
mimetype = mimetype,
|
mimetype = mimetype,
|
||||||
size = size?.toLong(),
|
size = size?.toLong(),
|
||||||
thumbnailInfo = thumbnailInfo?.map(),
|
thumbnailInfo = thumbnailInfo?.map(),
|
||||||
thumbnailUrl = thumbnailSource?.useUrl()
|
thumbnailSource = thumbnailSource?.map()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,6 @@ fun RustImageInfo.map(): ImageInfo = ImageInfo(
|
||||||
mimetype = mimetype,
|
mimetype = mimetype,
|
||||||
size = size?.toLong(),
|
size = size?.toLong(),
|
||||||
thumbnailInfo = thumbnailInfo?.map(),
|
thumbnailInfo = thumbnailInfo?.map(),
|
||||||
thumbnailUrl = thumbnailSource?.useUrl(),
|
thumbnailSource = thumbnailSource?.map(),
|
||||||
blurhash = blurhash
|
blurhash = blurhash
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.media
|
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.use
|
||||||
|
import org.matrix.rustcomponents.sdk.MediaSource as RustMediaSource
|
||||||
|
|
||||||
fun MediaSource.useUrl(): String = use { it.url() }
|
fun RustMediaSource.map(): MatrixMediaSource = use { MatrixMediaSource(it.url()) }
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,15 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.media
|
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.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||||
|
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.Client
|
import org.matrix.rustcomponents.sdk.Client
|
||||||
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import java.nio.file.Path
|
import java.io.File
|
||||||
import kotlin.io.path.Path
|
|
||||||
|
|
||||||
class RustMediaLoader(
|
class RustMediaLoader(
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
|
|
@ -31,10 +32,10 @@ class RustMediaLoader(
|
||||||
) : MatrixMediaLoader {
|
) : MatrixMediaLoader {
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
override suspend fun loadMediaContent(url: String): Result<ByteArray> =
|
override suspend fun loadMediaContent(source: MatrixMediaSource): Result<ByteArray> =
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
mediaSourceFromUrl(url).use { source ->
|
mediaSourceFromUrl(source.url).use { source ->
|
||||||
innerClient.getMediaContent(source).toUByteArray().toByteArray()
|
innerClient.getMediaContent(source).toUByteArray().toByteArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,13 +43,13 @@ class RustMediaLoader(
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
override suspend fun loadMediaThumbnail(
|
override suspend fun loadMediaThumbnail(
|
||||||
url: String,
|
source: MatrixMediaSource,
|
||||||
width: Long,
|
width: Long,
|
||||||
height: Long
|
height: Long
|
||||||
): Result<ByteArray> =
|
): Result<ByteArray> =
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
mediaSourceFromUrl(url).use { mediaSource ->
|
mediaSourceFromUrl(source.url).use { mediaSource ->
|
||||||
innerClient.getMediaThumbnail(
|
innerClient.getMediaThumbnail(
|
||||||
mediaSource = mediaSource,
|
mediaSource = mediaSource,
|
||||||
width = width.toULong(),
|
width = width.toULong(),
|
||||||
|
|
@ -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) {
|
withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
mediaSourceFromUrl(url).use { mediaSource ->
|
mediaSourceFromUrl(source.url).use { mediaSource ->
|
||||||
innerClient.getMediaFile(
|
innerClient.getMediaFile(
|
||||||
mediaSource = mediaSource,
|
mediaSource = mediaSource,
|
||||||
mimeType = mimeType ?: "application/octet-stream"
|
mimeType = mimeType ?: "application/octet-stream"
|
||||||
).use {
|
).use {
|
||||||
Path(it.path())
|
val file = File(it.path())
|
||||||
|
Uri.fromFile(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,6 @@ fun RustVideoInfo.map(): VideoInfo = VideoInfo(
|
||||||
mimetype = mimetype,
|
mimetype = mimetype,
|
||||||
size = size?.toLong(),
|
size = size?.toLong(),
|
||||||
thumbnailInfo = thumbnailInfo?.map(),
|
thumbnailInfo = thumbnailInfo?.map(),
|
||||||
thumbnailUrl = thumbnailSource?.useUrl(),
|
thumbnailSource = thumbnailSource?.map(),
|
||||||
blurhash = blurhash
|
blurhash = blurhash
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.UnknownMessageType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
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.map
|
||||||
import io.element.android.libraries.matrix.impl.media.useUrl
|
|
||||||
import org.matrix.rustcomponents.sdk.Message
|
import org.matrix.rustcomponents.sdk.Message
|
||||||
import org.matrix.rustcomponents.sdk.MessageType
|
import org.matrix.rustcomponents.sdk.MessageType
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
|
|
@ -42,13 +41,13 @@ class EventMessageMapper {
|
||||||
val type = message.msgtype().use { type ->
|
val type = message.msgtype().use { type ->
|
||||||
when (type) {
|
when (type) {
|
||||||
is MessageType.Audio -> {
|
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 -> {
|
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 -> {
|
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 -> {
|
is MessageType.Notice -> {
|
||||||
NoticeMessageType(type.content.body, type.content.formatted?.map())
|
NoticeMessageType(type.content.body, type.content.formatted?.map())
|
||||||
|
|
@ -60,7 +59,7 @@ class EventMessageMapper {
|
||||||
EmoteMessageType(type.content.body, type.content.formatted?.map())
|
EmoteMessageType(type.content.body, type.content.formatted?.map())
|
||||||
}
|
}
|
||||||
is MessageType.Video -> {
|
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 -> {
|
null -> {
|
||||||
UnknownMessageType
|
UnknownMessageType
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.libraries.matrix.impl.timeline.item.event
|
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.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.FailedToParseMessageLikeContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
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
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
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.StateContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
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.UnableToDecryptContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||||
import io.element.android.libraries.matrix.impl.media.map
|
import io.element.android.libraries.matrix.impl.media.map
|
||||||
|
|
@ -88,7 +88,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
|
||||||
StickerContent(
|
StickerContent(
|
||||||
body = kind.body,
|
body = kind.body,
|
||||||
info = kind.info.map(),
|
info = kind.info.map(),
|
||||||
url = kind.url
|
url = kind.url,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is TimelineItemContentKind.UnableToDecrypt -> {
|
is TimelineItemContentKind.UnableToDecrypt -> {
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,16 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.test.media
|
package io.element.android.libraries.matrix.test.media
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||||
import java.nio.file.Path
|
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||||
import kotlin.io.path.Path
|
import java.io.File
|
||||||
|
|
||||||
class FakeMediaLoader : MatrixMediaLoader {
|
class FakeMediaLoader : MatrixMediaLoader {
|
||||||
|
|
||||||
var shouldFail = false
|
var shouldFail = false
|
||||||
|
|
||||||
override suspend fun loadMediaContent(url: String): Result<ByteArray> {
|
override suspend fun loadMediaContent(source: MatrixMediaSource): Result<ByteArray> {
|
||||||
return if (shouldFail) {
|
return if (shouldFail) {
|
||||||
Result.failure(RuntimeException())
|
Result.failure(RuntimeException())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -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) {
|
return if (shouldFail) {
|
||||||
Result.failure(RuntimeException())
|
Result.failure(RuntimeException())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -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) {
|
return if (shouldFail) {
|
||||||
Result.failure(RuntimeException())
|
Result.failure(RuntimeException())
|
||||||
} else {
|
} else {
|
||||||
return Result.success(Path("path"))
|
return Result.success(Uri.fromFile(File("path")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,12 @@
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
fun AvatarData.toMediaRequestData(): MediaRequestData? {
|
fun AvatarData.toMediaRequestData(): MediaRequestData {
|
||||||
return url?.let {
|
return MediaRequestData(
|
||||||
MediaRequestData(
|
source = url?.let { MatrixMediaSource(it) },
|
||||||
url = it,
|
kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong())
|
||||||
kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong())
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,19 +38,15 @@ internal class CoilMediaFetcher(
|
||||||
ByteBuffer.wrap(data)
|
ByteBuffer.wrap(data)
|
||||||
}.map { byteBuffer ->
|
}.map { byteBuffer ->
|
||||||
imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
|
imageLoader.components.newFetcher(byteBuffer, options, imageLoader)?.first?.fetch()
|
||||||
}
|
}.getOrThrow()
|
||||||
.fold(
|
|
||||||
{ result -> result },
|
|
||||||
{ failure -> throw failure }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadMedia(): Result<ByteArray> {
|
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) {
|
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(
|
is MediaRequestData.Kind.Thumbnail -> mediaLoader.loadMediaThumbnail(
|
||||||
url = mediaData.url,
|
source = mediaData.source,
|
||||||
width = mediaData.kind.width,
|
width = mediaData.kind.width,
|
||||||
height = mediaData.kind.height
|
height = mediaData.kind.height
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.media.MatrixMediaSource
|
||||||
|
|
||||||
data class MediaRequestData(
|
data class MediaRequestData(
|
||||||
val url: String,
|
val source: MatrixMediaSource?,
|
||||||
val kind: Kind
|
val kind: Kind
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
|
||||||
internal class AvatarDataKeyer : Keyer<AvatarData> {
|
internal class AvatarDataKeyer : Keyer<AvatarData> {
|
||||||
override fun key(data: AvatarData, options: Options): String? {
|
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> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MediaRequestData.toKey() = "${url}_${kind}"
|
private fun MediaRequestData.toKey(): String? {
|
||||||
|
if (source == null) return null
|
||||||
|
return "${source.url}_${kind}"
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue