Merge pull request #900 from vector-im/feature/fga/better_media_handling

Feature/fga/better media handling
This commit is contained in:
ganfra 2023-07-18 17:52:41 +02:00 committed by GitHub
commit e09b8aca0e
47 changed files with 402 additions and 77 deletions

View file

@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.media.viewer.MediaViewerNode
import io.element.android.features.messages.impl.report.ReportMessageNode import io.element.android.features.messages.impl.report.ReportMessageNode
import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNode import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNode
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
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.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
@ -224,6 +225,20 @@ class MessagesFlowNode @AssistedInject constructor(
) )
backstack.push(navTarget) backstack.push(navTarget)
} }
is TimelineItemAudioContent -> {
val mediaSource = event.content.audioSource
val navTarget = NavTarget.MediaViewer(
mediaInfo = MediaInfo(
name = event.content.body,
mimeType = event.content.mimeType,
formattedFileSize = event.content.formattedFileSize,
fileExtension = event.content.fileExtension
),
mediaSource = mediaSource,
thumbnailSource = null,
)
backstack.push(navTarget)
}
is TimelineItemLocationContent -> { is TimelineItemLocationContent -> {
val navTarget = NavTarget.LocationViewer( val navTarget = NavTarget.LocationViewer(
location = event.content.location, location = event.content.location,

View file

@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.TimelinePresenter
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@ -108,10 +109,10 @@ class MessagesPresenter @AssistedInject constructor(
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val userHasPermissionToSendMessage by room.canSendEventAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) val userHasPermissionToSendMessage by room.canSendEventAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
val roomName by produceState(initialValue = room.displayName, key1 = syncUpdateFlow.value){ val roomName by produceState(initialValue = room.displayName, key1 = syncUpdateFlow.value) {
value = room.displayName value = room.displayName
} }
val roomAvatar by produceState(initialValue = room.avatarData(), key1 = syncUpdateFlow.value){ val roomAvatar by produceState(initialValue = room.avatarData(), key1 = syncUpdateFlow.value) {
value = room.avatarData() value = room.avatarData()
} }
var hasDismissedInviteDialog by rememberSaveable { var hasDismissedInviteDialog by rememberSaveable {
@ -250,28 +251,28 @@ class MessagesPresenter @AssistedInject constructor(
val textContent = messageSummaryFormatter.format(targetEvent) val textContent = messageSummaryFormatter.format(targetEvent)
val attachmentThumbnailInfo = when (targetEvent.content) { val attachmentThumbnailInfo = when (targetEvent.content) {
is TimelineItemImageContent -> AttachmentThumbnailInfo( is TimelineItemImageContent -> AttachmentThumbnailInfo(
mediaSource = targetEvent.content.mediaSource, thumbnailSource = targetEvent.content.thumbnailSource,
textContent = targetEvent.content.body, textContent = targetEvent.content.body,
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = targetEvent.content.blurhash, blurHash = targetEvent.content.blurhash,
) )
is TimelineItemVideoContent -> AttachmentThumbnailInfo( is TimelineItemVideoContent -> AttachmentThumbnailInfo(
mediaSource = targetEvent.content.thumbnailSource, thumbnailSource = targetEvent.content.thumbnailSource,
textContent = targetEvent.content.body, textContent = targetEvent.content.body,
type = AttachmentThumbnailType.Video, type = AttachmentThumbnailType.Video,
blurHash = targetEvent.content.blurHash, blurHash = targetEvent.content.blurHash,
) )
is TimelineItemFileContent -> AttachmentThumbnailInfo( is TimelineItemFileContent -> AttachmentThumbnailInfo(
mediaSource = targetEvent.content.thumbnailSource, thumbnailSource = targetEvent.content.thumbnailSource,
textContent = targetEvent.content.body, textContent = targetEvent.content.body,
type = AttachmentThumbnailType.File, type = AttachmentThumbnailType.File,
blurHash = null, )
is TimelineItemAudioContent -> AttachmentThumbnailInfo(
textContent = targetEvent.content.body,
type = AttachmentThumbnailType.Audio,
) )
is TimelineItemLocationContent -> AttachmentThumbnailInfo( is TimelineItemLocationContent -> AttachmentThumbnailInfo(
mediaSource = null,
textContent = null,
type = AttachmentThumbnailType.Location, type = AttachmentThumbnailType.Location,
blurHash = null,
) )
is TimelineItemTextBasedContent, is TimelineItemTextBasedContent,
is TimelineItemRedactedContent, is TimelineItemRedactedContent,

View file

@ -56,6 +56,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@ -246,8 +247,6 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
info = AttachmentThumbnailInfo( info = AttachmentThumbnailInfo(
type = AttachmentThumbnailType.Location, type = AttachmentThumbnailType.Location,
textContent = stringResource(CommonStrings.common_shared_location), textContent = stringResource(CommonStrings.common_shared_location),
mediaSource = null,
blurHash = null,
) )
) )
} }
@ -258,9 +257,9 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
AttachmentThumbnail( AttachmentThumbnail(
modifier = imageModifier, modifier = imageModifier,
info = AttachmentThumbnailInfo( info = AttachmentThumbnailInfo(
mediaSource = event.content.mediaSource, thumbnailSource = event.content.mediaSource,
textContent = textContent, textContent = textContent,
type = AttachmentThumbnailType.File, type = AttachmentThumbnailType.Image,
blurHash = event.content.blurhash, blurHash = event.content.blurhash,
) )
) )
@ -272,7 +271,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
AttachmentThumbnail( AttachmentThumbnail(
modifier = imageModifier, modifier = imageModifier,
info = AttachmentThumbnailInfo( info = AttachmentThumbnailInfo(
mediaSource = event.content.thumbnailSource, thumbnailSource = event.content.thumbnailSource,
textContent = textContent, textContent = textContent,
type = AttachmentThumbnailType.Video, type = AttachmentThumbnailType.Video,
blurHash = event.content.blurHash, blurHash = event.content.blurHash,
@ -286,10 +285,21 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
AttachmentThumbnail( AttachmentThumbnail(
modifier = imageModifier, modifier = imageModifier,
info = AttachmentThumbnailInfo( info = AttachmentThumbnailInfo(
mediaSource = null, thumbnailSource = event.content.thumbnailSource,
textContent = textContent, textContent = textContent,
type = AttachmentThumbnailType.File, type = AttachmentThumbnailType.File,
blurHash = null )
)
}
content = { ContentForBody(event.content.body) }
}
is TimelineItemAudioContent -> {
icon = {
AttachmentThumbnail(
modifier = imageModifier,
info = AttachmentThumbnailInfo(
textContent = textContent,
type = AttachmentThumbnailType.Audio,
) )
) )
} }

View file

@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Attachment import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -47,7 +48,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
@ -59,7 +59,9 @@ import io.element.android.features.messages.impl.media.helper.formatFileExtensio
import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper import io.element.android.features.messages.impl.media.local.exoplayer.ExoPlayerWrapper
import io.element.android.features.messages.impl.media.local.pdf.PdfViewer import io.element.android.features.messages.impl.media.local.pdf.PdfViewer
import io.element.android.features.messages.impl.media.local.pdf.rememberPdfViewerState import io.element.android.features.messages.impl.media.local.pdf.rememberPdfViewerState
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
import io.element.android.libraries.designsystem.R import io.element.android.libraries.designsystem.R
@ -103,6 +105,7 @@ fun LocalMediaView(
zoomableState = zoomableState, zoomableState = zoomableState,
modifier = modifier modifier = modifier
) )
//TODO handle audio with exoplayer
else -> MediaFileView( else -> MediaFileView(
localMediaViewState = localMediaViewState, localMediaViewState = localMediaViewState,
uri = localMedia?.uri, uri = localMedia?.uri,
@ -215,6 +218,7 @@ fun MediaFileView(
info: MediaInfo?, info: MediaInfo?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val isAudio = info?.mimeType.isMimeTypeAudio().orFalse()
localMediaViewState.isReady = uri != null localMediaViewState.isReady = uri != null
Box(modifier = modifier.padding(horizontal = 8.dp), contentAlignment = Alignment.Center) { Box(modifier = modifier.padding(horizontal = 8.dp), contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
@ -226,12 +230,12 @@ fun MediaFileView(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.Attachment, imageVector = if (isAudio) Icons.Outlined.GraphicEq else Icons.Outlined.Attachment,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.background, tint = MaterialTheme.colorScheme.background,
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
.rotate(-45f), .rotate(if (isAudio) 0f else -45f),
) )
} }
if (info != null) { if (info != null) {

View file

@ -29,7 +29,7 @@ data class MediaInfo(
) : Parcelable ) : Parcelable
fun anImageInfo(): MediaInfo = MediaInfo( fun anImageInfo(): MediaInfo = MediaInfo(
"an image file.jpg", MimeTypes.Jpeg, "4MB","jpg" "an image file.jpg", MimeTypes.Jpeg, "4MB", "jpg"
) )
fun aVideoInfo(): MediaInfo = MediaInfo( fun aVideoInfo(): MediaInfo = MediaInfo(
@ -43,3 +43,7 @@ fun aPdfInfo(): MediaInfo = MediaInfo(
fun aFileInfo(): MediaInfo = MediaInfo( fun aFileInfo(): MediaInfo = MediaInfo(
"an apk file.apk", MimeTypes.Apk, "50MB", "apk" "an apk file.apk", MimeTypes.Apk, "50MB", "apk"
) )
fun anAudioInfo(): MediaInfo = MediaInfo(
"an audio file.mp3", MimeTypes.Mp3, "7MB", "mp3"
)

View file

@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.features.messages.impl.media.local.aFileInfo import io.element.android.features.messages.impl.media.local.aFileInfo
import io.element.android.features.messages.impl.media.local.aPdfInfo import io.element.android.features.messages.impl.media.local.aPdfInfo
import io.element.android.features.messages.impl.media.local.aVideoInfo import io.element.android.features.messages.impl.media.local.aVideoInfo
import io.element.android.features.messages.impl.media.local.anAudioInfo
import io.element.android.features.messages.impl.media.local.anImageInfo import io.element.android.features.messages.impl.media.local.anImageInfo
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
@ -59,7 +60,17 @@ open class MediaViewerStateProvider : PreviewParameterProvider<MediaViewerState>
LocalMedia(Uri.EMPTY, aFileInfo()) LocalMedia(Uri.EMPTY, aFileInfo())
), ),
aFileInfo(), aFileInfo(),
) ),
aMediaViewerState(
Async.Loading(),
anAudioInfo(),
),
aMediaViewerState(
Async.Success(
LocalMedia(Uri.EMPTY, anAudioInfo())
),
anAudioInfo(),
),
) )
} }

View file

@ -56,7 +56,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.constraintlayout.compose.ConstrainScope import androidx.constraintlayout.compose.ConstrainScope
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
@ -85,6 +84,7 @@ import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
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.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
@ -521,28 +521,29 @@ private fun ReplyToContent(
private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready) = private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready) =
when (val type = inReplyTo.content.type) { when (val type = inReplyTo.content.type) {
is ImageMessageType -> AttachmentThumbnailInfo( is ImageMessageType -> AttachmentThumbnailInfo(
mediaSource = type.info?.thumbnailSource, thumbnailSource = type.info?.thumbnailSource,
textContent = inReplyTo.content.body, textContent = inReplyTo.content.body,
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = type.info?.blurhash, blurHash = type.info?.blurhash,
) )
is VideoMessageType -> AttachmentThumbnailInfo( is VideoMessageType -> AttachmentThumbnailInfo(
mediaSource = type.info?.thumbnailSource, thumbnailSource = type.info?.thumbnailSource,
textContent = inReplyTo.content.body, textContent = inReplyTo.content.body,
type = AttachmentThumbnailType.Video, type = AttachmentThumbnailType.Video,
blurHash = type.info?.blurhash, blurHash = type.info?.blurhash,
) )
is FileMessageType -> AttachmentThumbnailInfo( is FileMessageType -> AttachmentThumbnailInfo(
mediaSource = type.info?.thumbnailSource, thumbnailSource = type.info?.thumbnailSource,
textContent = inReplyTo.content.body, textContent = inReplyTo.content.body,
type = AttachmentThumbnailType.File, type = AttachmentThumbnailType.File,
blurHash = null,
) )
is LocationMessageType -> AttachmentThumbnailInfo( is LocationMessageType -> AttachmentThumbnailInfo(
mediaSource = null,
textContent = inReplyTo.content.body, textContent = inReplyTo.content.body,
type = AttachmentThumbnailType.Location, type = AttachmentThumbnailType.Location,
blurHash = null, )
is AudioMessageType -> AttachmentThumbnailInfo(
textContent = inReplyTo.content.body,
type = AttachmentThumbnailType.Audio,
) )
else -> null else -> null
} }

View file

@ -0,0 +1,97 @@
/*
* 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.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@Composable
fun TimelineItemAudioView(
content: TimelineItemAudioContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
) {
Box(
modifier = Modifier
.size(32.dp)
.clip(CircleShape)
.background(ElementTheme.materialColors.background),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Outlined.GraphicEq,
contentDescription = null,
tint = ElementTheme.materialColors.primary,
modifier = Modifier
.size(16.dp),
)
}
Spacer(Modifier.width(8.dp))
Column {
Text(
text = content.body,
color = ElementTheme.materialColors.primary,
maxLines = 2,
style = ElementTheme.typography.fontBodyLgRegular,
overflow = TextOverflow.Ellipsis
)
Text(
text = content.fileExtensionAndSize + extraPadding.getStr(12.sp),
color = ElementTheme.materialColors.secondary,
style = ElementTheme.typography.fontBodySmRegular,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
@DayNightPreviews
@Composable
internal fun TimelineItemAudioViewPreview(@PreviewParameter(TimelineItemAudioContentProvider::class) content: TimelineItemAudioContent) =
ElementPreview {
TimelineItemAudioView(
content,
extraPadding = noExtraPadding,
)
}

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
@ -80,6 +81,11 @@ fun TimelineItemEventContentView(
extraPadding = extraPadding, extraPadding = extraPadding,
modifier = modifier modifier = modifier
) )
is TimelineItemAudioContent -> TimelineItemAudioView(
content = content,
extraPadding = extraPadding,
modifier = modifier
)
is TimelineItemStateContent -> TimelineItemStateView( is TimelineItemStateContent -> TimelineItemStateView(
content = content, content = content,
modifier = modifier modifier = modifier

View file

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.timeline.factories.event package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.location.api.Location import io.element.android.features.location.api.Location
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
@ -30,6 +31,7 @@ import io.element.android.features.messages.impl.timeline.util.FileExtensionExtr
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.androidutils.filesize.FileSizeFormatter import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
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.FileMessageType import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
@ -99,6 +101,14 @@ class TimelineItemContentMessageFactory @Inject constructor(
fileExtension = fileExtensionExtractor.extractFromName(messageType.body) fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
) )
} }
is AudioMessageType -> TimelineItemAudioContent(
body = messageType.body,
audioSource = messageType.source,
duration = messageType.info?.duration?.toMillis() ?: 0L,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
)
is FileMessageType -> TimelineItemFileContent( is FileMessageType -> TimelineItemFileContent(
body = messageType.body, body = messageType.body,
thumbnailSource = messageType.info?.thumbnailSource, thumbnailSource = messageType.info?.thumbnailSource,

View file

@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.timeline.groups package io.element.android.features.messages.impl.timeline.groups
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@ -52,6 +53,7 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean {
is TimelineItemImageContent, is TimelineItemImageContent,
is TimelineItemFileContent, is TimelineItemFileContent,
is TimelineItemVideoContent, is TimelineItemVideoContent,
is TimelineItemAudioContent,
is TimelineItemLocationContent, is TimelineItemLocationContent,
TimelineItemRedactedContent, TimelineItemRedactedContent,
TimelineItemUnknownContent -> false TimelineItemUnknownContent -> false

View file

@ -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.features.messages.impl.media.helper.formatFileExtensionAndSize
import io.element.android.libraries.matrix.api.media.MediaSource
data class TimelineItemAudioContent(
val body: String,
val duration: Long,
val audioSource: MediaSource,
val mimeType: String,
val formattedFileSize: String,
val fileExtension: String,
) : TimelineItemEventContent {
val fileExtensionAndSize = formatFileExtensionAndSize(fileExtension, formattedFileSize)
override val type: String = "TimelineItemAudioContent"
}

View file

@ -0,0 +1,39 @@
/*
* 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.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource
open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineItemAudioContent> {
override val values: Sequence<TimelineItemAudioContent>
get() = sequenceOf(
aTimelineItemAudioContent("A sound.mp3"),
aTimelineItemAudioContent("A bigger name sound.mp3"),
aTimelineItemAudioContent("An even bigger bigger bigger bigger bigger bigger bigger sound name which doesn't fit .mp3"),
)
}
fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAudioContent(
body = fileName,
mimeType = MimeTypes.Pdf,
formattedFileSize = "100kB",
fileExtension = "mp3",
duration = 100,
audioSource = MediaSource(""),
)

View file

@ -26,7 +26,7 @@ class TimelineItemEventContentProvider : PreviewParameterProvider<TimelineItemEv
aTimelineItemEncryptedContent(), aTimelineItemEncryptedContent(),
aTimelineItemImageContent(), aTimelineItemImageContent(),
aTimelineItemVideoContent(), aTimelineItemVideoContent(),
aTimelineItemFileContent("A file.pdf"), aTimelineItemFileContent(),
aTimelineItemFileContent("A bigger file name which doesn't fit.pdf"), aTimelineItemFileContent("A bigger file name which doesn't fit.pdf"),
aTimelineItemLocationContent(), aTimelineItemLocationContent(),
aTimelineItemLocationContent("Location description"), aTimelineItemLocationContent("Location description"),

View file

@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource
open class TimelineItemFileContentProvider : PreviewParameterProvider<TimelineItemFileContent> { open class TimelineItemFileContentProvider : PreviewParameterProvider<TimelineItemFileContent> {
override val values: Sequence<TimelineItemFileContent> override val values: Sequence<TimelineItemFileContent>
get() = sequenceOf( get() = sequenceOf(
aTimelineItemFileContent("A file.pdf"), aTimelineItemFileContent(),
aTimelineItemFileContent("A bigger name file.pdf"), aTimelineItemFileContent("A bigger name file.pdf"),
aTimelineItemFileContent("An even bigger bigger bigger bigger bigger bigger bigger file name which doesn't fit .pdf"), aTimelineItemFileContent("An even bigger bigger bigger bigger bigger bigger bigger file name which doesn't fit .pdf"),
) )
@ -31,7 +31,7 @@ open class TimelineItemFileContentProvider : PreviewParameterProvider<TimelineIt
fun aTimelineItemFileContent(fileName: String = "A file.pdf") = TimelineItemFileContent( fun aTimelineItemFileContent(fileName: String = "A file.pdf") = TimelineItemFileContent(
body = fileName, body = fileName,
thumbnailSource = MediaSource(url = ""), thumbnailSource = null,
fileSource = MediaSource(url = ""), fileSource = MediaSource(url = ""),
mimeType = MimeTypes.Pdf, mimeType = MimeTypes.Pdf,
formattedFileSize = "100kB", formattedFileSize = "100kB",

View file

@ -31,7 +31,7 @@ open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineI
fun aTimelineItemVideoContent() = TimelineItemVideoContent( fun aTimelineItemVideoContent() = TimelineItemVideoContent(
body = "Video.mp4", body = "Video.mp4",
thumbnailSource = MediaSource(url = ""), thumbnailSource = null,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
aspectRatio = 0.5f, aspectRatio = 0.5f,
duration = 100, duration = 100,

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.utils.messagesummary
import android.content.Context import android.content.Context
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@ -50,6 +51,7 @@ class MessageSummaryFormatterImpl @Inject constructor(
is TimelineItemImageContent -> context.getString(CommonStrings.common_image) is TimelineItemImageContent -> context.getString(CommonStrings.common_image)
is TimelineItemVideoContent -> context.getString(CommonStrings.common_video) is TimelineItemVideoContent -> context.getString(CommonStrings.common_video)
is TimelineItemFileContent -> context.getString(CommonStrings.common_file) is TimelineItemFileContent -> context.getString(CommonStrings.common_file)
is TimelineItemAudioContent -> context.getString(CommonStrings.common_audio)
} }
} }
} }

View file

@ -325,7 +325,7 @@ class MessageComposerPresenterTest {
Result.success( Result.success(
MediaUploadInfo.Image( MediaUploadInfo.Image(
file = File("/some/path"), file = File("/some/path"),
info = ImageInfo( imageInfo = ImageInfo(
width = null, width = null,
height = null, height = null,
mimetype = null, mimetype = null,
@ -358,7 +358,7 @@ class MessageComposerPresenterTest {
Result.success( Result.success(
MediaUploadInfo.Video( MediaUploadInfo.Video(
file = File("/some/path"), file = File("/some/path"),
info = VideoInfo( videoInfo = VideoInfo(
width = null, width = null,
height = null, height = null,
mimetype = null, mimetype = null,

View file

@ -605,7 +605,7 @@ class RoomDetailsEditPresenterTest {
Result.success( Result.success(
MediaUploadInfo.AnyFile( MediaUploadInfo.AnyFile(
file = processedFile, file = processedFile,
info = mockk(), fileInfo = mockk(),
) )
) )
) )

View file

@ -38,6 +38,7 @@ object MimeTypes {
const val Audio = "audio/*" const val Audio = "audio/*"
const val Ogg = "audio/ogg" const val Ogg = "audio/ogg"
const val Mp3 = "audio/mp3"
const val PlainText = "text/plain" const val PlainText = "text/plain"

View file

@ -20,14 +20,17 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
@ -37,21 +40,32 @@ import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.ui.strings.CommonStrings
import timber.log.Timber
@Composable @Composable
fun ProgressDialog( fun ProgressDialog(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
text: String? = null, text: String? = null,
type: ProgressDialogType = ProgressDialogType.Indeterminate, type: ProgressDialogType = ProgressDialogType.Indeterminate,
onDismiss: () -> Unit = {}, isCancellable: Boolean = false,
onDismissRequest: () -> Unit = {},
) { ) {
DisposableEffect(Unit) {
onDispose {
Timber.v("OnDispose progressDialog")
}
}
Dialog( Dialog(
onDismissRequest = onDismiss, onDismissRequest = onDismissRequest,
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false) properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
) { ) {
ProgressDialogContent( ProgressDialogContent(
modifier = modifier, modifier = modifier,
text = text, text = text,
isCancellable = isCancellable,
onCancelClicked = onDismissRequest,
progressIndicator = { progressIndicator = {
when (type) { when (type) {
is ProgressDialogType.Indeterminate -> { is ProgressDialogType.Indeterminate -> {
@ -81,6 +95,8 @@ sealed interface ProgressDialogType {
private fun ProgressDialogContent( private fun ProgressDialogContent(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
text: String? = null, text: String? = null,
isCancellable: Boolean = false,
onCancelClicked: () -> Unit = {},
progressIndicator: @Composable () -> Unit = { progressIndicator: @Composable () -> Unit = {
CircularProgressIndicator( CircularProgressIndicator(
color = MaterialTheme.colorScheme.primary color = MaterialTheme.colorScheme.primary
@ -107,6 +123,17 @@ private fun ProgressDialogContent(
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
) )
} }
if (isCancellable) {
Spacer(modifier = Modifier.height(24.dp))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.BottomEnd
) {
TextButton(onClick = onCancelClicked) {
Text(stringResource(id = CommonStrings.action_cancel))
}
}
}
} }
} }
} }
@ -118,6 +145,6 @@ internal fun ProgressDialogPreview() = ElementThemedPreview { ContentToPreview()
@Composable @Composable
private fun ContentToPreview() { private fun ContentToPreview() {
DialogPreview { DialogPreview {
ProgressDialogContent(text = "test dialog content") ProgressDialogContent(text = "test dialog content", isCancellable = true)
} }
} }

View file

@ -21,5 +21,5 @@ import java.time.Duration
data class AudioInfo( data class AudioInfo(
val duration: Duration?, val duration: Duration?,
val size: Long?, val size: Long?,
val mimeType: String?, val mimetype: String?,
) )

View file

@ -22,11 +22,11 @@ import org.matrix.rustcomponents.sdk.AudioInfo as RustAudioInfo
fun RustAudioInfo.map(): AudioInfo = AudioInfo( fun RustAudioInfo.map(): AudioInfo = AudioInfo(
duration = duration, duration = duration,
size = size?.toLong(), size = size?.toLong(),
mimeType = mimetype mimetype = mimetype
) )
fun AudioInfo.map(): RustAudioInfo = RustAudioInfo( fun AudioInfo.map(): RustAudioInfo = RustAudioInfo(
duration = duration, duration = duration,
size = size?.toULong(), size = size?.toULong(),
mimetype = mimeType, mimetype = mimetype,
) )

View file

@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Attachment import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material.icons.outlined.VideoCameraBack import androidx.compose.material.icons.outlined.VideoCameraBack
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -44,9 +45,9 @@ fun AttachmentThumbnail(
thumbnailSize: Long = 32L, thumbnailSize: Long = 32L,
backgroundColor: Color = MaterialTheme.colorScheme.surface, backgroundColor: Color = MaterialTheme.colorScheme.surface,
) { ) {
if (info.mediaSource != null) { if (info.thumbnailSource != null) {
val mediaRequestData = MediaRequestData( val mediaRequestData = MediaRequestData(
source = info.mediaSource, source = info.thumbnailSource,
kind = MediaRequestData.Kind.Thumbnail(thumbnailSize), kind = MediaRequestData.Kind.Thumbnail(thumbnailSize),
) )
BlurHashAsyncImage( BlurHashAsyncImage(
@ -68,6 +69,12 @@ fun AttachmentThumbnail(
contentDescription = info.textContent, contentDescription = info.textContent,
) )
} }
AttachmentThumbnailType.Audio -> {
Icon(
imageVector = Icons.Outlined.GraphicEq,
contentDescription = info.textContent,
)
}
AttachmentThumbnailType.File -> { AttachmentThumbnailType.File -> {
Icon( Icon(
imageVector = Icons.Outlined.Attachment, imageVector = Icons.Outlined.Attachment,
@ -88,13 +95,13 @@ fun AttachmentThumbnail(
@Parcelize @Parcelize
enum class AttachmentThumbnailType: Parcelable { enum class AttachmentThumbnailType: Parcelable {
Image, Video, File, Location Image, Video, File, Audio, Location
} }
@Parcelize @Parcelize
data class AttachmentThumbnailInfo( data class AttachmentThumbnailInfo(
val mediaSource: MediaSource?, val type: AttachmentThumbnailType,
val textContent: String?, val thumbnailSource: MediaSource? = null,
val type: AttachmentThumbnailType?, val textContent: String? = null,
val blurHash: String?, val blurHash: String? = null,
): Parcelable ): Parcelable

View file

@ -46,36 +46,43 @@ class MediaSender @Inject constructor(
} }
private suspend fun MatrixRoom.sendMedia( private suspend fun MatrixRoom.sendMedia(
info: MediaUploadInfo, uploadInfo: MediaUploadInfo,
progressCallback: ProgressCallback? progressCallback: ProgressCallback?
): Result<Unit> { ): Result<Unit> {
return when (info) { return when (uploadInfo) {
is MediaUploadInfo.Image -> { is MediaUploadInfo.Image -> {
sendImage( sendImage(
file = info.file, file = uploadInfo.file,
thumbnailFile = info.thumbnailFile, thumbnailFile = uploadInfo.thumbnailFile,
imageInfo = info.info, imageInfo = uploadInfo.imageInfo,
progressCallback = progressCallback progressCallback = progressCallback
) )
} }
is MediaUploadInfo.Video -> { is MediaUploadInfo.Video -> {
sendVideo( sendVideo(
file = info.file, file = uploadInfo.file,
thumbnailFile = info.thumbnailFile, thumbnailFile = uploadInfo.thumbnailFile,
videoInfo = info.info, videoInfo = uploadInfo.videoInfo,
progressCallback = progressCallback
)
}
is MediaUploadInfo.Audio -> {
sendAudio(
file = uploadInfo.file,
audioInfo = uploadInfo.audioInfo,
progressCallback = progressCallback progressCallback = progressCallback
) )
} }
is MediaUploadInfo.AnyFile -> { is MediaUploadInfo.AnyFile -> {
sendFile( sendFile(
file = info.file, file = uploadInfo.file,
fileInfo = info.info, fileInfo = uploadInfo.fileInfo,
progressCallback = progressCallback progressCallback = progressCallback
) )
} }
else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $info")) else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $uploadInfo"))
} }
} }
} }

View file

@ -26,8 +26,8 @@ sealed interface MediaUploadInfo {
val file: File val file: File
data class Image(override val file: File, val info: ImageInfo, val thumbnailFile: File) : MediaUploadInfo data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File) : MediaUploadInfo
data class Video(override val file: File, val info: VideoInfo, val thumbnailFile: File) : MediaUploadInfo data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File) : MediaUploadInfo
data class Audio(override val file: File, val info: AudioInfo) : MediaUploadInfo data class Audio(override val file: File, val audioInfo: AudioInfo) : MediaUploadInfo
data class AnyFile(override val file: File, val info: FileInfo) : MediaUploadInfo data class AnyFile(override val file: File, val fileInfo: FileInfo) : MediaUploadInfo
} }

View file

@ -133,7 +133,7 @@ class AndroidMediaPreProcessor @Inject constructor(
removeSensitiveImageMetadata(compressionResult.file) removeSensitiveImageMetadata(compressionResult.file)
return MediaUploadInfo.Image( return MediaUploadInfo.Image(
file = compressionResult.file, file = compressionResult.file,
info = imageInfo, imageInfo = imageInfo,
thumbnailFile = thumbnailResult.file thumbnailFile = thumbnailResult.file
) )
} }
@ -156,7 +156,7 @@ class AndroidMediaPreProcessor @Inject constructor(
removeSensitiveImageMetadata(file) removeSensitiveImageMetadata(file)
return MediaUploadInfo.Image( return MediaUploadInfo.Image(
file = file, file = file,
info = imageInfo, imageInfo = imageInfo,
thumbnailFile = thumbnailResult.file thumbnailFile = thumbnailResult.file
) )
} }
@ -184,7 +184,7 @@ class AndroidMediaPreProcessor @Inject constructor(
val videoInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo) val videoInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo)
return MediaUploadInfo.Video( return MediaUploadInfo.Video(
file = resultFile, file = resultFile,
info = videoInfo, videoInfo = videoInfo,
thumbnailFile = thumbnailInfo.file thumbnailFile = thumbnailInfo.file
) )
} }
@ -196,7 +196,7 @@ class AndroidMediaPreProcessor @Inject constructor(
val info = AudioInfo( val info = AudioInfo(
duration = extractDuration(), duration = extractDuration(),
size = file.length(), size = file.length(),
mimeType = mimeType, mimetype = mimeType,
) )
MediaUploadInfo.Audio(file, info) MediaUploadInfo.Audio(file, info)

View file

@ -483,7 +483,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice", senderName = "Alice",
eventId = EventId("$1234"), eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo( attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = MediaSource("https://domain.com/image.jpg"), thumbnailSource = MediaSource("https://domain.com/image.jpg"),
textContent = "image.jpg", textContent = "image.jpg",
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
@ -501,7 +501,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice", senderName = "Alice",
eventId = EventId("$1234"), eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo( attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = MediaSource("https://domain.com/video.mp4"), thumbnailSource = MediaSource("https://domain.com/video.mp4"),
textContent = "video.mp4", textContent = "video.mp4",
type = AttachmentThumbnailType.Video, type = AttachmentThumbnailType.Video,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
@ -519,7 +519,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice", senderName = "Alice",
eventId = EventId("$1234"), eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo( attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = null, thumbnailSource = null,
textContent = "logs.txt", textContent = "logs.txt",
type = AttachmentThumbnailType.File, type = AttachmentThumbnailType.File,
blurHash = null, blurHash = null,
@ -537,7 +537,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice", senderName = "Alice",
eventId = EventId("$1234"), eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo( attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = null, thumbnailSource = null,
textContent = null, textContent = null,
type = AttachmentThumbnailType.Location, type = AttachmentThumbnailType.Location,
blurHash = null, blurHash = null,

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a667744cd675695d67f5b402101f2ce72318fcc9311933300a83cf86bf3b2c19 oid sha256:e456ca95ee33cf14cac839b2b57879d17ca47b156da65c8a870882a90ef2c84c
size 47473 size 40664

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:64a8da63cd27373a61e788cdf569e05f1608ab32bebffd2651755d5a3345c37c oid sha256:66a7d55b40a9d5d7bc02f531bd3c80a1ba96e24a619c123f68549df355019558
size 45752 size 38989

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a1805563eccf45f507152722511cacfa3f0411d1ccb90dd8d539f9a4a697b56f
size 14459

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2824676afbff473eff8c8cfbcf8b3e58b2851e37324c6a8d9ba47d626b0f8bc
size 14234

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6b83378cdfb20b5ffc781fc514c5a4d3aaa0714101b12a572f032f92ec13180c
size 9776

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:40d1ea7620f7f830f52b87fbe32d39d4c01ddeeeb10535da17f7207acc80a1d6
size 11821

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11e2a8b0a79c56b379d387a3987362d5309e7f30903b940994ed43217de1380d
size 21441

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e00510a5f35eb33aaef15c30e6caac62045d8e4c37c032a24922bbb749ad0375
size 9817

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:229d83ed02804137ab1dad4114eecc6d52d6a0e3ccbad436cf226f6cfc628cf7
size 12200

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:27fff9f0ea88cf06934298ea6155cbf4dd49c370cc5d0a14b4d387ac8a7e7c39
size 23203

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e00510a5f35eb33aaef15c30e6caac62045d8e4c37c032a24922bbb749ad0375
size 9817

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:229d83ed02804137ab1dad4114eecc6d52d6a0e3ccbad436cf226f6cfc628cf7
size 12200

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:27fff9f0ea88cf06934298ea6155cbf4dd49c370cc5d0a14b4d387ac8a7e7c39
size 23203

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ba5f5fefedaaf994fb2eae3df38724b245e27a1b03d48cb20d33f7ca49caa356
size 9458

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19ce1d8ddd69760417e267cda7d4663b44d7a50f8ef1a5617497e1135f8aa586
size 11500

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e589a013be7a4b54e3f91816565dd38a05086c19d9832480713d895b1462e72
size 20864

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:10b9570b3f00b7ad9fa473bd5de8568fc4e3305fc3ad5d6d3bec9cb40a1fbe46 oid sha256:22747c979bf704a554dfa4ee3ace1551e78f1180bc594c55f1497ec1d6529aa2
size 20350 size 21211