[WIP] Advanced both-side bubble layout

TODO:
- MessageFileItem minWidth might be calculatable
- No-bubble alignment got broken
This commit is contained in:
SpiritCroc 2020-05-25 17:55:43 +02:00
parent e56133348f
commit 96ca414d44
11 changed files with 491 additions and 103 deletions

View File

@ -397,8 +397,6 @@ class MessageItemFactory @Inject constructor(
.leftGuideline(avatarSizeProvider.leftGuideline)
.attributes(attributes)
.highlighted(highlight)
.outgoingMessage(informationData.sentByMe)
.incomingMessage(!informationData.sentByMe)
.movementMethod(createLinkMovementMethod(callback))
}

View File

@ -16,17 +16,30 @@
package im.vector.riotx.features.home.room.detail.timeline.item
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Paint
import android.graphics.Typeface
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.core.view.children
import com.airbnb.epoxy.EpoxyAttribute
import im.vector.riotx.R
import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.MessageColorProvider
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.themes.BubbleThemeUtils
import im.vector.riotx.features.themes.ThemeUtils
import kotlin.math.max
import kotlin.math.round
/**
* Base timeline item that adds an optional information bar with the sender avatar, name and time
@ -49,21 +62,37 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
override fun bind(holder: H) {
super.bind(holder)
if (attributes.informationData.showInformation) {
val contentInBubble = infoInBubbles(holder.memberNameView.context)
if (attributes.informationData.showInformation and (!contentInBubble || !attributes.informationData.sentByMe)) {
holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply {
height = attributes.avatarSize
width = attributes.avatarSize
}
holder.avatarImageView.visibility = View.VISIBLE
holder.avatarImageView.setOnClickListener(_avatarClickListener)
holder.memberNameView.visibility = View.VISIBLE
//holder.memberNameView.visibility = View.VISIBLE
holder.memberNameView.setOnClickListener(_memberNameClickListener)
holder.timeView.visibility = View.VISIBLE
holder.bubbleMemberNameView.setOnClickListener(_memberNameClickListener)
//holder.timeView.visibility = View.VISIBLE
holder.timeView.text = attributes.informationData.time
holder.bubbleTimeView.text = attributes.informationData.time
holder.memberNameView.text = attributes.informationData.memberName
holder.bubbleMemberNameView.text = attributes.informationData.memberName
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener)
holder.bubbleMemberNameView.setOnLongClickListener(attributes.itemLongClickListener)
if (contentInBubble) {
holder.memberNameView.visibility = View.GONE
holder.timeView.visibility = View.GONE
holder.bubbleMemberNameView.visibility = View.VISIBLE
holder.bubbleTimeView.visibility = View.VISIBLE
} else {
holder.memberNameView.visibility = View.VISIBLE
holder.timeView.visibility = View.VISIBLE
holder.bubbleMemberNameView.visibility = View.GONE
holder.bubbleTimeView.visibility = View.GONE
}
} else {
holder.avatarImageView.setOnClickListener(null)
holder.memberNameView.setOnClickListener(null)
@ -72,13 +101,41 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
holder.timeView.visibility = View.GONE
holder.avatarImageView.setOnLongClickListener(null)
holder.memberNameView.setOnLongClickListener(null)
if (attributes.informationData.showInformation /* && contentInBubble && attributes.informationData.sentByMe */) {
holder.bubbleTimeView.visibility = View.VISIBLE
holder.bubbleTimeView.text = attributes.informationData.time
holder.bubbleMemberNameView.setOnClickListener(_memberNameClickListener)
holder.bubbleMemberNameView.text = attributes.informationData.memberName
holder.bubbleMemberNameView.setOnLongClickListener(attributes.itemLongClickListener)
} else {
holder.bubbleTimeView.visibility = View.GONE
holder.bubbleMemberNameView.setOnClickListener(null)
holder.bubbleMemberNameView.visibility = View.GONE
holder.bubbleMemberNameView.setOnLongClickListener(null)
}
}
if (contentInBubble && attributes.informationData.showInformation) {
// Guess text width for name and time
val text = holder.bubbleMemberNameView.text.toString() + " " + holder.bubbleTimeView.text.toString()
val paint = Paint()
paint.textSize = max(holder.bubbleMemberNameView.textSize, holder.bubbleTimeView.textSize)
holder.viewStubContainer.minimumWidth = round(paint.measureText(text)).toInt()
} else {
holder.viewStubContainer.minimumWidth = 0
}
updateMessageBubble(holder)
}
abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) {
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
val timeView by bind<TextView>(R.id.messageTimeView)
val eventBaseView by bind<RelativeLayout>(R.id.eventBaseView)
val bubbleView by bind<View>(R.id.bubbleView)
val bubbleMemberNameView by bind<TextView>(R.id.bubbleMessageMemberNameView)
val bubbleTimeView by bind<TextView>(R.id.bubbleMessageTimeView)
val informationBottom by bind<LinearLayout>(R.id.informationBottom)
val viewStubContainer by bind<FrameLayout>(R.id.viewStubContainer)
}
/**
@ -97,4 +154,105 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
val emojiTypeFace: Typeface? = null
) : AbsBaseMessageItem.Attributes
override fun ignoreMessageGuideline(context: Context): Boolean {
return infoInBubbles(context) && attributes.informationData.sentByMe
}
open fun messageBubbleAllowed(): Boolean {
return false
}
fun infoInBubbles(context: Context): Boolean {
return messageBubbleAllowed() && BubbleThemeUtils.getBubbleStyle(context) == BubbleThemeUtils.BUBBLE_STYLE_BOTH
}
fun updateMessageBubble(holder: H) {
val bubbleStyle = if (messageBubbleAllowed()) BubbleThemeUtils.getBubbleStyle(holder.eventBaseView.context) else BubbleThemeUtils.BUBBLE_STYLE_NONE
val reverseBubble = attributes.informationData.sentByMe && bubbleStyle == BubbleThemeUtils.BUBBLE_STYLE_BOTH
//val bubbleView = holder.eventBaseView
val bubbleView = holder.bubbleView
when (bubbleStyle) {
BubbleThemeUtils.BUBBLE_STYLE_NONE -> {
bubbleView.background = null
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = 0
/*
(bubbleView.layoutParams as RelativeLayout.LayoutParams).marginStart = 0
(bubbleView.layoutParams as RelativeLayout.LayoutParams).topMargin = 0
(bubbleView.layoutParams as RelativeLayout.LayoutParams).bottomMargin = 0
*/
bubbleView.setPadding(0, 0, 0, 0)
}
BubbleThemeUtils.BUBBLE_STYLE_START, BubbleThemeUtils.BUBBLE_STYLE_BOTH -> {
bubbleView.setBackgroundResource(if (reverseBubble) R.drawable.msg_bubble_outgoing else R.drawable.msg_bubble_incoming)
var tintColor = ColorStateList(
arrayOf(intArrayOf(0)),
intArrayOf(ThemeUtils.getColor(bubbleView.context,
if (attributes.informationData.sentByMe) R.attr.sc_message_bg_outgoing else R.attr.sc_message_bg_incoming)
)
)
bubbleView.backgroundTintList = tintColor
val density = bubbleView.resources.displayMetrics.density
// TODO 96 = 2 * avatar size?
if (reverseBubble) {
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginStart = round(96 * density).toInt()
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = 0
} else {
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginStart = 0
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = round(96 * density).toInt()
}
/*
(bubbleView.layoutParams as RelativeLayout.LayoutParams).marginStart = round(20*density).toInt()
(bubbleView.layoutParams as RelativeLayout.LayoutParams).topMargin = round(8*density).toInt()
(bubbleView.layoutParams as RelativeLayout.LayoutParams).bottomMargin = round(8*density).toInt()
*/
// TODO padding?
if (reverseBubble) {
bubbleView.setPaddingRelative(
round(8 * density).toInt(),
round(8 * density).toInt(),
round(20 * density).toInt(),
round(8 * density).toInt()
)
} else {
bubbleView.setPaddingRelative(
round(20 * density).toInt(),
round(8 * density).toInt(),
round(8 * density).toInt(),
round(8 * density).toInt()
)
}
}
}
val defaultRtl = holder.eventBaseView.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL;
val shouldRtl = reverseBubble != defaultRtl
/*
holder.eventBaseView.layoutDirection = if (shouldRtl) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR
setRtl(shouldRtl)
*/
(holder.bubbleView.layoutParams as FrameLayout.LayoutParams).gravity = if (reverseBubble) Gravity.END else Gravity.START
//holder.informationBottom.layoutDirection = if (shouldRtl) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR
setFlatRtl(holder.informationBottom, if (shouldRtl) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR,
holder.eventBaseView.resources.configuration.layoutDirection, 2)
}
/*
open fun setRtl(rtl: Boolean) {
// TODO subclass overrides?
}
*/
fun setFlatRtl(layout: ViewGroup, direction: Int, childDirection: Int, depth: Int = 1) {
layout.layoutDirection = direction
for (child in layout.children) {
if (depth > 1 && child is ViewGroup) {
setFlatRtl(child, direction, childDirection, depth-1)
} else {
child.layoutDirection = childDirection
}
}
}
}

