Render MSC2530 captions

Change-Id: I10f875121e90102a0518d9bd39d87b3daa68ef2e
This commit is contained in:
SpiritCroc 2022-11-10 10:38:04 +01:00
parent 3214c782bc
commit 58dd1dedc9
21 changed files with 192 additions and 23 deletions

View file

@ -32,6 +32,7 @@ Here you can find some extra features and changes compared to Element Android (w
- Render inline images / custom emojis in the timeline
- Render image reactions
- Send freeform reactions
- Render media captions ([MSC2530](https://github.com/matrix-org/matrix-spec-proposals/pull/2530))
- Branding (name, app icon, links)
- Show a toast instead of a snackbar after copying text, in order to not block the input area right after copying

View file

@ -45,6 +45,11 @@ data class MessageAudioContent(
*/
@Json(name = "url") override val url: String? = null,
/**
* MSC 2530: filename as filename, using body as caption instead
*/
@Json(name = "filename") override val filename: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null,

View file

@ -38,7 +38,7 @@ data class MessageFileContent(
/**
* The original filename of the uploaded file.
*/
@Json(name = "filename") val filename: String? = null,
@Json(name = "filename") override val filename: String? = null,
/**
* Information about the file referred to in url.

View file

@ -45,6 +45,11 @@ data class MessageImageContent(
*/
@Json(name = "url") override val url: String? = null,
/**
* MSC 2530: filename as filename, using body as caption instead
*/
@Json(name = "filename") override val filename: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null,

View file

@ -46,6 +46,11 @@ data class MessageStickerContent(
*/
@Json(name = "url") override val url: String? = null,
/**
* MSC 2530: filename as filename, using body as caption instead
*/
@Json(name = "filename") override val filename: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null,

View file

@ -44,6 +44,11 @@ data class MessageVideoContent(
*/
@Json(name = "url") override val url: String? = null,
/**
* MSC 2530: filename as filename, using body as caption instead
*/
@Json(name = "filename") override val filename: String? = null,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null,

View file

@ -33,6 +33,9 @@ interface MessageWithAttachmentContent : MessageContent {
val encryptedFileInfo: EncryptedFileInfo?
val mimeType: String?
// MSC 2530 adds filename for other media types than m.file as well
val filename: String?
}
/**
@ -40,4 +43,6 @@ interface MessageWithAttachmentContent : MessageContent {
*/
fun MessageWithAttachmentContent.getFileUrl() = encryptedFileInfo?.url ?: url
fun MessageWithAttachmentContent.getFileName() = (this as? MessageFileContent)?.getFileName() ?: body
fun MessageWithAttachmentContent.getFileName() = (this as? MessageFileContent)?.getFileName() ?: filename ?: body
fun MessageWithAttachmentContent.getCaption() = body.takeIf { filename != null && filename != it }

View file

@ -108,6 +108,8 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
@ -285,7 +287,8 @@ class MessageItemFactory @Inject constructor(
return MessageAudioItem_()
.attributes(attributes)
.filename(messageContent.body)
.filename(messageContent.getFileName())
.caption(messageContent.getCaption())
.duration(messageContent.audioInfo?.duration ?: 0)
.playbackControlButtonClickListener(playbackControlButtonClickListener)
.audioMessagePlaybackTracker(audioMessagePlaybackTracker)
@ -418,7 +421,8 @@ class MessageItemFactory @Inject constructor(
.contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
.contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder)
.highlighted(highlight)
.filename(messageContent.body)
.filename(messageContent.getFileName())
.caption(messageContent.getCaption())
.iconRes(R.drawable.ic_paperclip)
}
@ -456,7 +460,8 @@ class MessageItemFactory @Inject constructor(
val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val data = ImageContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
@ -505,7 +510,8 @@ class MessageItemFactory @Inject constructor(
val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val thumbnailData = ImageContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
mimeType = messageContent.mimeType,
url = messageContent.videoInfo?.getThumbnailUrl(),
elementToDecrypt = messageContent.videoInfo?.thumbnailFile?.toElementToDecrypt(),
@ -522,7 +528,8 @@ class MessageItemFactory @Inject constructor(
val videoData = VideoContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),

View file

@ -23,6 +23,8 @@ import org.matrix.android.sdk.api.session.events.model.isVideoMessage
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -33,7 +35,8 @@ fun TimelineEvent.buildImageContentRendererData(maxHeight: Int, generateMissingV
?.let { messageImageContent ->
ImageContentRenderer.Data(
eventId = eventId,
filename = messageImageContent.body,
filename = messageImageContent.getFileName(),
caption = messageImageContent.getCaption(),
mimeType = messageImageContent.mimeType,
url = messageImageContent.getFileUrl(),
elementToDecrypt = messageImageContent.encryptedFileInfo?.toElementToDecrypt(),
@ -49,7 +52,8 @@ fun TimelineEvent.buildImageContentRendererData(maxHeight: Int, generateMissingV
val videoInfo = messageVideoContent.videoInfo
ImageContentRenderer.Data(
eventId = eventId,
filename = messageVideoContent.body,
filename = messageVideoContent.getFileName(),
caption = messageVideoContent.getCaption(),
mimeType = videoInfo?.thumbnailInfo?.mimeType,
url = videoInfo?.getThumbnailUrl(),
elementToDecrypt = videoInfo?.thumbnailFile?.toElementToDecrypt(),

View file

@ -30,11 +30,14 @@ import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.ui.views.FooteredTextView
import im.vector.app.core.utils.TextUtils
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.view.ScMessageBubbleWrapView
import im.vector.app.features.themes.ThemeUtils
@EpoxyModelClass
@ -43,6 +46,9 @@ abstract class MessageAudioItem : AbsMessageItem<MessageAudioItem.Holder>() {
@EpoxyAttribute
var filename: String = ""
@EpoxyAttribute
var caption: String? = null
@EpoxyAttribute
var mxcUrl: String = ""
@ -115,6 +121,7 @@ abstract class MessageAudioItem : AbsMessageItem<MessageAudioItem.Holder>() {
holder.fileSize.text = holder.rootLayout.context.getString(
R.string.audio_message_file_size, formattedFileSize
)
holder.captionView.setTextOrHide(caption)
holder.mainLayout.contentDescription = holder.rootLayout.context.getString(
R.string.a11y_audio_message_item, filename, durationContentDescription, formattedFileSize
)
@ -195,6 +202,19 @@ abstract class MessageAudioItem : AbsMessageItem<MessageAudioItem.Holder>() {
audioMessagePlaybackTracker.untrack(attributes.informationData.eventId)
}
private fun hasCaption() = !caption.isNullOrBlank()
override fun allowFooterOverlay(holder: Holder, bubbleWrapView: ScMessageBubbleWrapView): Boolean = hasCaption()
override fun needsFooterReservation(): Boolean {
return hasCaption()
}
override fun reserveFooterSpace(holder: Holder, width: Int, height: Int) {
holder.captionView.footerWidth = width
holder.captionView.footerHeight = height
}
override fun getViewStubId() = STUB_ID
class Holder : AbsMessageItem.Holder(STUB_ID) {
@ -205,6 +225,7 @@ abstract class MessageAudioItem : AbsMessageItem<MessageAudioItem.Holder>() {
val audioPlaybackTime by bind<TextView>(R.id.audioPlaybackTime)
val progressLayout by bind<ViewGroup>(R.id.messageFileUploadProgressLayout)
val fileSize by bind<TextView>(R.id.fileSize)
val captionView by bind<FooteredTextView>(R.id.messageCaptionView)
val audioPlaybackDuration by bind<TextView>(R.id.audioPlaybackDuration)
val audioSeekBar by bind<SeekBar>(R.id.audioSeekBar)
}

View file

@ -29,9 +29,12 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.ui.views.FooteredTextView
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.view.ScMessageBubbleWrapView
import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.themes.guessTextWidth
import kotlin.math.ceil
@ -43,6 +46,9 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
@EpoxyAttribute
var filename: String = ""
@EpoxyAttribute
var caption: String? = null
@EpoxyAttribute
var mxcUrl: String = ""
@ -74,6 +80,7 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
}
holder.filenameView.text = filename
holder.captionView.setTextOrHide(caption)
if (attributes.informationData.sendState.isSending()) {
holder.fileImageView.setImageResource(iconRes)
@ -124,6 +131,19 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
holder.mainLayout.setPadding(0, 0, 0, 0)
}
private fun hasCaption() = !caption.isNullOrBlank()
override fun allowFooterOverlay(holder: Holder, bubbleWrapView: ScMessageBubbleWrapView): Boolean = hasCaption()
override fun needsFooterReservation(): Boolean {
return hasCaption()
}
override fun reserveFooterSpace(holder: Holder, width: Int, height: Int) {
holder.captionView.footerWidth = width
holder.captionView.footerHeight = height
}
override fun getViewStubId() = STUB_ID
class Holder : AbsMessageItem.Holder(STUB_ID) {
@ -134,6 +154,7 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
val fileImageWrapper by bind<ViewGroup>(R.id.messageFileImageView)
val fileDownloadProgress by bind<ProgressBar>(R.id.messageFileProgressbar)
val filenameView by bind<TextView>(R.id.messageFilenameView)
val captionView by bind<FooteredTextView>(R.id.messageCaptionView)
}
companion object {

View file

@ -31,8 +31,10 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.core.glide.GlideApp
import im.vector.app.core.ui.views.FooteredTextView
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
@ -130,6 +132,8 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
holder.mediaContentView.onClick(attributes.itemClickListener)
holder.mediaContentView.setOnLongClickListener(attributes.itemLongClickListener)
holder.captionView.setTextOrHide(mediaData.caption)
holder.playContentView.visibility = if (animate) {
View.GONE
} else if (playable) {
@ -139,6 +143,8 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
}
}
private fun hasCaption() = !mediaData.caption.isNullOrBlank()
override fun unbind(holder: Holder) {
GlideApp.with(holder.view.context.applicationContext).clear(holder.imageView)
imageContentRenderer.clear(holder.imageView)
@ -151,6 +157,9 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
override fun getViewStubId() = STUB_ID
private fun shouldAllowFooterOverlay(footerMeasures: Array<Int>, imageWidth: Int, imageHeight: Int): Boolean {
if (hasCaption()) {
return true
}
val footerWidth = footerMeasures[0]
val footerHeight = footerMeasures[1]
// We need enough space in both directions to remain within the image bounds.
@ -159,6 +168,9 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
}
private fun shouldShowFooterBellow(footerMeasures: Array<Int>, imageWidth: Int, imageHeight: Int): Boolean {
if (hasCaption()) {
return false
}
// Only show footer bellow if the width is not the limiting factor (or it will get cut).
// Otherwise, we can not be sure in this place that we'll have enough space on the side
// Also, prefer footer on the side if possible (i.e. enough height available)
@ -168,6 +180,9 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
}
override fun allowFooterOverlay(holder: Holder, bubbleWrapView: ScMessageBubbleWrapView): Boolean {
if (hasCaption()) {
return true
}
val rememberedAllowFooterOverlay = forceAllowFooterOverlay
if (rememberedAllowFooterOverlay != null) {
lastAllowedFooterOverlay = rememberedAllowFooterOverlay
@ -187,15 +202,30 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
}
override fun allowFooterBelow(holder: Holder): Boolean {
if (hasCaption()) {
return true
}
val showBellow = showFooterBellow
lastShowFooterBellow = showBellow
return showBellow
}
override fun getScBubbleMargin(resources: Resources): Int {
if (hasCaption()) {
return super.getScBubbleMargin(resources)
}
return 0
}
override fun needsFooterReservation(): Boolean {
return hasCaption()
}
override fun reserveFooterSpace(holder: Holder, width: Int, height: Int) {
holder.captionView.footerWidth = width
holder.captionView.footerHeight = height
}
override fun applyScBubbleStyle(messageLayout: TimelineMessageLayout.ScBubble, holder: Holder) {
// Case: ImageContentRenderer.processSize only sees width=height=0 -> width of the ImageView not adapted to the actual content
// -> Align image within ImageView to same side as message bubbles
@ -208,7 +238,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
when {
// Don't show it for non-bubble layouts, don't show for Stickers, ...
// Also only supported for default corner radius
!(messageLayout.isRealBubble || messageLayout.isPseudoBubble) || mode != ImageContentRenderer.Mode.THUMBNAIL -> {
!(messageLayout.isRealBubble || messageLayout.isPseudoBubble) || hasCaption() || mode != ImageContentRenderer.Mode.THUMBNAIL -> {
holder.mediaContentView.background = null
}
attributes.informationData.sentByMe -> {
@ -223,6 +253,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
class Holder : AbsMessageItem.Holder(STUB_ID) {
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
val imageView by bind<ImageView>(R.id.messageThumbnailView)
val captionView by bind<FooteredTextView>(R.id.messageCaptionView)
val playContentView by bind<ImageView>(R.id.messageMediaPlayView)
val mediaContentView by bind<ViewGroup>(R.id.messageContentMedia)
}

View file

@ -31,6 +31,8 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageNoticeContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.isEdition
@ -209,6 +211,7 @@ class TimelineMessageLayoutFactory @Inject constructor(
private fun MessageContent?.isPseudoBubble(event: TimelineEvent): Boolean {
if (this == null) return false
if (event.root.isRedacted()) return false
if (this is MessageWithAttachmentContent && !getCaption().isNullOrBlank()) return false
return this.msgType in MSG_TYPES_WITH_PSEUDO_BUBBLE_LAYOUT
}

View file

@ -52,6 +52,7 @@ import kotlin.math.min
interface AttachmentData : Parcelable {
val eventId: String
val filename: String
val caption: String?
val mimeType: String?
val url: String?
val elementToDecrypt: ElementToDecrypt?
@ -73,6 +74,7 @@ class ImageContentRenderer @Inject constructor(
data class Data(
override val eventId: String,
override val filename: String,
override val caption: String?,
override val mimeType: String?,
override val url: String?,
override val elementToDecrypt: ElementToDecrypt?,

View file

@ -29,6 +29,8 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -60,7 +62,8 @@ class RoomEventsAttachmentProvider(
if (content is MessageImageContent) {
val data = ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
@ -87,7 +90,8 @@ class RoomEventsAttachmentProvider(
} else if (content is MessageStickerContent) {
val data = ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
@ -114,7 +118,8 @@ class RoomEventsAttachmentProvider(
} else if (content is MessageVideoContent) {
val thumbnailData = ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.videoInfo?.getThumbnailUrl(),
elementToDecrypt = content.videoInfo?.thumbnailFile?.toElementToDecrypt(),
@ -126,7 +131,8 @@ class RoomEventsAttachmentProvider(
)
val data = VideoContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),

View file

@ -49,6 +49,7 @@ class VideoContentRenderer @Inject constructor(
data class Data(
override val eventId: String,
override val filename: String,
override val caption: String?,
override val mimeType: String?,
override val url: String?,
override val elementToDecrypt: ElementToDecrypt?,

View file

@ -50,6 +50,8 @@ import im.vector.app.features.roomprofile.uploads.RoomUploadsViewState
import org.matrix.android.sdk.api.session.crypto.attachments.toElementToDecrypt
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import javax.inject.Inject
@ -132,7 +134,8 @@ class RoomUploadsMediaFragment :
is MessageImageContent -> {
ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
@ -145,7 +148,8 @@ class RoomUploadsMediaFragment :
is MessageVideoContent -> {
val thumbnailData = ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.videoInfo?.getThumbnailUrl(),
elementToDecrypt = content.videoInfo?.thumbnailFile?.toElementToDecrypt(),
@ -156,7 +160,8 @@ class RoomUploadsMediaFragment :
)
VideoContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
filename = content.getFileName(),
caption = content.getCaption(),
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),

View file

@ -30,6 +30,8 @@ import org.matrix.android.sdk.api.session.crypto.attachments.toElementToDecrypt
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.getCaption
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import org.matrix.android.sdk.api.session.room.uploads.UploadEvent
@ -108,7 +110,8 @@ class UploadsMediaController @Inject constructor(
return ImageContentRenderer.Data(
eventId = eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
url = messageContent.getFileUrl(),
mimeType = messageContent.mimeType,
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
@ -124,7 +127,8 @@ class UploadsMediaController @Inject constructor(
val thumbnailData = ImageContentRenderer.Data(
eventId = eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
mimeType = messageContent.mimeType,
url = messageContent.videoInfo?.getThumbnailUrl(),
elementToDecrypt = messageContent.videoInfo?.thumbnailFile?.toElementToDecrypt(),
@ -136,7 +140,8 @@ class UploadsMediaController @Inject constructor(
return VideoContentRenderer.Data(
eventId = eventId,
filename = messageContent.body,
filename = messageContent.getFileName(),
caption = messageContent.getCaption(),
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),

View file

@ -95,6 +95,17 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<im.vector.app.core.ui.views.FooteredTextView
android:id="@+id/messageCaptionView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:textColor="?vctr_content_primary"
android:layout_gravity="left"
tools:text="@sample/messages.json/data/message"
tools:ignore="RtlHardcoded" />
<include
android:id="@+id/messageFileUploadProgressLayout"
layout="@layout/media_upload_download_progress_layout"

View file

@ -48,6 +48,17 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<im.vector.app.core.ui.views.FooteredTextView
android:id="@+id/messageCaptionView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:textColor="?vctr_content_primary"
android:layout_gravity="left"
tools:text="@sample/messages.json/data/message"
tools:ignore="RtlHardcoded" />
<include
android:id="@+id/messageFileUploadProgressLayout"
layout="@layout/media_upload_download_progress_layout"

View file

@ -18,6 +18,21 @@
tools:layout_height="300dp"
tools:src="@tools:sample/backgrounds/scenic" />
<im.vector.app.core.ui.views.FooteredTextView
android:id="@+id/messageCaptionView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:textColor="?vctr_content_primary"
android:layout_gravity="left"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/messageThumbnailView"
app:layout_constraintHorizontal_bias="0"
tools:text="@sample/messages.json/data/message"
tools:ignore="RtlHardcoded" />
<ImageView
android:id="@+id/messageMediaPlayView"
android:layout_width="40dp"
@ -42,6 +57,6 @@
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/messageThumbnailView"
app:layout_constraintTop_toBottomOf="@id/messageTextView"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>