diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 08b485b1e79..fc3fba9c59a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -50,7 +50,6 @@ import io.element.android.features.poll.api.create.CreatePollMode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.show import io.element.android.libraries.di.RoomScope @@ -324,7 +323,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemImageContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.filename ?: event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -341,7 +341,8 @@ class MessagesFlowNode @AssistedInject constructor( if (event.content.preferredMediaSource != null) { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -358,7 +359,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemVideoContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.filename ?: event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -372,7 +374,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemFileContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension @@ -386,7 +389,8 @@ class MessagesFlowNode @AssistedInject constructor( is TimelineItemAudioContent -> { val navTarget = NavTarget.MediaViewer( mediaInfo = MediaInfo( - name = event.content.body, + filename = event.content.filename, + caption = event.content.caption, mimeType = event.content.mimeType, formattedFileSize = event.content.formattedFileSize, fileExtension = event.content.fileExtension diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index cccfe894098..0b9377ae90a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -269,19 +269,19 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif content = { ContentForBody(stringResource(CommonStrings.common_shared_location)) } } is TimelineItemImageContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemStickerContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemVideoContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemFileContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemAudioContent -> { - content = { ContentForBody(event.content.body) } + content = { ContentForBody(event.content.bestDescription) } } is TimelineItemVoiceContent -> { content = { ContentForBody(textContent) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 67342203bdc..bc42824d683 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -629,7 +629,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = isMine, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt index 5bda7bb8d42..cdec04eb056 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowForDirectRoomPreview.kt @@ -38,7 +38,7 @@ internal fun TimelineItemEventRowForDirectRoomPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = it, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 5f ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt index a3f943bed3e..328741c8078 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowShieldPreview.kt @@ -45,7 +45,7 @@ internal fun TimelineItemEventRowShieldPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = true, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, @@ -54,7 +54,7 @@ internal fun TimelineItemEventRowShieldPreview() = ElementPreview { ) ATimelineItemEventRow( event = aTimelineItemEvent( - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt index ee13c8ec039..c429e9cc835 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -49,7 +49,7 @@ internal fun TimelineItemEventRowWithReplyContentToPreview( event = aTimelineItemEvent( isMine = it, timelineItemReactions = aTimelineItemReactions(count = 0), - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( aspectRatio = 2.5f ), inReplyTo = inReplyToDetails, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt index 3ea3f33135f..23069a1facb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt @@ -63,7 +63,7 @@ fun TimelineItemAudioView( Spacer(Modifier.width(spacing)) Column { Text( - text = content.body, + text = content.bestDescription, color = ElementTheme.materialColors.primary, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt index 354bfaf2c7d..dadfadc299d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt @@ -64,7 +64,7 @@ fun TimelineItemFileView( Spacer(Modifier.width(spacing)) Column { Text( - text = content.body, + text = content.bestDescription, color = ElementTheme.materialColors.primary, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index 85b2b7f6788..fcc24b791e2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -91,7 +91,7 @@ fun TimelineItemImageView( model = MediaRequestData( source = content.preferredMediaSource, kind = MediaRequestData.Kind.File( - body = content.filename ?: content.body, + fileName = content.filename, mimeType = content.mimeType, ), ), @@ -108,7 +108,9 @@ fun TimelineItemImageView( val caption = if (LocalInspectionMode.current) { SpannedString(content.caption) } else { - content.formatted?.body?.takeIf { content.formatted.format == MessageFormat.HTML } ?: SpannedString(content.caption) + content.formattedCaption?.body + ?.takeIf { content.formattedCaption.format == MessageFormat.HTML } + ?: SpannedString(content.caption) } CompositionLocalProvider( LocalContentColor provides ElementTheme.colors.textPrimary, @@ -158,9 +160,9 @@ internal fun TimelineImageWithCaptionRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = isMine, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( filename = "image.jpg", - body = "A long caption that may wrap into several lines", + caption = "A long caption that may wrap into several lines", aspectRatio = 2.5f, ), groupPosition = TimelineItemGroupPosition.Last, @@ -170,9 +172,9 @@ internal fun TimelineImageWithCaptionRowPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( isMine = false, - content = aTimelineItemImageContent().copy( + content = aTimelineItemImageContent( filename = "image.jpg", - body = "Image with null aspectRatio", + caption = "Image with null aspectRatio", aspectRatio = null, ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt index cef5acd1dd4..0574843dbaf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt @@ -43,7 +43,7 @@ fun TimelineItemStickerView( onShowClick: () -> Unit, modifier: Modifier = Modifier, ) { - val description = content.body.takeIf { it.isNotEmpty() } ?: stringResource(CommonStrings.common_image) + val description = content.bestDescription.takeIf { it.isNotEmpty() } ?: stringResource(CommonStrings.common_image) Column( modifier = modifier.semantics { contentDescription = description }, ) { @@ -65,7 +65,7 @@ fun TimelineItemStickerView( model = MediaRequestData( source = content.preferredMediaSource, kind = MediaRequestData.Kind.File( - body = content.body, + fileName = content.filename, mimeType = content.mimeType, ), ), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 7815e02610d..e743338ccf4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -76,8 +76,8 @@ fun TimelineItemVideoView( ) { val containerModifier = if (content.showCaption) { Modifier - .padding(top = 6.dp) - .clip(RoundedCornerShape(6.dp)) + .padding(top = 6.dp) + .clip(RoundedCornerShape(6.dp)) } else { Modifier } @@ -93,12 +93,12 @@ fun TimelineItemVideoView( var isLoaded by remember { mutableStateOf(false) } AsyncImage( modifier = Modifier - .fillMaxWidth() - .then(if (isLoaded) Modifier.background(Color.White) else Modifier), + .fillMaxWidth() + .then(if (isLoaded) Modifier.background(Color.White) else Modifier), model = MediaRequestData( source = content.thumbnailSource, kind = MediaRequestData.Kind.File( - body = content.filename ?: content.body, + fileName = content.filename, mimeType = content.mimeType ) ), @@ -126,7 +126,9 @@ fun TimelineItemVideoView( val caption = if (LocalInspectionMode.current) { SpannedString(content.caption) } else { - content.formatted?.body?.takeIf { content.formatted.format == MessageFormat.HTML } ?: SpannedString(content.caption) + content.formattedCaption?.body + ?.takeIf { content.formattedCaption.format == MessageFormat.HTML } + ?: SpannedString(content.caption) } CompositionLocalProvider( LocalContentColor provides ElementTheme.colors.textPrimary, @@ -178,7 +180,7 @@ internal fun TimelineVideoWithCaptionRowPreview() = ElementPreview { isMine = isMine, content = aTimelineItemVideoContent().copy( filename = "video.mp4", - body = "A long caption that may wrap into several lines", + caption = "A long caption that may wrap into several lines", aspectRatio = 2.5f, ), groupPosition = TimelineItemGroupPosition.Last, @@ -190,7 +192,7 @@ internal fun TimelineVideoWithCaptionRowPreview() = ElementPreview { isMine = false, content = aTimelineItemVideoContent().copy( filename = "video.mp4", - body = "Video with null aspect ratio", + caption = "Video with null aspect ratio", aspectRatio = null, ), groupPosition = TimelineItemGroupPosition.Last, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index d171c3dd0f6..3eb0c665940 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -84,9 +84,9 @@ class TimelineItemContentMessageFactory @Inject constructor( is ImageMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemImageContent( - body = messageType.body.trimEnd(), - formatted = messageType.formatted, filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -95,13 +95,15 @@ class TimelineItemContentMessageFactory @Inject constructor( height = messageType.info?.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = messageType.filename?.let { fileExtensionExtractor.extractFromName(it) }.orEmpty() + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) ) } is StickerMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemStickerContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, thumbnailSource = messageType.info?.thumbnailSource, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -110,7 +112,7 @@ class TimelineItemContentMessageFactory @Inject constructor( height = messageType.info?.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body) + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) ) } is LocationMessageType -> { @@ -136,9 +138,9 @@ class TimelineItemContentMessageFactory @Inject constructor( is VideoMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemVideoContent( - body = messageType.body.trimEnd(), - formatted = messageType.formatted, filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, thumbnailSource = messageType.info?.thumbnailSource, videoSource = messageType.source, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -148,17 +150,19 @@ class TimelineItemContentMessageFactory @Inject constructor( blurHash = messageType.info?.blurhash, aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = messageType.filename?.let { fileExtensionExtractor.extractFromName(it) }.orEmpty(), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } is AudioMessageType -> { TimelineItemAudioContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } is VoiceMessageType -> { @@ -166,7 +170,9 @@ class TimelineItemContentMessageFactory @Inject constructor( true -> { TimelineItemVoiceContent( eventId = eventId, - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, @@ -175,20 +181,24 @@ class TimelineItemContentMessageFactory @Inject constructor( } false -> { TimelineItemAudioContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, mediaSource = messageType.source, duration = messageType.info?.duration ?: Duration.ZERO, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(messageType.body), + fileExtension = fileExtensionExtractor.extractFromName(messageType.filename), ) } } } is FileMessageType -> { - val fileExtension = fileExtensionExtractor.extractFromName(messageType.body) + val fileExtension = fileExtensionExtractor.extractFromName(messageType.filename) TimelineItemFileContent( - body = messageType.body.trimEnd(), + filename = messageType.filename, + caption = messageType.caption?.trimEnd(), + formattedCaption = messageType.formattedCaption, thumbnailSource = messageType.info?.thumbnailSource, fileSource = messageType.source, mimeType = messageType.info?.mimetype ?: MimeTypes.fromFileExtension(fileExtension), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt index 787e4d4be63..b76dfdf07b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt @@ -33,7 +33,9 @@ class TimelineItemContentStickerFactory @Inject constructor( val aspectRatio = aspectRatioOf(content.info.width, content.info.height) return TimelineItemStickerContent( - body = content.body, + filename = content.filename, + caption = content.body, + formattedCaption = null, mediaSource = content.source, thumbnailSource = content.info.thumbnailSource, mimeType = content.info.mimetype ?: MimeTypes.OctetStream, @@ -42,7 +44,7 @@ class TimelineItemContentStickerFactory @Inject constructor( height = content.info.height?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(content.info.size ?: 0), - fileExtension = fileExtensionExtractor.extractFromName(content.body) + fileExtension = fileExtensionExtractor.extractFromName(content.filename) ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt index aa33804d8ec..fa2957b8604 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContent.kt @@ -8,17 +8,20 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize import kotlin.time.Duration data class TimelineItemAudioContent( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val duration: Duration, val mediaSource: MediaSource, val mimeType: String, val formattedFileSize: String, val fileExtension: String, -) : TimelineItemEventContent { +) : TimelineItemEventContentWithAttachment { val fileExtensionAndSize = formatFileExtensionAndSize( fileExtension, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt index 25933e9ecdd..7d4f6baf846 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemAudioContentProvider.kt @@ -22,8 +22,10 @@ open class TimelineItemAudioContentProvider : PreviewParameterProvider, -) : TimelineItemEventContent { +) : TimelineItemEventContentWithAttachment { override val type: String = "TimelineItemAudioContent" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt index 84354008f51..ddd731eceb3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemVoiceContentProvider.kt @@ -35,17 +35,21 @@ open class TimelineItemVoiceContentProvider : PreviewParameterProvider = listOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 8f, 7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f), ) = TimelineItemVoiceContent( - eventId = eventId?.let { EventId(it) }, - body = body, + eventId = eventId, + filename = filename, + caption = caption, + formattedCaption = null, duration = duration, - mediaSource = MediaSource(contentUri), + mediaSource = mediaSource, mimeType = mimeType, waveform = waveform.toPersistentList(), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 3ab00a71bc7..155b47728a1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -59,7 +59,7 @@ class VoiceMessagePresenter @AssistedInject constructor( eventId = content.eventId, mediaSource = content.mediaSource, mimeType = content.mimeType, - body = content.body, + body = content.caption, ) private val play = mutableStateOf>(AsyncData.Uninitialized) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 284f141d41c..14ef106578d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -334,9 +334,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( - body = "image.jpg", - formatted = null, - filename = null, + filename = "image.jpg", + caption = null, + formattedCaption = null, mediaSource = MediaSource(AN_AVATAR_URL), thumbnailSource = null, mimeType = MimeTypes.Jpeg, @@ -373,9 +373,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( - body = "video.mp4", - formatted = null, - filename = null, + filename = "video.mp4", + caption = null, + formattedCaption = null, duration = 10.milliseconds, videoSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL), @@ -413,7 +413,9 @@ class MessagesPresenterTest { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( - body = "file.pdf", + filename = "file.pdf", + caption = null, + formattedCaption = null, fileSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL), formattedFileSize = "10 MB", diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt index 529a280994e..014e5e939e1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt @@ -69,7 +69,7 @@ class PinnedMessagesListViewTest { state = state, onEventClick = callback ) - rule.onAllNodesWithText(content.body).onFirst().performClick() + rule.onAllNodesWithText(content.filename).onFirst().performClick() } } @@ -85,7 +85,7 @@ class PinnedMessagesListViewTest { rule.setPinnedMessagesListView( state = state, ) - rule.onAllNodesWithText(content.body).onFirst() + rule.onAllNodesWithText(content.filename).onFirst() .performTouchInput { longClick() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index c627ff3fdce..337ca9ab7ea 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -62,6 +62,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.media.aMediaSource import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation import kotlinx.collections.immutable.persistentListOf @@ -228,14 +229,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create VideoMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = VideoMessageType("body", null, null, MediaSource("url"), null)), + content = createMessageContent(type = VideoMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemVideoContent( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, videoSource = MediaSource(url = "url", json = null), thumbnailSource = null, @@ -256,9 +257,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = VideoMessageType( - body = "body.mp4 caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.mp4", + caption = "body.mp4 caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), source = MediaSource("url"), info = VideoInfo( duration = 1.minutes, @@ -281,9 +282,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemVideoContent( - body = "body.mp4 caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.mp4", + caption = "body.mp4 caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), duration = 1.minutes, videoSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), @@ -302,12 +303,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create AudioMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = AudioMessageType("body", MediaSource("url"), null)), + content = createMessageContent(type = AudioMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -323,7 +326,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = AudioMessageType( - body = "body.mp3", + filename = "body.mp3", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = AudioInfo( duration = 1.minutes, @@ -336,7 +341,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body.mp3", + filename = "body.mp3", + caption = null, + formattedCaption = null, duration = 1.minutes, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.Mp3, @@ -350,13 +357,15 @@ class TimelineItemContentMessageFactoryTest { fun `test create VoiceMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemVoiceContent( + filename = "filename", eventId = AN_EVENT_ID, - body = "body", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -371,7 +380,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = VoiceMessageType( - body = "body.ogg", + filename = "body.ogg", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = AudioInfo( duration = 1.minutes, @@ -389,7 +400,9 @@ class TimelineItemContentMessageFactoryTest { ) val expected = TimelineItemVoiceContent( eventId = AN_EVENT_ID, - body = "body.ogg", + filename = "body.ogg", + caption = null, + formattedCaption = null, duration = 1.minutes, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.Ogg, @@ -408,12 +421,14 @@ class TimelineItemContentMessageFactoryTest { ) ) val result = sut.create( - content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)), + content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemAudioContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, duration = Duration.ZERO, mediaSource = MediaSource(url = "url", json = null), mimeType = MimeTypes.OctetStream, @@ -427,14 +442,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create ImageMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = ImageMessageType("body", null, null, MediaSource("url"), null)), + content = createMessageContent(type = ImageMessageType("filename", "body", null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemImageContent( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "body", + formattedCaption = null, mediaSource = MediaSource(url = "url", json = null), thumbnailSource = null, formattedFileSize = "0 Bytes", @@ -453,13 +468,15 @@ class TimelineItemContentMessageFactoryTest { val sut = createTimelineItemContentStickerFactory() val result = sut.create( content = createStickerContent( - "body", - ImageInfo(32, 32, "image/webp", 8192, null, MediaSource("thumbnail://url"), null), - "url" + filename = "filename", + inImageInfo = ImageInfo(32, 32, "image/webp", 8192, null, MediaSource("thumbnail://url"), null), + inUrl = "url" ) ) val expected = TimelineItemStickerContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, mediaSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource(url = "thumbnail://url", json = null), formattedFileSize = "8192 Bytes", @@ -479,9 +496,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = ImageMessageType( - body = "body.jpg caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.jpg", + caption = "body.jpg caption", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), source = MediaSource("url"), info = ImageInfo( height = 10L, @@ -503,9 +520,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemImageContent( - body = "body.jpg caption", - formatted = FormattedBody(MessageFormat.HTML, "formatted"), filename = "body.jpg", + formattedCaption = FormattedBody(MessageFormat.HTML, "formatted"), + caption = "body.jpg caption", mediaSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), formattedFileSize = "888 Bytes", @@ -523,12 +540,14 @@ class TimelineItemContentMessageFactoryTest { fun `test create FileMessageType`() = runTest { val sut = createTimelineItemContentMessageFactory() val result = sut.create( - content = createMessageContent(type = FileMessageType("body", MediaSource("url"), null)), + content = createMessageContent(type = FileMessageType("filename", null, null, MediaSource("url"), null)), senderDisambiguatedDisplayName = "Bob", eventId = AN_EVENT_ID, ) val expected = TimelineItemFileContent( - body = "body", + filename = "filename", + caption = null, + formattedCaption = null, fileSource = MediaSource(url = "url", json = null), thumbnailSource = null, formattedFileSize = "0 Bytes", @@ -544,7 +563,9 @@ class TimelineItemContentMessageFactoryTest { val result = sut.create( content = createMessageContent( type = FileMessageType( - body = "body.pdf", + filename = "body.pdf", + caption = null, + formattedCaption = null, source = MediaSource("url"), info = FileInfo( mimetype = MimeTypes.Pdf, @@ -563,7 +584,9 @@ class TimelineItemContentMessageFactoryTest { eventId = AN_EVENT_ID, ) val expected = TimelineItemFileContent( - body = "body.pdf", + filename = "body.pdf", + caption = null, + formattedCaption = null, fileSource = MediaSource(url = "url", json = null), thumbnailSource = MediaSource("url_thumbnail"), formattedFileSize = "123 Bytes", @@ -749,14 +772,16 @@ class TimelineItemContentMessageFactoryTest { ) private fun createStickerContent( - body: String = "Body", + filename: String = "filename", inImageInfo: ImageInfo, - inUrl: String + inUrl: String, + body: String? = null, ): StickerContent { - return StickerContent( + return aStickerContent( + filename = filename, body = body, info = inImageInfo, - source = aMediaSource(url = inUrl), + mediaSource = aMediaSource(url = inUrl), ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index c501c6cf38c..4030e302721 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -206,7 +206,8 @@ class RoomDetailsFlowNode @AssistedInject constructor( val mimeType = MimeTypes.Images val input = MediaViewerNode.Inputs( mediaInfo = MediaInfo( - name = navTarget.name, + filename = navTarget.name, + caption = null, mimeType = mimeType, formattedFileSize = "", fileExtension = "" diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index 26b578a39de..b544ad4750b 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -84,10 +84,11 @@ class UserProfileFlowNode @AssistedInject constructor( val mimeType = MimeTypes.Images val input = MediaViewerNode.Inputs( mediaInfo = MediaInfo( - name = navTarget.name, + filename = navTarget.name, + caption = null, mimeType = mimeType, formattedFileSize = "", - fileExtension = "" + fileExtension = "", ), mediaSource = MediaSource(url = navTarget.avatarUrl), thumbnailSource = null, diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt index 8fb18e1c3e7..ab7a19a9c78 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatter.kt @@ -46,7 +46,8 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( return when (val content = event.content) { is MessageContent -> processMessageContents(event, content) is StickerContent -> { - content.body.prefixWith(CommonStrings.common_sticker) + val text = content.body ?: content.filename + text.prefixWith(CommonStrings.common_sticker) } is UnableToDecryptContent -> { sp.getString(CommonStrings.common_waiting_for_decryption_key) @@ -76,25 +77,25 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - messageType.body.prefixWith(CommonStrings.common_video) + messageType.bestDescription.prefixWith(CommonStrings.common_video) } is ImageMessageType -> { - messageType.body.prefixWith(CommonStrings.common_image) + messageType.bestDescription.prefixWith(CommonStrings.common_image) } is StickerMessageType -> { - messageType.body.prefixWith(CommonStrings.common_sticker) + messageType.bestDescription.prefixWith(CommonStrings.common_sticker) } is LocationMessageType -> { messageType.body.prefixWith(CommonStrings.common_shared_location) } is FileMessageType -> { - messageType.body.prefixWith(CommonStrings.common_file) + messageType.bestDescription.prefixWith(CommonStrings.common_file) } is AudioMessageType -> { - messageType.body.prefixWith(CommonStrings.common_audio) + messageType.bestDescription.prefixWith(CommonStrings.common_audio) } is VoiceMessageType -> { - messageType.body.prefixWith(CommonStrings.common_voice_message) + messageType.bestDescription.prefixWith(CommonStrings.common_voice_message) } is OtherMessageType -> { messageType.body diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 200b93343dd..6b43fc3607f 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -67,7 +67,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is StickerContent -> { - val message = sp.getString(CommonStrings.common_sticker) + " (" + content.body + ")" + val message = sp.getString(CommonStrings.common_sticker) + " (" + content.bestDescription + ")" message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is UnableToDecryptContent -> { diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt index 6247065e375..af347135fbf 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultPinnedMessagesBannerFormatterTest.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.OtherState import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent @@ -46,6 +45,7 @@ import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.timeline.aPollContent import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.toolbox.impl.strings.AndroidStringProvider @@ -91,7 +91,7 @@ class DefaultPinnedMessagesBannerFormatterTest { fun `Sticker content`() { val body = "a sticker body" val info = ImageInfo(null, null, null, null, null, null, null) - val message = createRoomEvent(false, null, StickerContent(body, info, aMediaSource(url = "url"))) + val message = createRoomEvent(false, null, aStickerContent(body, info, aMediaSource(url = "url"))) val result = formatter.format(message) val expectedBody = "Sticker: a sticker body" assertThat(result.toString()).isEqualTo(expectedBody) @@ -135,11 +135,11 @@ class DefaultPinnedMessagesBannerFormatterTest { val sharedContentMessagesTypes = arrayOf( TextMessageType(body, null), VideoMessageType(body, null, null, MediaSource("url"), null), - AudioMessageType(body, MediaSource("url"), null), - VoiceMessageType(body, MediaSource("url"), null, null), + AudioMessageType(body, null, null, MediaSource("url"), null), + VoiceMessageType(body, null, null, MediaSource("url"), null, null), ImageMessageType(body, null, null, MediaSource("url"), null), - StickerMessageType(body, MediaSource("url"), null), - FileMessageType(body, MediaSource("url"), null), + StickerMessageType(body, null, null, MediaSource("url"), null), + FileMessageType(body, null, null, MediaSource("url"), null), LocationMessageType(body, "geo:1,2", null), NoticeMessageType(body, null), EmoteMessageType(body, null), diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt index db3377c5e83..efc748d10fd 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt @@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.OtherState import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent @@ -46,6 +45,7 @@ import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.timeline.aPollContent import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails +import io.element.android.libraries.matrix.test.timeline.aStickerContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import org.junit.Before @@ -98,7 +98,7 @@ class DefaultRoomLastMessageFormatterTest { fun `Sticker content`() { val body = "a sticker body" val info = ImageInfo(null, null, null, null, null, null, null) - val message = createRoomEvent(false, null, StickerContent(body, info, aMediaSource(url = "url"))) + val message = createRoomEvent(false, null, aStickerContent(body, info, aMediaSource(url = "url"))) val result = formatter.format(message, false) val expectedBody = someoneElseId.toString() + ": Sticker (a sticker body)" assertThat(result.toString()).isEqualTo(expectedBody) @@ -179,11 +179,11 @@ class DefaultRoomLastMessageFormatterTest { val sharedContentMessagesTypes = arrayOf( TextMessageType(body, null), VideoMessageType(body, null, null, MediaSource("url"), null), - AudioMessageType(body, MediaSource("url"), null), - VoiceMessageType(body, MediaSource("url"), null, null), + AudioMessageType(body, null, null, MediaSource("url"), null), + VoiceMessageType(body, null, null, MediaSource("url"), null, null), ImageMessageType(body, null, null, MediaSource("url"), null), - StickerMessageType(body, MediaSource("url"), null), - FileMessageType(body, MediaSource("url"), null), + StickerMessageType(body, null, null, MediaSource("url"), null), + FileMessageType(body, null, null, MediaSource("url"), null), LocationMessageType(body, "geo:1,2", null), NoticeMessageType(body, null), EmoteMessageType(body, null), diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index fc65d309551..ebe8a10bdc2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -30,10 +30,14 @@ data class MessageContent( data object RedactedContent : EventContent data class StickerContent( - val body: String, + val filename: String, + val body: String?, val info: ImageInfo, val source: MediaSource, -) : EventContent +) : EventContent { + val bestDescription: String + get() = body ?: filename +} data class PollContent( val question: String, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt index b398e056bce..9237c5c8ea8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt @@ -18,24 +18,37 @@ import io.element.android.libraries.matrix.api.media.VideoInfo @Immutable sealed interface MessageType +@Immutable +sealed interface MessageTypeWithAttachment : MessageType { + val filename: String + val caption: String? + val formattedCaption: FormattedBody? + + val bestDescription: String + get() = caption ?: filename +} + data class EmoteMessageType( val body: String, val formatted: FormattedBody? ) : MessageType data class ImageMessageType( - val body: String, - val formatted: FormattedBody?, - val filename: String?, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: ImageInfo? -) : MessageType +) : MessageTypeWithAttachment +// FIXME This is never used in production code. data class StickerMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: ImageInfo? -) : MessageType +) : MessageTypeWithAttachment data class LocationMessageType( val body: String, @@ -44,31 +57,37 @@ data class LocationMessageType( ) : MessageType data class AudioMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: AudioInfo?, -) : MessageType +) : MessageTypeWithAttachment data class VoiceMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: AudioInfo?, val details: AudioDetails?, -) : MessageType +) : MessageTypeWithAttachment data class VideoMessageType( - val body: String, - val formatted: FormattedBody?, - val filename: String?, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: VideoInfo? -) : MessageType +) : MessageTypeWithAttachment data class FileMessageType( - val body: String, + override val filename: String, + override val caption: String?, + override val formattedCaption: FormattedBody?, val source: MediaSource, val info: FileInfo? -) : MessageType +) : MessageTypeWithAttachment data class NoticeMessageType( val body: String, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index 3e67bd85954..cc6bb87f0f3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -50,14 +50,18 @@ class EventMessageMapper { when (type.content.voice) { null -> { AudioMessageType( - body = type.content.body, + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), source = type.content.source.map(), info = type.content.info?.map(), ) } else -> { VoiceMessageType( - body = type.content.body, + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), source = type.content.source.map(), info = type.content.info?.map(), details = type.content.audio?.map(), @@ -66,10 +70,22 @@ class EventMessageMapper { } } is RustMessageType.File -> { - FileMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) + FileMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Image -> { - ImageMessageType(type.content.body, type.content.formatted?.map(), type.content.filename, type.content.source.map(), type.content.info?.map()) + ImageMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Notice -> { NoticeMessageType(type.content.body, type.content.formatted?.map()) @@ -81,7 +97,13 @@ class EventMessageMapper { EmoteMessageType(type.content.body, type.content.formatted?.map()) } is RustMessageType.Video -> { - VideoMessageType(type.content.body, type.content.formatted?.map(), type.content.filename, type.content.source.map(), type.content.info?.map()) + VideoMessageType( + filename = type.content.filename, + caption = type.content.caption, + formattedCaption = type.content.formattedCaption?.map(), + source = type.content.source.map(), + info = type.content.info?.map(), + ) } is RustMessageType.Location -> { LocationMessageType(type.content.body, type.content.geoUri, type.content.description) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index ed075c508f3..3c0ec16f659 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -84,7 +84,8 @@ class TimelineEventContentMapper( } is TimelineItemContent.Sticker -> { StickerContent( - body = it.body, + filename = it.body, + body = null, info = it.info.map(), source = it.source.map(), ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt index b8bc798b28b..85833f183a6 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/TimelineFixture.kt @@ -10,6 +10,8 @@ package io.element.android.libraries.matrix.test.timeline import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId 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.MediaSource import io.element.android.libraries.matrix.api.poll.PollAnswer import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo @@ -25,6 +27,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.Receipt +import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -110,6 +113,18 @@ fun aMessageContent( type = messageType ) +fun aStickerContent( + filename: String = "filename", + info: ImageInfo, + mediaSource: MediaSource, + body: String? = null, +) = StickerContent( + filename = filename, + body = body, + info = info, + source = mediaSource, +) + fun aTimelineItemDebugInfo( model: String = "Rust(Model())", originalJson: String? = null, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index f6a3e5a3d2a..a93ce21dea6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -46,7 +46,7 @@ internal class CoilMediaFetcher( * */ private suspend fun fetchFile(mediaSource: MediaSource, kind: MediaRequestData.Kind.File): FetchResult? { - return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.body) + return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.fileName) .map { mediaFile -> val file = mediaFile.toFile() SourceResult( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt index ac073d38405..38499d15fbd 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt @@ -26,7 +26,12 @@ data class MediaRequestData( ) { sealed interface Kind { data object Content : Kind - data class File(val body: String?, val mimeType: String) : Kind + + data class File( + val fileName: String, + val mimeType: String, + ) : Kind + data class Thumbnail(val width: Long, val height: Long) : Kind { constructor(size: Long) : this(size, size) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt index 301966566ad..58b3506d463 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt @@ -49,11 +49,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider ), aMessageContent( body = "Audio", - type = AudioMessageType("Audio", MediaSource("url"), null), + type = AudioMessageType("Audio", null, null, MediaSource("url"), null), ), aMessageContent( body = "Voice", - type = VoiceMessageType("Voice", MediaSource("url"), null, null), + type = VoiceMessageType("Voice", null, null, MediaSource("url"), null, null), ), aMessageContent( body = "Image", @@ -61,11 +61,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider ), aMessageContent( body = "Sticker", - type = StickerMessageType("Image", MediaSource("url"), null), + type = StickerMessageType("Image", null, null, MediaSource("url"), null), ), aMessageContent( body = "File", - type = FileMessageType("File", MediaSource("url"), null), + type = FileMessageType("File", null, null, MediaSource("url"), null), ), aMessageContent( body = "Location", diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt index 9e4318b3be0..e2249551642 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt @@ -75,9 +75,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = ImageMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, source = aMediaSource(), info = anImageInfo(), ) @@ -105,9 +105,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = ImageMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = anImageInfo(), ) @@ -134,6 +134,7 @@ class InReplyToMetadataKtTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( eventContent = StickerContent( + filename = "filename", body = "body", info = anImageInfo(), source = aMediaSource(url = "url") @@ -160,6 +161,7 @@ class InReplyToMetadataKtTest { moleculeFlow(RecompositionMode.Immediate) { anInReplyToDetailsReady( eventContent = StickerContent( + filename = "filename", body = "body", info = anImageInfo(), source = aMediaSource(url = "url") @@ -187,9 +189,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VideoMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = null, + formattedCaption = null, source = aMediaSource(), info = aVideoInfo(), ) @@ -217,9 +219,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VideoMessageType( - body = "body", - formatted = null, - filename = null, + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = aVideoInfo(), ) @@ -247,7 +249,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = FileMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = FileInfo( mimetype = null, @@ -280,7 +284,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = FileMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = FileInfo( mimetype = null, @@ -313,7 +319,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = AudioMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = AudioInfo( duration = null, @@ -375,7 +383,9 @@ class InReplyToMetadataKtTest { anInReplyToDetailsReady( eventContent = aMessageContent( messageType = VoiceMessageType( - body = "body", + filename = "filename", + caption = "caption", + formattedCaption = null, source = aMediaSource(), info = null, details = null, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt index ef27ce68a7f..7b63c22121e 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/LocalMediaView.kt @@ -303,7 +303,7 @@ private fun MediaFileView( if (info != null) { Spacer(modifier = Modifier.height(20.dp)) Text( - text = info.name, + text = info.filename, maxLines = 2, style = ElementTheme.typography.fontBodyLgRegular, overflow = TextOverflow.Ellipsis, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt index c836a25a854..5ded65b2b34 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt @@ -13,43 +13,49 @@ import kotlinx.parcelize.Parcelize @Parcelize data class MediaInfo( - val name: String, + val filename: String, + val caption: String?, val mimeType: String, val formattedFileSize: String, val fileExtension: String, ) : Parcelable fun anImageMediaInfo(): MediaInfo = MediaInfo( - "an image file.jpg", - MimeTypes.Jpeg, - "4MB", - "jpg" + filename = "an image file.jpg", + caption = null, + mimeType = MimeTypes.Jpeg, + formattedFileSize = "4MB", + fileExtension = "jpg", ) fun aVideoMediaInfo(): MediaInfo = MediaInfo( - "a video file.mp4", - MimeTypes.Mp4, - "14MB", - "mp4" + filename = "a video file.mp4", + caption = null, + mimeType = MimeTypes.Mp4, + formattedFileSize = "14MB", + fileExtension = "mp4", ) fun aPdfMediaInfo(): MediaInfo = MediaInfo( - "a pdf file.pdf", - MimeTypes.Pdf, - "23MB", - "pdf" + filename = "a pdf file.pdf", + caption = null, + mimeType = MimeTypes.Pdf, + formattedFileSize = "23MB", + fileExtension = "pdf", ) fun anApkMediaInfo(): MediaInfo = MediaInfo( - "an apk file.apk", - MimeTypes.Apk, - "50MB", - "apk" + filename = "an apk file.apk", + caption = null, + mimeType = MimeTypes.Apk, + formattedFileSize = "50MB", + fileExtension = "apk", ) fun anAudioMediaInfo(): MediaInfo = MediaInfo( - "an audio file.mp3", - MimeTypes.Mp3, - "7MB", - "mp3" + filename = "an audio file.mp3", + caption = null, + mimeType = MimeTypes.Mp3, + formattedFileSize = "7MB", + fileExtension = "mp3", ) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt index c62094b364d..3d51096dd31 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerPresenter.kt @@ -92,7 +92,7 @@ class MediaViewerPresenter @AssistedInject constructor( mediaLoader.downloadMediaFile( source = inputs.mediaSource, mimeType = inputs.mediaInfo.mimeType, - body = inputs.mediaInfo.name + body = inputs.mediaInfo.filename ) .onSuccess { mediaFile.value = it diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt index f76bf510732..9f362c13fa3 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerView.kt @@ -322,7 +322,7 @@ private fun ThumbnailView( if (isVisible) { val mediaRequestData = MediaRequestData( source = thumbnailSource, - kind = MediaRequestData.Kind.File(mediaInfo.name, mediaInfo.mimeType) + kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType) ) AsyncImage( modifier = Modifier.fillMaxSize(), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt index 33c63d4f727..66bed465ed9 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaActions.kt @@ -157,7 +157,7 @@ class AndroidLocalMediaActions @Inject constructor( @RequiresApi(Build.VERSION_CODES.Q) private fun saveOnDiskUsingMediaStore(localMedia: LocalMedia) { val contentValues = ContentValues().apply { - put(MediaStore.MediaColumns.DISPLAY_NAME, localMedia.info.name) + put(MediaStore.MediaColumns.DISPLAY_NAME, localMedia.info.filename) put(MediaStore.MediaColumns.MIME_TYPE, localMedia.info.mimeType) put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) } @@ -175,7 +175,7 @@ class AndroidLocalMediaActions @Inject constructor( private fun saveOnDiskUsingExternalStorageApi(localMedia: LocalMedia) { val target = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - localMedia.info.name + localMedia.info.filename ) localMedia.openStream()?.use { input -> FileOutputStream(target).use { output -> diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index d8e5af82337..06fefa26230 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -32,21 +32,36 @@ class AndroidLocalMediaFactory @Inject constructor( private val fileSizeFormatter: FileSizeFormatter, private val fileExtensionExtractor: FileExtensionExtractor, ) : LocalMediaFactory { - override fun createFromMediaFile(mediaFile: MediaFile, mediaInfo: MediaInfo): LocalMedia { - val uri = mediaFile.toFile().toUri() - return createFromUri( - uri = uri, - mimeType = mediaInfo.mimeType, - name = mediaInfo.name, - formattedFileSize = mediaInfo.formattedFileSize, - ) - } + override fun createFromMediaFile( + mediaFile: MediaFile, + mediaInfo: MediaInfo, + ): LocalMedia = createFromUri( + uri = mediaFile.toFile().toUri(), + mimeType = mediaInfo.mimeType, + name = mediaInfo.filename, + caption = mediaInfo.caption, + formattedFileSize = mediaInfo.formattedFileSize, + ) override fun createFromUri( uri: Uri, mimeType: String?, name: String?, formattedFileSize: String? + ): LocalMedia = createFromUri( + uri = uri, + mimeType = mimeType, + name = name, + caption = null, + formattedFileSize = formattedFileSize, + ) + + private fun createFromUri( + uri: Uri, + mimeType: String?, + name: String?, + caption: String?, + formattedFileSize: String? ): LocalMedia { val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream val fileName = name ?: context.getFileName(uri) ?: "" @@ -56,7 +71,8 @@ class AndroidLocalMediaFactory @Inject constructor( uri = uri, info = MediaInfo( mimeType = resolvedMimeType, - name = fileName, + filename = fileName, + caption = caption, formattedFileSize = fileSize, fileExtension = fileExtension ) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt index af0a1e9b887..eb78517a36a 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -29,7 +29,8 @@ class AndroidLocalMediaFactoryTest { assertThat(result.uri.toString()).endsWith("aPath") assertThat(result.info).isEqualTo( MediaInfo( - name = "an image file.jpg", + filename = "an image file.jpg", + caption = null, mimeType = MimeTypes.Jpeg, formattedFileSize = "4MB", fileExtension = "jpg", diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index cc8b29da2bb..43474e24bb5 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -32,7 +32,8 @@ class FakeLocalMediaFactory( override fun createFromUri(uri: Uri, mimeType: String?, name: String?, formattedFileSize: String?): LocalMedia { val safeName = name ?: fallbackName val mediaInfo = MediaInfo( - name = safeName, + filename = safeName, + caption = null, mimeType = mimeType ?: fallbackMimeType, formattedFileSize = formattedFileSize ?: fallbackFileSize, fileExtension = fileExtensionExtractor.extractFromName(safeName) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index 08e95fcfcf5..1d6e9161240 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -265,15 +265,15 @@ class DefaultNotifiableEventResolver @Inject constructor( senderDisambiguatedDisplayName: String, ): String { return when (val messageType = content.messageType) { - is AudioMessageType -> messageType.body + is AudioMessageType -> messageType.bestDescription is VoiceMessageType -> stringProvider.getString(CommonStrings.common_voice_message) is EmoteMessageType -> "* $senderDisambiguatedDisplayName ${messageType.body}" - is FileMessageType -> messageType.body - is ImageMessageType -> messageType.body - is StickerMessageType -> messageType.body + is FileMessageType -> messageType.bestDescription + is ImageMessageType -> messageType.bestDescription + is StickerMessageType -> messageType.bestDescription is NoticeMessageType -> messageType.body is TextMessageType -> messageType.toPlainText(permalinkParser = permalinkParser) - is VideoMessageType -> messageType.body + is VideoMessageType -> messageType.bestDescription is LocationMessageType -> messageType.body is OtherMessageType -> messageType.body } @@ -299,7 +299,7 @@ class DefaultNotifiableEventResolver @Inject constructor( .getMediaFile( mediaSource = messageType.source, mimeType = messageType.info?.mimetype, - body = messageType.body, + body = messageType.filename, ) is VideoMessageType -> null // Use the thumbnail here? else -> null diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt index 143f134656a..f8818cf341b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -47,7 +47,7 @@ interface NotificationMediaRepo { * * @param mediaSource the media source of the media. * @param mimeType the mime type of the media. - * @param body the body of the message. + * @param body optional body which will be used to name the file. * @return A [Result] holding either the media [File] from the cache directory or an [Exception]. */ suspend fun getMediaFile( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index aae4d84d382..de29fcf1f01 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -187,7 +187,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = AudioMessageType(body = "Audio", MediaSource("url"), null) + messageType = AudioMessageType("Audio", null, null, MediaSource("url"), null) ), ) ) @@ -206,7 +206,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = VideoMessageType(body = "Video", null, null, MediaSource("url"), null) + messageType = VideoMessageType("Video", null, null, MediaSource("url"), null) ), ) ) @@ -225,7 +225,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = VoiceMessageType(body = "Voice", MediaSource("url"), null, null) + messageType = VoiceMessageType("Voice", null, null, MediaSource("url"), null, null) ), ) ) @@ -263,7 +263,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = StickerMessageType("Sticker", MediaSource("url"), null), + messageType = StickerMessageType("Sticker", null, null, MediaSource("url"), null), ), ) ) @@ -282,7 +282,7 @@ class DefaultNotifiableEventResolverTest { aNotificationData( content = NotificationContent.MessageLike.RoomMessage( senderId = A_USER_ID_2, - messageType = FileMessageType("File", MediaSource("url"), null), + messageType = FileMessageType("File", null, null, MediaSource("url"), null), ), ) ) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png index 518d669991d..07d4116d650 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c13e631e41cafa49321988065bedff6081fbc4aa99d9cb3a32b4d860888c7535 -size 42412 +oid sha256:0f5adfb435286a84587a66d6970b1a7f03a8cb53eac86956f61092d97ea16951 +size 43121 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png index 59f8a02c54e..eed6d921be9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_SheetContent_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35988d035ddf2eea2463586024703fc630f996c6e03a232fc65196ab5186b89a -size 41582 +oid sha256:87d68af6f69c67de710145b3fabbff40bc6c9a767542e5a62be80d084972cb28 +size 42221