View File

@ -15,6 +15,7 @@
*/
package im.vector.riotx.features.home.room.detail.timeline.item
import android.content.Context
import android.view.View
import android.view.ViewStub
import android.widget.RelativeLayout
@ -47,7 +48,11 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
override fun bind(holder: H) {
super.bind(holder)
holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
this.marginStart = leftGuideline
if (ignoreMessageGuideline(holder.leftGuideline.context)) {
this.marginStart = 0
} else {
this.marginStart = leftGuideline
}
}
holder.checkableBackground.isChecked = highlighted
}
@ -72,4 +77,8 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
view.findViewById<ViewStub>(stubId).inflate()
}
}
open fun ignoreMessageGuideline(context: Context): Boolean {
return false
}
}

View File

@ -51,4 +51,8 @@ abstract class MessageBlockCodeItem : AbsMessageItem<MessageBlockCodeItem.Holder
companion object {
private const val STUB_ID = R.id.messageContentCodeBlockStub
}
override fun messageBubbleAllowed(): Boolean {
return true
}
}

View File

@ -55,6 +55,8 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
holder.fileImageView.setImageResource(iconRes)
holder.filenameView.setOnClickListener(clickListener)
holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG)
// TODO calculate minimum width
holder.viewStubContainer.minimumWidth = 2000
}
override fun unbind(holder: Holder) {
@ -64,6 +66,10 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
override fun getViewType() = STUB_ID
override fun messageBubbleAllowed(): Boolean {
return true
}
class Holder : AbsMessageItem.Holder(STUB_ID) {
val progressLayout by bind<ViewGroup>(R.id.messageFileUploadProgressLayout)
val fileLayout by bind<ViewGroup>(R.id.messageFileLayout)

View File

@ -70,6 +70,10 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
override fun getViewType() = STUB_ID
override fun messageBubbleAllowed(): Boolean {
return true
}
class Holder : AbsMessageItem.Holder(STUB_ID) {
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
val imageView by bind<ImageView>(R.id.messageThumbnailView)

View File

@ -16,12 +16,7 @@
package im.vector.riotx.features.home.room.detail.timeline.item
import android.content.res.ColorStateList
import android.text.method.MovementMethod
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.text.PrecomputedTextCompat
import androidx.core.widget.TextViewCompat
@ -29,12 +24,6 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.features.home.room.detail.timeline.tools.findPillsAndProcess
import im.vector.riotx.features.themes.BubbleThemeUtils
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_BOTH
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_NONE
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_START
import im.vector.riotx.features.themes.ThemeUtils
import kotlin.math.round
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
@ -47,10 +36,6 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
var useBigFont: Boolean = false
@EpoxyAttribute
var movementMethod: MovementMethod? = null
@EpoxyAttribute
var incomingMessage: Boolean = false
@EpoxyAttribute
var outgoingMessage: Boolean = false
override fun bind(holder: Holder) {
super.bind(holder)
@ -71,38 +56,6 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
TextViewCompat.getTextMetricsParams(holder.messageView),
null)
holder.messageView.setTextFuture(textFuture)
val bubbleStyle = if (incomingMessage || outgoingMessage) BubbleThemeUtils.getBubbleStyle(holder.messageView.context) else BUBBLE_STYLE_NONE
val reverseBubble = outgoingMessage && bubbleStyle == BUBBLE_STYLE_BOTH
when (bubbleStyle) {
BUBBLE_STYLE_NONE -> {
holder.messageView.background = null
holder.messageView.setPadding(0, 0, 0, 0)
}
BUBBLE_STYLE_START, BUBBLE_STYLE_BOTH -> {
holder.messageView.setBackgroundResource(if (reverseBubble) R.drawable.msg_bubble_outgoing else R.drawable.msg_bubble_incoming)
var tintColor = ColorStateList(
arrayOf(intArrayOf(0)),
intArrayOf(ThemeUtils.getColor(holder.messageView.context,
if (outgoingMessage) R.attr.sc_message_bg_outgoing else R.attr.sc_message_bg_incoming)
)
)
holder.messageView.backgroundTintList = tintColor
val density = holder.messageView.resources.displayMetrics.density
holder.messageView.setPaddingRelative(
round(20*density).toInt(),
round(8*density).toInt(),
round(8*density).toInt(),
round(8*density).toInt()
)
}
}
if (holder.messageView.layoutParams is FrameLayout.LayoutParams) {
//(holder.messageView.layoutParams as FrameLayout.LayoutParams).gravity =
// if (outgoingMessage && bubbleStyle == BUBBLE_STYLE_BOTH) Gravity.END else Gravity.START
val defaultReverse = holder.messageView.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL;
(holder.messageView.parent.parent as RelativeLayout).layoutDirection = if (reverseBubble != defaultReverse) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR
}
}
override fun getViewType() = STUB_ID
@ -114,4 +67,8 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
companion object {
private const val STUB_ID = R.id.messageContentTextStub
}
override fun messageBubbleAllowed(): Boolean {
return true
}
}

View File

@ -2,6 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/eventBaseView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addStatesFromChildren="true"
@ -80,78 +81,142 @@
android:id="@+id/messageE2EDecoration"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignTop="@id/viewStubContainer"
android:layout_alignTop="@id/bubbleWrapper"
android:layout_marginTop="7dp"
android:layout_alignEnd="@id/decorationSpace"
android:visibility="gone"
tools:src="@drawable/ic_shield_warning"
tools:visibility="visible" />
<!-- bubble wrapper for controlling gravity -->
<FrameLayout
android:id="@+id/viewStubContainer"
android:id="@+id/bubbleWrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/messageMemberNameView"
android:layout_toEndOf="@id/messageStartGuideline"
android:addStatesFromChildren="true">
android:layout_alignStart="@+id/messageMemberNameView">
<ViewStub
android:id="@+id/messageContentTextStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
<RelativeLayout
android:id="@+id/bubbleView"
android:layout_width="wrap_content"
android:inflatedId="@id/messageTextView"
android:layout="@layout/item_timeline_event_text_message_stub"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageContentCodeBlockStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_code_block_stub"
tools:visibility="visible" />
android:layout_marginStart="0dp"
android:layout_marginEnd="8dp"
android:layout_marginVertical="4dp"
tools:background="@drawable/msg_bubble_incoming">
<ViewStub
android:id="@+id/messageContentMediaStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:inflatedId="@+id/messageContentMedia"
android:layout="@layout/item_timeline_event_media_message_stub" />
<!--
<im.vector.riotx.core.platform.EllipsizingTextView
-->
<TextView
android:id="@+id/bubbleMessageMemberNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/bubbleMessageTimeView"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_primary"
android:textSize="15sp"
android:textStyle="bold"
tools:text="@sample/matrix.json/data/displayName" />
<ViewStub
android:id="@+id/messageContentFileStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_file_stub" />
<TextView
android:id="@+id/bubbleMessageTimeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/bubbleMessageMemberNameView"
android:layout_alignEnd="@id/viewStubContainer"
android:maxLines="1"
android:textColor="?riotx_text_secondary"
android:textSize="12sp"
tools:text="@tools:sample/date/hhmm" />
<ViewStub
android:id="@+id/messageContentRedactedStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="20dp"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_redacted_stub" />
<FrameLayout
android:id="@+id/viewStubContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/bubbleMessageTimeView"
android:layout_margin="0dp"
android:addStatesFromChildren="true">
<ViewStub
android:id="@+id/messagePollStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_poll_stub" />
<ViewStub
android:id="@+id/messageContentTextStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:inflatedId="@id/messageTextView"
android:layout="@layout/item_timeline_event_text_message_stub"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageOptionsStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_option_buttons_stub" />
<ViewStub
android:id="@+id/messageContentCodeBlockStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout="@layout/item_timeline_event_code_block_stub"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageContentMediaStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:inflatedId="@+id/messageContentMedia"
android:layout="@layout/item_timeline_event_media_message_stub" />
<ViewStub
android:id="@+id/messageContentFileStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_file_stub" />
<ViewStub
android:id="@+id/messageContentRedactedStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="20dp"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_redacted_stub" />
<ViewStub
android:id="@+id/messagePollStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_poll_stub" />
<ViewStub
android:id="@+id/messageOptionsStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_option_buttons_stub" />
</FrameLayout>
<!--
<TextView
android:id="@+id/bubbleMessageTimeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/viewStubContainer"
android:layout_alignEnd="@id/viewStubContainer"
android:layout_marginStart="8dp"
android:layout_marginEnd="0dp"
android:maxLines="1"
android:textColor="?riotx_text_secondary"
android:textSize="12sp"
tools:text="@tools:sample/date/hhmm" />
-->
</RelativeLayout>
</FrameLayout>
<LinearLayout
android:id="@+id/informationBottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/viewStubContainer"
android:layout_below="@+id/bubbleWrapper"
android:layout_toEndOf="@+id/messageStartGuideline"
android:addStatesFromChildren="true"
android:orientation="vertical">

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/eventBaseView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addStatesFromChildren="true"
android:background="?attr/selectableItemBackground">
<im.vector.riotx.core.platform.CheckableView
android:id="@+id/messageSelectedBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/informationBottom"
android:layout_alignParentTop="true"
android:background="?riotx_highlighted_message_background" />
<ImageView
android:id="@+id/messageAvatarImageView"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
tools:src="@tools:sample/avatars" />
<!-- spacer so rtl-hack works better with messageMemberNameView -->
<View
android:id="@+id/spacer_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/messageTimeView"
android:layout_toEndOf="@+id/messageMemberNameView" />
<im.vector.riotx.core.platform.EllipsizingTextView
android:id="@+id/messageMemberNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="4dp"
android:layout_marginRight="8dp"
android:layout_toEndOf="@+id/messageStartGuideline"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_primary"
android:textSize="15sp"
android:textStyle="bold"
tools:text="@sample/matrix.json/data/displayName" />
<TextView
android:id="@+id/messageTimeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/messageMemberNameView"
android:layout_alignParentEnd="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?riotx_text_secondary"
android:textSize="12sp"
tools:text="@tools:sample/date/hhmm" />
<View
android:id="@+id/messageStartGuideline"
android:layout_width="0dp"
android:layout_height="0dp"
tools:layout_marginStart="52dp" />
<Space
android:id="@+id/decorationSpace"
android:layout_width="4dp"
android:layout_height="8dp"
android:layout_toEndOf="@id/messageStartGuideline"
/>
<ImageView
android:id="@+id/messageE2EDecoration"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignTop="@id/viewStubContainer"
android:layout_marginTop="7dp"
android:layout_alignEnd="@id/decorationSpace"
android:visibility="gone"
tools:src="@drawable/ic_shield_warning"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/viewStubContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/messageMemberNameView"
android:layout_toEndOf="@id/messageStartGuideline"
android:addStatesFromChildren="true">
<ViewStub
android:id="@+id/messageContentTextStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:inflatedId="@id/messageTextView"
android:layout="@layout/item_timeline_event_text_message_stub"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageContentCodeBlockStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_code_block_stub"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageContentMediaStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:inflatedId="@+id/messageContentMedia"
android:layout="@layout/item_timeline_event_media_message_stub" />
<ViewStub
android:id="@+id/messageContentFileStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_file_stub" />
<ViewStub
android:id="@+id/messageContentRedactedStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="20dp"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_redacted_stub" />
<ViewStub
android:id="@+id/messagePollStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_poll_stub" />
<ViewStub
android:id="@+id/messageOptionsStub"
style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content"
android:layout_marginEnd="56dp"
android:layout="@layout/item_timeline_event_option_buttons_stub" />
</FrameLayout>
<LinearLayout
android:id="@+id/informationBottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/viewStubContainer"
android:layout_toEndOf="@+id/messageStartGuideline"
android:addStatesFromChildren="true"
android:orientation="vertical">
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/reactionsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="4dp"
app:dividerDrawable="@drawable/reaction_divider"
app:flexWrap="wrap"
app:showDivider="middle"
tools:background="#F0E0F0"
tools:layout_height="40dp">
<!-- ReactionButtons will be added here in the code -->
<!--im.vector.riotx.features.reactions.widget.ReactionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content" /-->
</com.google.android.flexbox.FlexboxLayout>
<im.vector.riotx.core.ui.views.ReadReceiptsView
android:id="@+id/readReceiptsView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp" />
</LinearLayout>
</RelativeLayout>

View File

@ -9,8 +9,6 @@
android:id="@+id/messageThumbnailView"
android:layout_width="375dp"
android:layout_height="0dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
@ -48,8 +46,6 @@
android:layout_width="0dp"
android:layout_height="46dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -323,12 +323,14 @@
<style name="TimelineContentStubBaseParams">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<!--
<item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginLeft">8dp</item>
<item name="android:layout_marginEnd">8dp</item>
<item name="android:layout_marginRight">8dp</item>
<item name="android:layout_marginBottom">4dp</item>
<item name="android:layout_marginTop">4dp</item>
-->
</style>
<style name="VectorLabel">