Render MSC2530 captions
Change-Id: I10f875121e90102a0518d9bd39d87b3daa68ef2e
This commit is contained in:
parent
3214c782bc
commit
58dd1dedc9
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,13 +238,13 @@ 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 -> {
|
||||
attributes.informationData.sentByMe -> {
|
||||
holder.mediaContentView.setBackgroundResource(messageLayout.bubbleAppearance.imageBorderOutgoing)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
holder.mediaContentView.setBackgroundResource(messageLayout.bubbleAppearance.imageBorderIncoming)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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?,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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?,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue