diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt index 7b80acbc8e..2fac9df665 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -106,8 +106,6 @@ abstract class AbsMessageItem : AbsBaseMessageItem holder.timeView.isVisible = false } - holder.additionalTopSpace.isVisible = attributes.informationData.messageLayout.addTopMargin - // Render send state indicator holder.sendStateImageView.render(attributes.informationData.sendStateDecoration) holder.eventSendingIndicator.isVisible = attributes.informationData.sendStateDecoration == SendStateDecoration.SENDING_MEDIA @@ -157,7 +155,6 @@ abstract class AbsMessageItem : AbsBaseMessageItem abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) { - val additionalTopSpace by bind(R.id.additionalTopSpace) val avatarImageView by bind(R.id.messageAvatarImageView) val memberNameView by bind(R.id.messageMemberNameView) val timeView by bind(R.id.messageTimeView) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayout.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayout.kt index 202d8b05da..c0e668e013 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayout.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayout.kt @@ -21,43 +21,44 @@ import im.vector.app.R import kotlinx.parcelize.Parcelize sealed interface TimelineMessageLayout : Parcelable { + val layoutRes: Int val showAvatar: Boolean val showDisplayName: Boolean - val addTopMargin: Boolean val showTimestamp: Boolean @Parcelize - data class Default(override val showAvatar: Boolean, - override val showDisplayName: Boolean, - override val showTimestamp: Boolean, - override val addTopMargin: Boolean = false, - // Keep defaultLayout generated on epoxy items - override val layoutRes: Int = 0) : TimelineMessageLayout + data class Default( + override val showAvatar: Boolean, + override val showDisplayName: Boolean, + override val showTimestamp: Boolean, + // Keep defaultLayout generated on epoxy items + override val layoutRes: Int = 0, + ) : TimelineMessageLayout @Parcelize data class Bubble( - override val showAvatar: Boolean, - override val showDisplayName: Boolean, - override val showTimestamp: Boolean = true, - override val addTopMargin: Boolean = false, - val isIncoming: Boolean, - val isPseudoBubble: Boolean, - val cornersRadius: CornersRadius, - val timestampAsOverlay: Boolean, - override val layoutRes: Int = if (isIncoming) { - R.layout.item_timeline_event_bubble_incoming_base - } else { - R.layout.item_timeline_event_bubble_outgoing_base - } + override val showAvatar: Boolean, + override val showDisplayName: Boolean, + override val showTimestamp: Boolean = true, + val addTopMargin: Boolean = false, + val isIncoming: Boolean, + val isPseudoBubble: Boolean, + val cornersRadius: CornersRadius, + val timestampAsOverlay: Boolean, + override val layoutRes: Int = if (isIncoming) { + R.layout.item_timeline_event_bubble_incoming_base + } else { + R.layout.item_timeline_event_bubble_outgoing_base + }, ) : TimelineMessageLayout { @Parcelize data class CornersRadius( - val topStartRadius: Float, - val topEndRadius: Float, - val bottomStartRadius: Float, - val bottomEndRadius: Float + val topStartRadius: Float, + val topEndRadius: Float, + val bottomStartRadius: Float, + val bottomEndRadius: Float, ) : Parcelable } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt index 93eae9a1d3..954aa0bf34 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt @@ -42,9 +42,11 @@ import im.vector.app.features.home.room.detail.timeline.style.shapeAppearanceMod import im.vector.app.features.themes.ThemeUtils import timber.log.Timber -class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0) : - RelativeLayout(context, attrs, defStyleAttr), TimelineMessageLayoutRenderer { +class MessageBubbleView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : RelativeLayout(context, attrs, defStyleAttr), TimelineMessageLayoutRenderer { private var isIncoming: Boolean = false @@ -87,22 +89,43 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri outlineProvider = ViewOutlineProvider.BACKGROUND clipToOutline = true background = RippleDrawable( - ContextCompat.getColorStateList(context, R.color.mtrl_btn_ripple_color) ?: ColorStateList.valueOf(Color.TRANSPARENT), - bubbleDrawable, - rippleMaskDrawable) + ContextCompat.getColorStateList(context, R.color.mtrl_btn_ripple_color) ?: ColorStateList.valueOf(Color.TRANSPARENT), + bubbleDrawable, + rippleMaskDrawable) } } override fun renderMessageLayout(messageLayout: TimelineMessageLayout) { - if (messageLayout !is TimelineMessageLayout.Bubble) { - Timber.v("Can't render messageLayout $messageLayout") - return + (messageLayout as? TimelineMessageLayout.Bubble) + ?.updateDrawables() + ?.setConstraintsAndColor() + ?.toggleMessageOverlay() + ?.setPadding() + ?.setMargins() + ?.setAdditionalTopSpace() + ?: Timber.v("Can't render messageLayout $messageLayout") + } + + private fun TimelineMessageLayout.Bubble.updateDrawables() = apply { + val shapeAppearanceModel = cornersRadius.shapeAppearanceModel() + bubbleDrawable.apply { + this.shapeAppearanceModel = shapeAppearanceModel + this.fillColor = if (isPseudoBubble) { + ColorStateList.valueOf(Color.TRANSPARENT) + } else { + val backgroundColorAttr = if (isIncoming) R.attr.vctr_message_bubble_inbound else R.attr.vctr_message_bubble_outbound + val backgroundColor = ThemeUtils.getColor(context, backgroundColorAttr) + ColorStateList.valueOf(backgroundColor) + } } - updateDrawables(messageLayout) + rippleMaskDrawable.shapeAppearanceModel = shapeAppearanceModel + } + + private fun TimelineMessageLayout.Bubble.setConstraintsAndColor() = apply { ConstraintSet().apply { clone(views.bubbleView) clear(R.id.viewStubContainer, ConstraintSet.END) - if (messageLayout.timestampAsOverlay) { + if (timestampAsOverlay) { val timeColor = ContextCompat.getColor(context, R.color.palette_white) views.messageTimeView.setTextColor(timeColor) connect(R.id.viewStubContainer, ConstraintSet.END, R.id.parent, ConstraintSet.END, 0) @@ -113,17 +136,26 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri } applyTo(views.bubbleView) } - if (messageLayout.timestampAsOverlay) { + } + + private fun TimelineMessageLayout.Bubble.toggleMessageOverlay() = apply { + if (timestampAsOverlay) { views.messageOverlayView.isVisible = true - (views.messageOverlayView.background as? GradientDrawable)?.cornerRadii = messageLayout.cornersRadius.toFloatArray() + (views.messageOverlayView.background as? GradientDrawable)?.cornerRadii = cornersRadius.toFloatArray() } else { views.messageOverlayView.isVisible = false } - if (messageLayout.isPseudoBubble && messageLayout.timestampAsOverlay) { + } + + private fun TimelineMessageLayout.Bubble.setPadding() = apply { + if (isPseudoBubble && timestampAsOverlay) { views.viewStubContainer.root.setPadding(0, 0, 0, 0) } else { views.viewStubContainer.root.setPadding(horizontalStubPadding, verticalStubPadding, horizontalStubPadding, verticalStubPadding) } + } + + private fun TimelineMessageLayout.Bubble.setMargins() = apply { if (isIncoming) { views.messageEndGuideline.updateLayoutParams { marginEnd = resources.getDimensionPixelSize(R.dimen.chat_bubble_margin_end) @@ -141,22 +173,11 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri } } + private fun TimelineMessageLayout.Bubble.setAdditionalTopSpace() = apply { + views.additionalTopSpace.isVisible = addTopMargin + } + private fun TimelineMessageLayout.Bubble.CornersRadius.toFloatArray(): FloatArray { return floatArrayOf(topStartRadius, topStartRadius, topEndRadius, topEndRadius, bottomEndRadius, bottomEndRadius, bottomStartRadius, bottomStartRadius) } - - private fun updateDrawables(messageLayout: TimelineMessageLayout.Bubble) { - val shapeAppearanceModel = messageLayout.cornersRadius.shapeAppearanceModel() - bubbleDrawable.apply { - this.shapeAppearanceModel = shapeAppearanceModel - this.fillColor = if (messageLayout.isPseudoBubble) { - ColorStateList.valueOf(Color.TRANSPARENT) - } else { - val backgroundColorAttr = if (isIncoming) R.attr.vctr_message_bubble_inbound else R.attr.vctr_message_bubble_outbound - val backgroundColor = ThemeUtils.getColor(context, backgroundColorAttr) - ColorStateList.valueOf(backgroundColor) - } - } - rippleMaskDrawable.shapeAppearanceModel = shapeAppearanceModel - } }