[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) .leftGuideline(avatarSizeProvider.leftGuideline)
.attributes(attributes) .attributes(attributes)
.highlighted(highlight) .highlighted(highlight)
.outgoingMessage(informationData.sentByMe)
.incomingMessage(!informationData.sentByMe)
.movementMethod(createLinkMovementMethod(callback)) .movementMethod(createLinkMovementMethod(callback))
} }

View File

@ -16,17 +16,30 @@
package im.vector.riotx.features.home.room.detail.timeline.item 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.graphics.Typeface
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.core.view.children
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer 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.MessageColorProvider
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController 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 * 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) { override fun bind(holder: H) {
super.bind(holder) 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 { holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply {
height = attributes.avatarSize height = attributes.avatarSize
width = attributes.avatarSize width = attributes.avatarSize
} }
holder.avatarImageView.visibility = View.VISIBLE holder.avatarImageView.visibility = View.VISIBLE
holder.avatarImageView.setOnClickListener(_avatarClickListener) holder.avatarImageView.setOnClickListener(_avatarClickListener)
holder.memberNameView.visibility = View.VISIBLE //holder.memberNameView.visibility = View.VISIBLE
holder.memberNameView.setOnClickListener(_memberNameClickListener) 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.timeView.text = attributes.informationData.time
holder.bubbleTimeView.text = attributes.informationData.time
holder.memberNameView.text = attributes.informationData.memberName holder.memberNameView.text = attributes.informationData.memberName
holder.bubbleMemberNameView.text = attributes.informationData.memberName
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView) attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener) holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
holder.memberNameView.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 { } else {
holder.avatarImageView.setOnClickListener(null) holder.avatarImageView.setOnClickListener(null)
holder.memberNameView.setOnClickListener(null) holder.memberNameView.setOnClickListener(null)
@ -72,13 +101,41 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
holder.timeView.visibility = View.GONE holder.timeView.visibility = View.GONE
holder.avatarImageView.setOnLongClickListener(null) holder.avatarImageView.setOnLongClickListener(null)
holder.memberNameView.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) { abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) {
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView) val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
val memberNameView by bind<TextView>(R.id.messageMemberNameView) val memberNameView by bind<TextView>(R.id.messageMemberNameView)
val timeView by bind<TextView>(R.id.messageTimeView) 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, override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
val emojiTypeFace: Typeface? = null val emojiTypeFace: Typeface? = null
) : AbsBaseMessageItem.Attributes ) : 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 package im.vector.riotx.features.home.room.detail.timeline.item
import android.content.Context
import android.view.View import android.view.View
import android.view.ViewStub import android.view.ViewStub
import android.widget.RelativeLayout import android.widget.RelativeLayout
@ -47,8 +48,12 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
override fun bind(holder: H) { override fun bind(holder: H) {
super.bind(holder) super.bind(holder)
holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> { holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
if (ignoreMessageGuideline(holder.leftGuideline.context)) {
this.marginStart = 0
} else {
this.marginStart = leftGuideline this.marginStart = leftGuideline
} }
}
holder.checkableBackground.isChecked = highlighted holder.checkableBackground.isChecked = highlighted
} }
@ -72,4 +77,8 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
view.findViewById<ViewStub>(stubId).inflate() 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 { companion object {
private const val STUB_ID = R.id.messageContentCodeBlockStub 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.fileImageView.setImageResource(iconRes)
holder.filenameView.setOnClickListener(clickListener) holder.filenameView.setOnClickListener(clickListener)
holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG) holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG)
// TODO calculate minimum width
holder.viewStubContainer.minimumWidth = 2000
} }
override fun unbind(holder: Holder) { override fun unbind(holder: Holder) {
@ -64,6 +66,10 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
override fun getViewType() = STUB_ID override fun getViewType() = STUB_ID
override fun messageBubbleAllowed(): Boolean {
return true
}
class Holder : AbsMessageItem.Holder(STUB_ID) { class Holder : AbsMessageItem.Holder(STUB_ID) {
val progressLayout by bind<ViewGroup>(R.id.messageFileUploadProgressLayout) val progressLayout by bind<ViewGroup>(R.id.messageFileUploadProgressLayout)
val fileLayout by bind<ViewGroup>(R.id.messageFileLayout) 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 getViewType() = STUB_ID
override fun messageBubbleAllowed(): Boolean {
return true
}
class Holder : AbsMessageItem.Holder(STUB_ID) { class Holder : AbsMessageItem.Holder(STUB_ID) {
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout) val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
val imageView by bind<ImageView>(R.id.messageThumbnailView) val imageView by bind<ImageView>(R.id.messageThumbnailView)

View File

@ -16,12 +16,7 @@
package im.vector.riotx.features.home.room.detail.timeline.item package im.vector.riotx.features.home.room.detail.timeline.item
import android.content.res.ColorStateList
import android.text.method.MovementMethod 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.appcompat.widget.AppCompatTextView
import androidx.core.text.PrecomputedTextCompat import androidx.core.text.PrecomputedTextCompat
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
@ -29,12 +24,6 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.features.home.room.detail.timeline.tools.findPillsAndProcess 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) @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() { abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
@ -47,10 +36,6 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
var useBigFont: Boolean = false var useBigFont: Boolean = false
@EpoxyAttribute @EpoxyAttribute
var movementMethod: MovementMethod? = null var movementMethod: MovementMethod? = null
@EpoxyAttribute
var incomingMessage: Boolean = false
@EpoxyAttribute
var outgoingMessage: Boolean = false
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
@ -71,38 +56,6 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
TextViewCompat.getTextMetricsParams(holder.messageView), TextViewCompat.getTextMetricsParams(holder.messageView),
null) null)
holder.messageView.setTextFuture(textFuture) 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 override fun getViewType() = STUB_ID
@ -114,4 +67,8 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
companion object { companion object {
private const val STUB_ID = R.id.messageContentTextStub 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" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/eventBaseView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:addStatesFromChildren="true" android:addStatesFromChildren="true"
@ -80,19 +81,64 @@
android:id="@+id/messageE2EDecoration" android:id="@+id/messageE2EDecoration"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_alignTop="@id/viewStubContainer" android:layout_alignTop="@id/bubbleWrapper"
android:layout_marginTop="7dp" android:layout_marginTop="7dp"
android:layout_alignEnd="@id/decorationSpace" android:layout_alignEnd="@id/decorationSpace"
android:visibility="gone" android:visibility="gone"
tools:src="@drawable/ic_shield_warning" tools:src="@drawable/ic_shield_warning"
tools:visibility="visible" /> tools:visibility="visible" />
<!-- bubble wrapper for controlling gravity -->
<FrameLayout <FrameLayout
android:id="@+id/viewStubContainer" android:id="@+id/bubbleWrapper"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/messageMemberNameView" android:layout_below="@id/messageMemberNameView"
android:layout_toEndOf="@id/messageStartGuideline" android:layout_toEndOf="@id/messageStartGuideline"
android:layout_alignStart="@+id/messageMemberNameView">
<RelativeLayout
android:id="@+id/bubbleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="8dp"
android:layout_marginVertical="4dp"
tools:background="@drawable/msg_bubble_incoming">
<!--
<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" />
<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" />
<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"> android:addStatesFromChildren="true">
<ViewStub <ViewStub
@ -108,6 +154,7 @@
android:id="@+id/messageContentCodeBlockStub" android:id="@+id/messageContentCodeBlockStub"
style="@style/TimelineContentStubBaseParams" style="@style/TimelineContentStubBaseParams"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout="@layout/item_timeline_event_code_block_stub" android:layout="@layout/item_timeline_event_code_block_stub"
tools:visibility="visible" /> tools:visibility="visible" />
@ -147,11 +194,29 @@
</FrameLayout> </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 <LinearLayout
android:id="@+id/informationBottom" android:id="@+id/informationBottom"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/viewStubContainer" android:layout_below="@+id/bubbleWrapper"
android:layout_toEndOf="@+id/messageStartGuideline" android:layout_toEndOf="@+id/messageStartGuideline"
android:addStatesFromChildren="true" android:addStatesFromChildren="true"
android:orientation="vertical"> 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:id="@+id/messageThumbnailView"
android:layout_width="375dp" android:layout_width="375dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -48,8 +46,6 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="46dp" android:layout_height="46dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

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