mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-15 02:38:59 +03:00
[bubble merge] Revert Schildi bubbles, to be re-implemented
Change-Id: I86c8c1b7f5a0fad76d7fbec26eceabecacdf202c
This commit is contained in:
parent
ecd28447c9
commit
af202d22be
14 changed files with 303 additions and 985 deletions
|
@ -7,6 +7,7 @@ import im.vector.app.features.themes.BubbleThemeUtils
|
|||
|
||||
interface BubbleDependentView<H> {
|
||||
|
||||
/*
|
||||
fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
@ -20,8 +21,10 @@ interface BubbleDependentView<H> {
|
|||
}
|
||||
|
||||
fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean)
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
// This function belongs to BubbleDependentView, but turned out to raise a NoSuchMethodError since recently
|
||||
// when called from an onImageSizeUpdated listener
|
||||
fun <H>updateMessageBubble(context: Context, view: BubbleDependentView<H>, holder: H) {
|
||||
|
@ -55,3 +58,4 @@ fun setFlatRtl(layout: ViewGroup, direction: Int, childDirection: Int, depth: In
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -31,10 +31,7 @@ import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
|||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.view.TimelineMessageLayoutRenderer
|
||||
import im.vector.app.features.reactions.widget.ReactionButton
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* Base timeline item with reactions and read receipts.
|
||||
|
@ -119,60 +116,6 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
|
|||
failureIndicator?.isVisible = baseAttributes.informationData.sendState.hasFailed()
|
||||
}
|
||||
|
||||
override fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
super.setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
|
||||
|
||||
// ATTENTION: we go over the bubbleStyleSetting here: this might differ from the effective bubbleStyle
|
||||
// for this view class! We want to use the setting to do some uniform alignments for all views though.
|
||||
when (bubbleStyleSetting) {
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_BOTH,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN -> {
|
||||
// Padding for views that align with the bubble (should be roughly the bubble tail width)
|
||||
val bubbleStartAlignWidth = holder.informationBottom.resources.getDimensionPixelSize(R.dimen.sc_bubble_tail_size)
|
||||
if (reverseBubble) {
|
||||
// Align reactions container to bubble
|
||||
holder.informationBottom.setPaddingRelative(
|
||||
0,
|
||||
0,
|
||||
bubbleStartAlignWidth,
|
||||
0
|
||||
)
|
||||
} else {
|
||||
// Align reactions container to bubble
|
||||
holder.informationBottom.setPaddingRelative(
|
||||
bubbleStartAlignWidth,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// No alignment padding for reactions required
|
||||
holder.informationBottom.setPaddingRelative(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (BubbleThemeUtils.drawsDualSide(bubbleStyleSetting) /*&& baseAttributes.informationData.sentByMe*/) {
|
||||
// Haven't found a good location for this yet for outgoing messages
|
||||
holder.e2EDecorationView.render(null)
|
||||
} else {
|
||||
// Moved here from upstream's bind()
|
||||
when (baseAttributes.informationData.e2eDecoration) {
|
||||
E2EDecoration.NONE -> {
|
||||
holder.e2EDecorationView.render(null)
|
||||
}
|
||||
E2EDecoration.WARN_IN_CLEAR,
|
||||
E2EDecoration.WARN_SENT_BY_UNVERIFIED,
|
||||
E2EDecoration.WARN_SENT_BY_UNKNOWN -> {
|
||||
holder.e2EDecorationView.render(RoomEncryptionTrustLevel.Warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Holder(@IdRes stubId: Int) : BaseEventItem.BaseHolder(stubId) {
|
||||
val reactionsContainer by bind<ViewGroup>(R.id.reactionsContainer)
|
||||
val informationBottom by bind<ViewGroup>(R.id.informationBottom)
|
||||
|
|
|
@ -16,17 +16,10 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
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.ProgressBar
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
|
@ -35,20 +28,14 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import kotlin.math.max
|
||||
import kotlin.math.round
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.ui.views.SendStateImageView
|
||||
import im.vector.app.core.ui.views.setFlatRtl
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import kotlin.math.ceil
|
||||
import org.matrix.android.sdk.api.session.threads.ThreadDetails
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
|
@ -88,147 +75,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
|||
|
||||
override fun bind(holder: H) {
|
||||
super.bind(holder)
|
||||
if (true /* SC-TODO is SC layout */) {
|
||||
val contentInBubble = infoInBubbles(holder.memberNameView.context)
|
||||
val senderInBubble = senderNameInBubble(holder.memberNameView.context)
|
||||
|
||||
val avatarImageView: ImageView?
|
||||
var memberNameView: TextView?
|
||||
var timeView: TextView?
|
||||
val hiddenViews = ArrayList<View>()
|
||||
val invisibleViews = ArrayList<View>()
|
||||
|
||||
val canHideAvatar = canHideAvatars()
|
||||
val canHideSender = canHideSender()
|
||||
|
||||
// Select which views are visible, based on bubble style and other criteria
|
||||
if (attributes.informationData.messageLayout.showDisplayName) {
|
||||
if (senderInBubble) {
|
||||
memberNameView = holder.bubbleMemberNameView
|
||||
hiddenViews.add(holder.memberNameView)
|
||||
} else {
|
||||
memberNameView = holder.memberNameView
|
||||
hiddenViews.add(holder.bubbleMemberNameView)
|
||||
}
|
||||
if (contentInBubble) {
|
||||
timeView = holder.bubbleTimeView
|
||||
hiddenViews.add(holder.timeView)
|
||||
} else {
|
||||
timeView = holder.timeView
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
}
|
||||
} else if (attributes.informationData.messageLayout.showTimestamp) {
|
||||
memberNameView = null
|
||||
//hiddenViews.add(holder.memberNameView) // this one get's some special hiding treatment below
|
||||
hiddenViews.add(holder.bubbleMemberNameView)
|
||||
if (contentInBubble) {
|
||||
timeView = holder.bubbleTimeView
|
||||
hiddenViews.add(holder.timeView)
|
||||
|
||||
hiddenViews.add(holder.memberNameView)
|
||||
} else {
|
||||
timeView = holder.timeView
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
|
||||
// Set to INVISIBLE instead of adding to hiddenViews, which are set to GONE
|
||||
// (upstream sets memberNameView.isInvisible = true here, which is effectively the same)
|
||||
invisibleViews.add(holder.memberNameView)
|
||||
}
|
||||
} else {
|
||||
memberNameView = null
|
||||
hiddenViews.add(holder.memberNameView)
|
||||
hiddenViews.add(holder.bubbleMemberNameView)
|
||||
timeView = null
|
||||
hiddenViews.add(holder.timeView)
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
}
|
||||
|
||||
if (timeView === holder.bubbleTimeView) {
|
||||
// We have two possible bubble time view locations
|
||||
// For code readability, we don't inline this setting in the above cases
|
||||
if (BubbleThemeUtils.getBubbleTimeLocation(holder.bubbleTimeView.context) == BubbleThemeUtils.BUBBLE_TIME_BOTTOM) {
|
||||
timeView = holder.bubbleFooterTimeView
|
||||
if (attributes.informationData.messageLayout.showDisplayName) {
|
||||
if (canHideSender) {
|
||||
// In the case of footer time, we can also hide the names without making it look awkward
|
||||
if (memberNameView != null) {
|
||||
hiddenViews.add(memberNameView)
|
||||
memberNameView = null
|
||||
}
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
} else if (!senderInBubble) {
|
||||
// We don't need to reserve space here
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
} else {
|
||||
// Don't completely remove, just hide, so our relative layout rules still work
|
||||
invisibleViews.add(holder.bubbleTimeView)
|
||||
}
|
||||
} else {
|
||||
// Do hide, or we accidentally reserve space
|
||||
hiddenViews.add(holder.bubbleTimeView)
|
||||
}
|
||||
} else {
|
||||
hiddenViews.add(holder.bubbleFooterTimeView)
|
||||
}
|
||||
}
|
||||
|
||||
// Dual-side bubbles: hide own avatar, and all in direct chats
|
||||
if ((!attributes.informationData.messageLayout.showAvatar) ||
|
||||
(contentInBubble && canHideAvatar)) {
|
||||
avatarImageView = null
|
||||
hiddenViews.add(holder.avatarImageView)
|
||||
} else {
|
||||
avatarImageView = holder.avatarImageView
|
||||
}
|
||||
|
||||
// Views available in upstream Element
|
||||
avatarImageView?.layoutParams = avatarImageView?.layoutParams?.apply {
|
||||
height = attributes.avatarSize
|
||||
width = attributes.avatarSize
|
||||
}
|
||||
avatarImageView?.visibility = View.VISIBLE
|
||||
avatarImageView?.onClick(_avatarClickListener)
|
||||
memberNameView?.visibility = View.VISIBLE
|
||||
memberNameView?.onClick(_memberNameClickListener)
|
||||
timeView?.visibility = View.VISIBLE
|
||||
timeView?.text = attributes.informationData.time
|
||||
memberNameView?.text = attributes.informationData.memberName
|
||||
memberNameView?.setTextColor(attributes.getMemberNameColor())
|
||||
if (avatarImageView != null) attributes.avatarRenderer.render(attributes.informationData.matrixItem, avatarImageView)
|
||||
avatarImageView?.setOnLongClickListener(attributes.itemLongClickListener)
|
||||
memberNameView?.setOnLongClickListener(attributes.itemLongClickListener)
|
||||
|
||||
// More extra views added by Schildi
|
||||
if (senderInBubble) {
|
||||
holder.viewStubContainer.minimumWidth = getViewStubMinimumWidth(holder, contentInBubble, attributes.informationData.messageLayout.showDisplayName)
|
||||
} else {
|
||||
holder.viewStubContainer.minimumWidth = 0
|
||||
}
|
||||
if (contentInBubble) {
|
||||
holder.bubbleFootView.visibility = View.VISIBLE
|
||||
} else {
|
||||
hiddenViews.add(holder.bubbleFootView)
|
||||
}
|
||||
|
||||
// Actually hide all unnecessary views
|
||||
hiddenViews.forEach {
|
||||
// Same as it.isVisible = false
|
||||
it.visibility = View.GONE
|
||||
}
|
||||
invisibleViews.forEach {
|
||||
// Same as it.isInvisible = true
|
||||
it.visibility = View.INVISIBLE
|
||||
}
|
||||
// Render send state indicator
|
||||
if (contentInBubble) {
|
||||
// Bubbles have their own decoration in the anonymous read receipt (in the message footer)
|
||||
holder.sendStateImageView.isVisible = false
|
||||
holder.eventSendingIndicator.isVisible = false
|
||||
} else {
|
||||
holder.sendStateImageView.render(attributes.informationData.sendStateDecoration)
|
||||
holder.eventSendingIndicator.isVisible = attributes.informationData.sendStateDecoration == SendStateDecoration.SENDING_MEDIA
|
||||
}
|
||||
} else { // SC-TODO non-SC bubbles
|
||||
if (attributes.informationData.messageLayout.showAvatar) {
|
||||
holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply {
|
||||
height = attributes.avatarSize
|
||||
|
@ -263,7 +109,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
|||
// Render send state indicator
|
||||
holder.sendStateImageView.render(attributes.informationData.sendStateDecoration)
|
||||
holder.eventSendingIndicator.isVisible = attributes.informationData.sendStateDecoration == SendStateDecoration.SENDING_MEDIA
|
||||
} // SC- TODO
|
||||
|
||||
// Threads
|
||||
if (attributes.areThreadMessagesEnabled) {
|
||||
|
@ -320,14 +165,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
|||
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<ViewGroup>(R.id.bubbleView)
|
||||
val bubbleMemberNameView by bind<TextView>(R.id.bubbleMessageMemberNameView)
|
||||
val bubbleTimeView by bind<TextView>(R.id.bubbleMessageTimeView)
|
||||
val bubbleFootView by bind<LinearLayout>(R.id.bubbleFootView)
|
||||
val bubbleFooterTimeView by bind<TextView>(R.id.bubbleFooterMessageTimeView)
|
||||
val bubbleFooterReadReceipt by bind<ImageView>(R.id.bubbleFooterReadReceipt)
|
||||
val viewStubContainer by bind<FrameLayout>(R.id.viewStubContainer)
|
||||
val sendStateImageView by bind<SendStateImageView>(R.id.messageSendStateImageView)
|
||||
val eventSendingIndicator by bind<ProgressBar>(R.id.eventSendingIndicator)
|
||||
val threadSummaryConstraintLayout by bind<ConstraintLayout>(R.id.messageThreadSummaryConstraintLayout)
|
||||
|
@ -382,380 +219,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
|||
}
|
||||
|
||||
override fun ignoreMessageGuideline(context: Context): Boolean {
|
||||
return infoInBubbles(context) && canHideAvatars()
|
||||
return false // SC-TODO infoInBubbles(context) && canHideAvatars()
|
||||
}
|
||||
|
||||
open fun getViewStubMinimumWidth(holder: H, contentInBubble: Boolean, showInformation: Boolean): Int {
|
||||
val memberName = attributes.informationData.memberName.toString()
|
||||
val time = attributes.informationData.time.toString()
|
||||
return if (contentInBubble) {
|
||||
if (BubbleThemeUtils.getBubbleTimeLocation(holder.bubbleTimeView.context) == BubbleThemeUtils.BUBBLE_TIME_BOTTOM) {
|
||||
if (attributes.informationData.messageLayout.showDisplayName && !canHideSender()) {
|
||||
// Since timeView automatically gets enough space, either within or outside the viewStub, we just need to ensure the member name view has enough space
|
||||
// Somehow not enough without extra space...
|
||||
ceil(BubbleThemeUtils.guessTextWidth(holder.bubbleMemberNameView, "$memberName ")).toInt()
|
||||
} else {
|
||||
// wrap_content works!
|
||||
0
|
||||
}
|
||||
} else if (attributes.informationData.messageLayout.showDisplayName || attributes.informationData.messageLayout.showTimestamp) {
|
||||
// Guess text width for name and time next to each other
|
||||
val text = if (attributes.informationData.messageLayout.showDisplayName) {
|
||||
"$memberName $time"
|
||||
} else {
|
||||
time
|
||||
}
|
||||
val textSize = if (attributes.informationData.messageLayout.showDisplayName) {
|
||||
max(holder.bubbleMemberNameView.textSize, holder.bubbleTimeView.textSize)
|
||||
} else {
|
||||
holder.bubbleTimeView.textSize
|
||||
}
|
||||
ceil(BubbleThemeUtils.guessTextWidth(textSize, text)).toInt()
|
||||
} else {
|
||||
// Not showing any header, use wrap_content of content only
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
private fun infoInBubbles(context: Context): Boolean {
|
||||
return BubbleThemeUtils.getBubbleStyle(context) == BubbleThemeUtils.BUBBLE_STYLE_BOTH &&
|
||||
(messageBubbleAllowed(context) || pseudoBubbleAllowed())
|
||||
}
|
||||
|
||||
private fun senderNameInBubble(context: Context): Boolean {
|
||||
return infoInBubbles(context) && !pseudoBubbleAllowed()
|
||||
}
|
||||
|
||||
override fun shouldReverseBubble(): Boolean {
|
||||
return attributes.informationData.sentByMe
|
||||
}
|
||||
|
||||
open fun getBubbleMargin(resources: Resources, bubbleStyle: String, reverseBubble: Boolean): Int {
|
||||
return when (bubbleStyle) {
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN -> 0
|
||||
// else: dual-side bubbles (getBubbleMargin should not get called for other bubbleStyles)
|
||||
else -> {
|
||||
when {
|
||||
// Direct chats usually have avatars hidden on both sides
|
||||
attributes.informationData.isDirect -> resources.getDimensionPixelSize(R.dimen.dual_bubble_both_sides_without_avatar_margin)
|
||||
// No direct chat, but sent by me: other side has an avatar
|
||||
attributes.informationData.sentByMe -> {
|
||||
resources.getDimensionPixelSize(R.dimen.dual_bubble_one_side_without_avatar_margin) +
|
||||
resources.getDimensionPixelSize(R.dimen.dual_bubble_one_side_avatar_offset) +
|
||||
attributes.avatarSize
|
||||
}
|
||||
// No direct chat, sent by other: my side has hidden avatar
|
||||
else -> resources.getDimensionPixelSize(R.dimen.dual_bubble_one_side_without_avatar_margin)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the footer in front of the viewStub
|
||||
*/
|
||||
open fun allowFooterOverlay(holder: H): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the footer aligned below the viewStub - requires enough width!
|
||||
*/
|
||||
open fun allowFooterBelow(holder: H): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
open fun needsFooterReservation(holder: H): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
open fun reserveFooterSpace(holder: H, width: Int, height: Int) {
|
||||
}
|
||||
|
||||
private fun canHideAvatars(): Boolean {
|
||||
return attributes.informationData.sentByMe || attributes.informationData.isDirect
|
||||
}
|
||||
|
||||
private fun canHideSender(): Boolean {
|
||||
return attributes.informationData.sentByMe ||
|
||||
(attributes.informationData.isDirect && attributes.informationData.senderId == attributes.informationData.dmChatPartnerId)
|
||||
}
|
||||
|
||||
protected fun getFooterMeasures(holder: H): Array<Int> {
|
||||
val anonymousReadReceipt = BubbleThemeUtils.getVisibleAnonymousReadReceipts(holder.bubbleFootView.context,
|
||||
attributes.informationData.readReceiptAnonymous, attributes.informationData.sentByMe)
|
||||
return getFooterMeasures(holder, anonymousReadReceipt)
|
||||
}
|
||||
|
||||
private fun getFooterMeasures(holder: H, anonymousReadReceipt: AnonymousReadReceipt): Array<Int> {
|
||||
val timeWidth: Int
|
||||
val timeHeight: Int
|
||||
if (BubbleThemeUtils.getBubbleTimeLocation(holder.bubbleTimeView.context) == BubbleThemeUtils.BUBBLE_TIME_BOTTOM) {
|
||||
// Guess text width for name and time
|
||||
timeWidth = ceil(BubbleThemeUtils.guessTextWidth(holder.bubbleFooterTimeView, attributes.informationData.time.toString())).toInt() + holder.bubbleFooterTimeView.paddingLeft + holder.bubbleFooterTimeView.paddingRight
|
||||
timeHeight = ceil(holder.bubbleFooterTimeView.textSize).toInt() + holder.bubbleFooterTimeView.paddingTop + holder.bubbleFooterTimeView.paddingBottom
|
||||
} else {
|
||||
timeWidth = 0
|
||||
timeHeight = 0
|
||||
}
|
||||
val readReceiptWidth: Int
|
||||
val readReceiptHeight: Int
|
||||
if (anonymousReadReceipt == AnonymousReadReceipt.NONE) {
|
||||
readReceiptWidth = 0
|
||||
readReceiptHeight = 0
|
||||
} else {
|
||||
readReceiptWidth = holder.bubbleFooterReadReceipt.maxWidth + holder.bubbleFooterReadReceipt.paddingLeft + holder.bubbleFooterReadReceipt.paddingRight
|
||||
readReceiptHeight = holder.bubbleFooterReadReceipt.maxHeight + holder.bubbleFooterReadReceipt.paddingTop + holder.bubbleFooterReadReceipt.paddingBottom
|
||||
}
|
||||
|
||||
var footerWidth = timeWidth + readReceiptWidth
|
||||
var footerHeight = max(timeHeight, readReceiptHeight)
|
||||
// Reserve extra padding, if we do have actual content
|
||||
if (footerWidth > 0) {
|
||||
footerWidth += holder.bubbleFootView.paddingLeft + holder.bubbleFootView.paddingRight
|
||||
}
|
||||
if (footerHeight > 0) {
|
||||
footerHeight += holder.bubbleFootView.paddingTop + holder.bubbleFootView.paddingBottom
|
||||
}
|
||||
return arrayOf(footerWidth, footerHeight)
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
override fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
super.setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
|
||||
|
||||
//val bubbleView = holder.eventBaseView
|
||||
val bubbleView = holder.bubbleView
|
||||
val contentInBubble = infoInBubbles(holder.memberNameView.context)
|
||||
|
||||
val defaultDirection = holder.eventBaseView.resources.configuration.layoutDirection;
|
||||
val defaultRtl = defaultDirection == View.LAYOUT_DIRECTION_RTL
|
||||
val reverseDirection = if (defaultRtl) View.LAYOUT_DIRECTION_LTR else View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
// Notice formatting - also relevant if no actual bubbles are shown
|
||||
bubbleView.alpha = if (attributes.isNotice) 0.65f else 1f
|
||||
|
||||
when (bubbleStyle) {
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_BOTH,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN,
|
||||
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN -> {
|
||||
// Padding for bubble content: long for side with tail, short for other sides
|
||||
val longPaddingDp: Int
|
||||
val shortPaddingDp: Int
|
||||
if (BubbleThemeUtils.drawsActualBubbles(bubbleStyle)) {
|
||||
val bubbleRes = if (attributes.informationData.messageLayout.showAvatar) { // tail
|
||||
if (reverseBubble) { // outgoing
|
||||
R.drawable.msg_bubble_text_outgoing
|
||||
} else { // incoming
|
||||
R.drawable.msg_bubble_text_incoming
|
||||
}
|
||||
} else { // notail
|
||||
if (reverseBubble) { // outgoing
|
||||
R.drawable.msg_bubble_text_outgoing_notail
|
||||
} else { // incoming
|
||||
R.drawable.msg_bubble_text_incoming_notail
|
||||
}
|
||||
}
|
||||
bubbleView.setBackgroundResource(bubbleRes)
|
||||
longPaddingDp = bubbleView.resources.getDimensionPixelSize(R.dimen.sc_bubble_inner_padding_long_side)
|
||||
shortPaddingDp = bubbleView.resources.getDimensionPixelSize(R.dimen.sc_bubble_inner_padding_short_side)
|
||||
} else {
|
||||
longPaddingDp = bubbleView.resources.getDimensionPixelSize(R.dimen.sc_bubble_tail_size)
|
||||
shortPaddingDp = 0//if (attributes.informationData.showInformation && !hideSenderInformation()) { 8 } else { 0 }
|
||||
}
|
||||
if (reverseBubble != defaultRtl) {
|
||||
// Use left/right instead of start/end: bubbleView is always LTR
|
||||
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).leftMargin = getBubbleMargin(bubbleView.resources, bubbleStyle, reverseBubble)
|
||||
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).rightMargin = 0
|
||||
} else {
|
||||
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).leftMargin = 0
|
||||
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).rightMargin = getBubbleMargin(bubbleView.resources, bubbleStyle, reverseBubble)
|
||||
}
|
||||
if (reverseBubble != defaultRtl) {
|
||||
// Use left/right instead of start/end: bubbleView is always LTR
|
||||
bubbleView.setPadding(
|
||||
shortPaddingDp,
|
||||
shortPaddingDp,
|
||||
longPaddingDp,
|
||||
shortPaddingDp
|
||||
)
|
||||
} else {
|
||||
bubbleView.setPadding(
|
||||
longPaddingDp,
|
||||
shortPaddingDp,
|
||||
shortPaddingDp,
|
||||
shortPaddingDp
|
||||
)
|
||||
}
|
||||
|
||||
if (contentInBubble) {
|
||||
val anonymousReadReceipt = BubbleThemeUtils.getVisibleAnonymousReadReceipts(holder.bubbleFootView.context,
|
||||
attributes.informationData.readReceiptAnonymous, attributes.informationData.sentByMe)
|
||||
|
||||
when (anonymousReadReceipt) {
|
||||
AnonymousReadReceipt.PROCESSING -> {
|
||||
holder.bubbleFooterReadReceipt.visibility = View.VISIBLE
|
||||
holder.bubbleFooterReadReceipt.setImageResource(R.drawable.ic_processing_msg)
|
||||
}
|
||||
else -> {
|
||||
holder.bubbleFooterReadReceipt.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
// We can't use end and start because of our weird layout RTL tricks
|
||||
val alignEnd = if(defaultRtl) RelativeLayout.ALIGN_LEFT else RelativeLayout.ALIGN_RIGHT
|
||||
val alignStart = if(defaultRtl) RelativeLayout.ALIGN_RIGHT else RelativeLayout.ALIGN_LEFT
|
||||
val startOf = if(defaultRtl) RelativeLayout.RIGHT_OF else RelativeLayout.LEFT_OF
|
||||
val endOf = if(defaultRtl) RelativeLayout.LEFT_OF else RelativeLayout.RIGHT_OF
|
||||
|
||||
val footerLayoutParams = holder.bubbleFootView.layoutParams as RelativeLayout.LayoutParams
|
||||
var footerMarginStartDp = holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_margin_start)
|
||||
var footerMarginEndDp = holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_margin_end)
|
||||
if (allowFooterOverlay(holder)) {
|
||||
footerLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.viewStubContainer)
|
||||
footerLayoutParams.addRule(alignEnd, R.id.viewStubContainer)
|
||||
footerLayoutParams.removeRule(alignStart)
|
||||
footerLayoutParams.removeRule(RelativeLayout.BELOW)
|
||||
footerLayoutParams.removeRule(endOf)
|
||||
footerLayoutParams.removeRule(startOf)
|
||||
if (needsFooterReservation(holder)) {
|
||||
// Remove style used when not having reserved space
|
||||
removeFooterOverlayStyle(holder)
|
||||
|
||||
// Calculate required footer space
|
||||
val footerMeasures = getFooterMeasures(holder, anonymousReadReceipt)
|
||||
val footerWidth = footerMeasures[0]
|
||||
val footerHeight = footerMeasures[1]
|
||||
|
||||
reserveFooterSpace(holder, footerWidth, footerHeight)
|
||||
} else {
|
||||
// We have no reserved space -> style it to ensure readability on arbitrary backgrounds
|
||||
styleFooterOverlay(holder)
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
allowFooterBelow(holder) -> {
|
||||
footerLayoutParams.addRule(RelativeLayout.BELOW, R.id.viewStubContainer)
|
||||
footerLayoutParams.addRule(alignEnd, R.id.viewStubContainer)
|
||||
footerLayoutParams.removeRule(alignStart)
|
||||
footerLayoutParams.removeRule(RelativeLayout.ALIGN_BOTTOM)
|
||||
footerLayoutParams.removeRule(endOf)
|
||||
footerLayoutParams.removeRule(startOf)
|
||||
footerLayoutParams.removeRule(RelativeLayout.START_OF)
|
||||
}
|
||||
reverseBubble -> /* force footer on the left / at the start */ {
|
||||
footerLayoutParams.addRule(RelativeLayout.START_OF, R.id.viewStubContainer)
|
||||
footerLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.viewStubContainer)
|
||||
footerLayoutParams.removeRule(alignEnd)
|
||||
footerLayoutParams.removeRule(alignStart)
|
||||
footerLayoutParams.removeRule(endOf)
|
||||
footerLayoutParams.removeRule(startOf)
|
||||
footerLayoutParams.removeRule(RelativeLayout.BELOW)
|
||||
// Reverse margins
|
||||
footerMarginStartDp = holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_reverse_margin_start)
|
||||
footerMarginEndDp = holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_reverse_margin_end)
|
||||
}
|
||||
else -> /* footer on the right / at the end */ {
|
||||
footerLayoutParams.addRule(endOf, R.id.viewStubContainer)
|
||||
footerLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.viewStubContainer)
|
||||
footerLayoutParams.removeRule(startOf)
|
||||
footerLayoutParams.removeRule(alignEnd)
|
||||
footerLayoutParams.removeRule(alignStart)
|
||||
footerLayoutParams.removeRule(RelativeLayout.BELOW)
|
||||
footerLayoutParams.removeRule(RelativeLayout.START_OF)
|
||||
}
|
||||
}
|
||||
removeFooterOverlayStyle(holder)
|
||||
}
|
||||
if (defaultRtl) {
|
||||
footerLayoutParams.rightMargin = footerMarginStartDp
|
||||
footerLayoutParams.leftMargin = footerMarginEndDp
|
||||
holder.bubbleMemberNameView.gravity = Gravity.RIGHT
|
||||
} else {
|
||||
footerLayoutParams.leftMargin = footerMarginStartDp
|
||||
footerLayoutParams.rightMargin = footerMarginEndDp
|
||||
holder.bubbleMemberNameView.gravity = Gravity.LEFT
|
||||
}
|
||||
}
|
||||
if (bubbleStyle == BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN) {
|
||||
// We need to align the non-bubble member name view to pseudo bubbles
|
||||
if (reverseBubble) {
|
||||
holder.memberNameView.setPaddingRelative(
|
||||
shortPaddingDp,
|
||||
0,
|
||||
longPaddingDp,
|
||||
0
|
||||
)
|
||||
} else {
|
||||
holder.memberNameView.setPaddingRelative(
|
||||
longPaddingDp,
|
||||
0,
|
||||
shortPaddingDp,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//BubbleThemeUtils.BUBBLE_STYLE_NONE,
|
||||
else -> {
|
||||
bubbleView.background = null
|
||||
(bubbleView.layoutParams as ViewGroup.MarginLayoutParams).marginStart = 0
|
||||
(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)
|
||||
holder.memberNameView.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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.reactionsContainer, if (reverseBubble) reverseDirection else defaultDirection,
|
||||
holder.eventBaseView.resources.configuration.layoutDirection)
|
||||
// Layout is broken if bubbleView is RTL (for some reason, Android uses left/end padding for right/start as well...)
|
||||
setFlatRtl(holder.bubbleView, View.LAYOUT_DIRECTION_LTR,
|
||||
holder.eventBaseView.resources.configuration.layoutDirection)
|
||||
}
|
||||
|
||||
private fun tintFooter(holder: H, color: Int) {
|
||||
val tintList = ColorStateList(arrayOf(intArrayOf(0)), intArrayOf(color))
|
||||
holder.bubbleFooterReadReceipt.imageTintList = tintList
|
||||
holder.bubbleFooterTimeView.setTextColor(tintList)
|
||||
}
|
||||
|
||||
private fun styleFooterOverlay(holder: H) {
|
||||
holder.bubbleFootView.setBackgroundResource(R.drawable.timestamp_overlay)
|
||||
tintFooter(holder, ThemeUtils.getColor(holder.bubbleFootView.context, R.attr.timestamp_overlay_fg))
|
||||
val padding = holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_overlay_padding)
|
||||
holder.bubbleFootView.setPaddingRelative(
|
||||
padding,
|
||||
padding,
|
||||
// compensate from inner view padding on the other side
|
||||
padding + holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_padding_compensation),
|
||||
padding
|
||||
)
|
||||
}
|
||||
|
||||
private fun removeFooterOverlayStyle(holder: H) {
|
||||
holder.bubbleFootView.background = null
|
||||
tintFooter(holder, ThemeUtils.getColor(holder.bubbleFootView.context, R.attr.vctr_content_secondary))
|
||||
holder.bubbleFootView.setPaddingRelative(
|
||||
0,
|
||||
holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_noverlay_padding_top),
|
||||
0,
|
||||
holder.bubbleFootView.resources.getDimensionPixelSize(R.dimen.sc_footer_noverlay_padding_bottom)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,11 @@
|
|||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewStub
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import im.vector.app.R
|
||||
|
@ -32,8 +28,6 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
|
|||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.platform.CheckableView
|
||||
import im.vector.app.core.ui.views.BubbleDependentView
|
||||
import im.vector.app.core.ui.views.updateMessageBubble
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
|
||||
/**
|
||||
* Children must override getViewType()
|
||||
|
@ -71,8 +65,6 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
|||
}
|
||||
}
|
||||
holder.checkableBackground.isChecked = highlighted
|
||||
|
||||
updateMessageBubble(holder.checkableBackground.context, holder)
|
||||
}
|
||||
|
||||
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
||||
|
@ -93,55 +85,4 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
|||
open fun ignoreMessageGuideline(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldReverseBubble(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun pseudoBubbleAllowed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
/* TODO-SC-merge: read receipt layout alignment
|
||||
val defaultDirection = holder.readReceiptsView.resources.configuration.layoutDirection;
|
||||
val defaultRtl = defaultDirection == View.LAYOUT_DIRECTION_RTL
|
||||
val reverseDirection = if (defaultRtl) View.LAYOUT_DIRECTION_LTR else View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
// Always keep read receipts of others on other side for dual side bubbles
|
||||
val dualBubbles = BubbleThemeUtils.drawsDualSide(bubbleStyleSetting)
|
||||
|
||||
val receiptParent = holder.readReceiptsView.parent
|
||||
if (receiptParent is LinearLayout) {
|
||||
(holder.readReceiptsView.layoutParams as LinearLayout.LayoutParams).gravity = if (dualBubbles) Gravity.START else Gravity.END
|
||||
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.END_OF)
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.ALIGN_PARENT_START)
|
||||
if (dualBubbles) {
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_START, RelativeLayout.TRUE)
|
||||
} else {
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.END_OF, R.id.messageStartGuideline)
|
||||
}
|
||||
} else {
|
||||
if (dualBubbles) {
|
||||
(holder.readReceiptsView.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.ALIGN_PARENT_END)
|
||||
} else {
|
||||
(holder.readReceiptsView.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_END)
|
||||
}
|
||||
}
|
||||
|
||||
// Also set rtl to have members fill from the natural side
|
||||
setFlatRtl(holder.readReceiptsView, if (dualBubbles) reverseDirection else defaultDirection, defaultDirection)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
fun updateMessageBubble(context: Context, holder: H) {
|
||||
return updateMessageBubble(context, this, holder)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
|
@ -28,15 +27,12 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import kotlin.math.max
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
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.themes.ThemeUtils
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import kotlin.math.ceil
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
|
||||
|
@ -109,24 +105,6 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
|
|||
|
||||
override fun getViewStubId() = STUB_ID
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getViewStubMinimumWidth(holder: Holder, contentInBubble: Boolean, showInformation: Boolean): Int {
|
||||
val superVal = super.getViewStubMinimumWidth(holder, contentInBubble, showInformation)
|
||||
|
||||
// Guess text width for name and time
|
||||
// On first call, holder.fileImageView.width is not initialized yet
|
||||
val imageWidth = holder.fileImageView.resources.getDimensionPixelSize(R.dimen.chat_avatar_size)
|
||||
val minimumWidthWithText =
|
||||
ceil(BubbleThemeUtils.guessTextWidth(holder.filenameView, filename)).toInt() +
|
||||
imageWidth +
|
||||
holder.filenameView.resources.getDimensionPixelSize(R.dimen.sc_bubble_guess_minimum_width_padding)
|
||||
val absoluteMinimumWidth = imageWidth*3
|
||||
return max(max(absoluteMinimumWidth, minimumWidthWithText), superVal)
|
||||
}
|
||||
|
||||
class Holder : AbsMessageItem.Holder(STUB_ID) {
|
||||
val mainLayout by bind<ViewGroup>(R.id.messageFileMainLayout)
|
||||
val progressLayout by bind<ViewGroup>(R.id.messageFileUploadProgressLayout)
|
||||
|
|
|
@ -16,12 +16,9 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
|
@ -37,7 +34,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStat
|
|||
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
|
||||
import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
|
@ -61,6 +57,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
@EpoxyAttribute
|
||||
lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder
|
||||
|
||||
// SC-TODO
|
||||
var lastAllowedFooterOverlay: Boolean = true
|
||||
var lastShowFooterBellow: Boolean = true
|
||||
var forceAllowFooterOverlay: Boolean? = null
|
||||
|
@ -73,6 +70,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
val onImageSizeListener = object: ImageContentRenderer.OnImageSizeListener {
|
||||
override fun onImageSizeUpdated(width: Int, height: Int) {
|
||||
// Image size change -> different footer space situation possible
|
||||
/* SC-TODO
|
||||
val footerMeasures = getFooterMeasures(holder)
|
||||
forceAllowFooterOverlay = shouldAllowFooterOverlay(footerMeasures, width, height)
|
||||
val newShowFooterBellow = shouldShowFooterBellow(footerMeasures, width, height)
|
||||
|
@ -80,6 +78,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
showFooterBellow = newShowFooterBellow
|
||||
updateMessageBubble(holder.imageView.context, holder)
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
val animate = mediaData.mimeType == MimeTypes.Gif
|
||||
|
@ -123,43 +122,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
|
||||
override fun getViewStubId() = STUB_ID
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun pseudoBubbleAllowed(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getBubbleMargin(resources: Resources, bubbleStyle: String, reverseBubble: Boolean): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun setBubbleLayout(holder: Holder, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
super.setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
|
||||
|
||||
// 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
|
||||
holder.imageView.scaleType = if (reverseBubble) ImageView.ScaleType.FIT_END else ImageView.ScaleType.FIT_START
|
||||
// Case: Message information (sender name + date) makes the containing view wider than the ImageView
|
||||
// -> Align ImageView within its parent to the same side as message bubbles
|
||||
(holder.imageView.layoutParams as ConstraintLayout.LayoutParams).horizontalBias = if (reverseBubble) 1f else 0f
|
||||
|
||||
// Image outline
|
||||
when {
|
||||
bubbleStyle == BubbleThemeUtils.BUBBLE_STYLE_NONE || mode != ImageContentRenderer.Mode.THUMBNAIL -> {
|
||||
// Don't show it for non-bubble layouts, don't show for Stickers, ...
|
||||
holder.mediaContentView.background = null
|
||||
}
|
||||
attributes.informationData.sentByMe -> {
|
||||
holder.mediaContentView.setBackgroundResource(R.drawable.background_image_border_outgoing)
|
||||
}
|
||||
else -> {
|
||||
holder.mediaContentView.setBackgroundResource(R.drawable.background_image_border_incoming)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SC-TODO
|
||||
private fun shouldAllowFooterOverlay(footerMeasures: Array<Int>, imageWidth: Int, imageHeight: Int): Boolean {
|
||||
val footerWidth = footerMeasures[0]
|
||||
val footerHeight = footerMeasures[1]
|
||||
|
@ -168,6 +131,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
return imageWidth > 1.5*footerWidth && imageHeight > 1.5*footerHeight && (imageWidth * imageHeight > 4 * footerWidth * footerHeight)
|
||||
}
|
||||
|
||||
// SC-TODO
|
||||
private fun shouldShowFooterBellow(footerMeasures: Array<Int>, imageWidth: Int, imageHeight: Int): Boolean {
|
||||
// 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
|
||||
|
@ -177,32 +141,6 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
|||
return imageWidth > 1.5*footerWidth && imageHeight < 1.5*footerHeight
|
||||
}
|
||||
|
||||
override fun allowFooterOverlay(holder: Holder): Boolean {
|
||||
val rememberedAllowFooterOverlay = forceAllowFooterOverlay
|
||||
if (rememberedAllowFooterOverlay != null) {
|
||||
lastAllowedFooterOverlay = rememberedAllowFooterOverlay
|
||||
return rememberedAllowFooterOverlay
|
||||
}
|
||||
val imageWidth = holder.imageView.width
|
||||
val imageHeight = holder.imageView.height
|
||||
if (imageWidth == 0 && imageHeight == 0) {
|
||||
// Not initialised yet, assume true
|
||||
lastAllowedFooterOverlay = true
|
||||
return true
|
||||
}
|
||||
// If the footer covers most of the image, or is even larger than the image, move it outside
|
||||
val footerMeasures = getFooterMeasures(holder)
|
||||
lastAllowedFooterOverlay = shouldAllowFooterOverlay(footerMeasures, imageWidth, imageHeight)
|
||||
return lastAllowedFooterOverlay
|
||||
}
|
||||
|
||||
override fun allowFooterBelow(holder: Holder): Boolean {
|
||||
val showBellow = showFooterBellow
|
||||
lastShowFooterBellow = showBellow
|
||||
return showBellow
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Holder : AbsMessageItem.Holder(STUB_ID) {
|
||||
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
|
||||
|
|
|
@ -16,13 +16,10 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.text.method.MovementMethod
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.text.PrecomputedTextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
|
@ -180,36 +177,4 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
|||
companion object {
|
||||
private const val STUB_ID = R.id.messageContentTextStub
|
||||
}
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun allowFooterOverlay(holder: Holder): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun needsFooterReservation(holder: Holder): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun reserveFooterSpace(holder: Holder, width: Int, height: Int) {
|
||||
if (attributes.informationData.messageLayout !is TimelineMessageLayout.ScBubble) {
|
||||
return
|
||||
}
|
||||
// Remember for PreviewUrlViewUpdater.onStateUpdated
|
||||
footerWidth = width
|
||||
footerHeight = height
|
||||
// Reserve both in preview and in message
|
||||
// User might close preview, so we still need place in the message
|
||||
// if we don't want to change this afterwards
|
||||
// This might be a race condition, but the UI-isssue if evaluated wrongly is negligible
|
||||
if (!holder.previewUrlViewSc.isVisible) {
|
||||
holder.messageView.footerWidth = width
|
||||
holder.messageView.footerHeight = height
|
||||
} // else: will be handled in onStateUpdated
|
||||
holder.previewUrlViewSc.footerWidth = height
|
||||
holder.previewUrlViewSc.footerHeight = height
|
||||
holder.previewUrlViewSc.updateFooterSpace()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.text.format.DateUtils
|
||||
|
@ -24,7 +23,6 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
|
@ -37,7 +35,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadSt
|
|||
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker
|
||||
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
|
@ -164,18 +161,4 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
|||
companion object {
|
||||
private const val STUB_ID = R.id.messageContentVoiceStub
|
||||
}
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setBubbleLayout(holder: Holder, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
super.setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
|
||||
|
||||
if (BubbleThemeUtils.drawsActualBubbles(bubbleStyle)) {
|
||||
(holder.voiceLayout.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = 0
|
||||
} else {
|
||||
(holder.voiceLayout.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = holder.voiceLayout.resources.getDimensionPixelSize(R.dimen.no_bubble_margin_end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
|
@ -90,8 +89,4 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||
companion object {
|
||||
private const val STUB_ID = R.id.messageContentPollStub
|
||||
}
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,6 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
|
@ -32,11 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
|
|||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.ui.views.BubbleDependentView
|
||||
import im.vector.app.core.ui.views.ReadReceiptsView
|
||||
import im.vector.app.core.ui.views.setFlatRtl
|
||||
import im.vector.app.core.ui.views.updateMessageBubble
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
import timber.log.Timber
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_read_receipts)
|
||||
abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(), ItemWithEvents, BubbleDependentView<ReadReceiptsItem.Holder> {
|
||||
|
@ -56,8 +46,6 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
|
|||
holder.readReceiptsView.onClick(clickListener)
|
||||
holder.readReceiptsView.render(readReceipts, avatarRenderer)
|
||||
|
||||
updateMessageBubble(holder.readReceiptsView.context, holder)
|
||||
|
||||
holder.readReceiptsView.isVisible = !shouldHideReadReceipts
|
||||
}
|
||||
|
||||
|
@ -70,51 +58,4 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
|
|||
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
||||
}
|
||||
|
||||
override fun setBubbleLayout(holder: Holder, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
|
||||
val defaultDirection = holder.readReceiptsView.resources.configuration.layoutDirection;
|
||||
val defaultRtl = defaultDirection == View.LAYOUT_DIRECTION_RTL
|
||||
val reverseDirection = if (defaultRtl) View.LAYOUT_DIRECTION_LTR else View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
// Always keep read receipts of others on other side for dual side bubbles
|
||||
val dualBubbles = BubbleThemeUtils.drawsDualSide(bubbleStyleSetting)
|
||||
|
||||
/*
|
||||
val receiptParent = holder.readReceiptsView.parent
|
||||
if (receiptParent is LinearLayout) {
|
||||
(holder.readReceiptsView.layoutParams as LinearLayout.LayoutParams).gravity = if (dualBubbles) Gravity.START else Gravity.END
|
||||
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.END_OF)
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.ALIGN_PARENT_START)
|
||||
if (dualBubbles) {
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_START, RelativeLayout.TRUE)
|
||||
} else {
|
||||
(receiptParent.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.END_OF, R.id.messageStartGuideline)
|
||||
}
|
||||
} else if (receiptParent is RelativeLayout) {
|
||||
if (dualBubbles) {
|
||||
(holder.readReceiptsView.layoutParams as RelativeLayout.LayoutParams).removeRule(RelativeLayout.ALIGN_PARENT_END)
|
||||
} else {
|
||||
(holder.readReceiptsView.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_END)
|
||||
}
|
||||
} else if (receiptParent is FrameLayout) {
|
||||
*/
|
||||
if (dualBubbles) {
|
||||
(holder.readReceiptsView.layoutParams as FrameLayout.LayoutParams).gravity = Gravity.START
|
||||
} else {
|
||||
(holder.readReceiptsView.layoutParams as FrameLayout.LayoutParams).gravity = Gravity.END
|
||||
}
|
||||
/*
|
||||
} else {
|
||||
Timber.e("Unsupported layout for read receipts parent: $receiptParent")
|
||||
}
|
||||
*/
|
||||
|
||||
// Also set rtl to have members fill from the natural side
|
||||
setFlatRtl(holder.readReceiptsView, if (dualBubbles) reverseDirection else defaultDirection, defaultDirection)
|
||||
}
|
||||
|
||||
fun updateMessageBubble(context: Context, holder: Holder) {
|
||||
return updateMessageBubble(context, this, holder)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.features.themes.BubbleThemeUtils
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class RedactedMessageItem : AbsMessageItem<RedactedMessageItem.Holder>() {
|
||||
|
@ -33,8 +31,4 @@ abstract class RedactedMessageItem : AbsMessageItem<RedactedMessageItem.Holder>(
|
|||
companion object {
|
||||
private const val STUB_ID = R.id.messageContentRedactedStub
|
||||
}
|
||||
|
||||
override fun messageBubbleAllowed(context: Context): Boolean {
|
||||
return BubbleThemeUtils.getBubbleStyle(context) == BubbleThemeUtils.BUBBLE_STYLE_BOTH
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ class TimelineLayoutSettingsProvider @Inject constructor(private val vectorPrefe
|
|||
|
||||
fun getLayoutSettings(): TimelineLayoutSettings {
|
||||
// SC-TODO
|
||||
if (true) return TimelineLayoutSettings.SC_BUBBLE
|
||||
return if (vectorPreferences.useMessageBubblesLayout()) {
|
||||
TimelineLayoutSettings.BUBBLE
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<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"
|
||||
|
@ -72,142 +71,35 @@
|
|||
android:id="@+id/messageE2EDecoration"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignTop="@id/bubbleWrapper"
|
||||
android:layout_alignTop="@id/viewStubContainer"
|
||||
android:layout_alignEnd="@id/decorationSpace"
|
||||
android:layout_marginTop="7dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!-- bubble wrapper for controlling gravity -->
|
||||
<FrameLayout
|
||||
android:id="@+id/bubbleWrapper"
|
||||
android:layout_width="match_parent"
|
||||
<include
|
||||
android:id="@+id/viewStubContainer"
|
||||
layout="@layout/item_timeline_event_view_stubs_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/messageMemberNameView"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toStartOf="@id/messageSendStateImageView"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/bubbleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_marginVertical="4dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
tools:background="@drawable/msg_bubble_text_incoming"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<!--
|
||||
<im.vector.app.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="?vctr_content_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:layout_marginBottom="4dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
|
||||
<include
|
||||
android:id="@+id/viewStubContainer"
|
||||
layout="@layout/item_timeline_event_view_stubs_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/bubbleMessageTimeView"
|
||||
android:layout_margin="0dp"
|
||||
android:addStatesFromChildren="true" />
|
||||
|
||||
<!--
|
||||
<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="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
-->
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_marginBottom="1dp"
|
||||
tools:layout_marginStart="4dp"
|
||||
tools:layout_marginEnd="1dp"
|
||||
tools:layout_alignBottom="@id/viewStubContainer"
|
||||
tools:layout_alignEnd="@id/viewStubContainer"
|
||||
tools:paddingTop="@dimen/sc_footer_noverlay_padding_top"
|
||||
tools:paddingBottom="@dimen/sc_footer_noverlay_padding_bottom"
|
||||
android:id="@+id/bubbleFootView">
|
||||
<TextView
|
||||
android:id="@+id/bubbleFooterMessageTimeView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:maxLines="1"
|
||||
tools:textColor="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
android:layout_gravity="bottom"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
<!-- We read maxWidth and maxHeight from code to guess footer size -->
|
||||
<ImageView
|
||||
android:id="@+id/bubbleFooterReadReceipt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="16dp"
|
||||
android:maxHeight="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_gravity="bottom"
|
||||
tools:tint="?vctr_content_secondary"
|
||||
tools:src="@drawable/ic_processing_msg"
|
||||
android:contentDescription="@string/footer_read_receipt_content_description" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
android:addStatesFromChildren="true" />
|
||||
|
||||
<im.vector.app.core.ui.views.SendStateImageView
|
||||
android:id="@+id/messageSendStateImageView"
|
||||
android:layout_width="@dimen/item_event_message_state_size"
|
||||
android:layout_height="@dimen/item_event_message_state_size"
|
||||
android:layout_alignBottom="@id/bubbleWrapper"
|
||||
android:layout_alignBottom="@id/viewStubContainer"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:contentDescription="@string/event_status_a11y_sending"
|
||||
android:src="@drawable/ic_sending_message"
|
||||
android:visibility="gone"
|
||||
android:visibility="invisible"
|
||||
tools:tint="?vctr_content_tertiary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
@ -215,7 +107,7 @@
|
|||
android:id="@+id/eventSendingIndicator"
|
||||
android:layout_width="@dimen/item_event_message_state_size"
|
||||
android:layout_height="@dimen/item_event_message_state_size"
|
||||
android:layout_alignBottom="@id/bubbleWrapper"
|
||||
android:layout_alignBottom="@id/viewStubContainer"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
|
@ -230,7 +122,7 @@
|
|||
android:id="@+id/informationBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/bubbleWrapper"
|
||||
android:layout_below="@id/viewStubContainer"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:addStatesFromChildren="true"
|
||||
android:orientation="vertical">
|
||||
|
@ -278,4 +170,4 @@
|
|||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
281
vector/src/main/res/layout/view_message_bubble_sc.xml
Normal file
281
vector/src/main/res/layout/view_message_bubble_sc.xml
Normal file
|
@ -0,0 +1,281 @@
|
|||
<?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"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<im.vector.app.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="@drawable/highlighted_message_background" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageAvatarImageView"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
tools:src="@sample/user_round_avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageMemberNameView"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:layout_toStartOf="@id/messageTimeView"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
tools:text="@sample/users.json/data/displayName" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageTimeView"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
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_marginEnd="8dp"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
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" />
|
||||
|
||||
<im.vector.app.core.ui.views.ShieldImageView
|
||||
android:id="@+id/messageE2EDecoration"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignTop="@id/bubbleWrapper"
|
||||
android:layout_alignEnd="@id/decorationSpace"
|
||||
android:layout_marginTop="7dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!-- bubble wrapper for controlling gravity -->
|
||||
<FrameLayout
|
||||
android:id="@+id/bubbleWrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/messageMemberNameView"
|
||||
android:layout_toStartOf="@id/messageSendStateImageView"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/bubbleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_marginVertical="4dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
tools:background="@drawable/msg_bubble_text_incoming"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<!--
|
||||
<im.vector.app.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="?vctr_content_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:layout_marginBottom="4dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
|
||||
<include
|
||||
android:id="@+id/viewStubContainer"
|
||||
layout="@layout/item_timeline_event_view_stubs_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/bubbleMessageTimeView"
|
||||
android:layout_margin="0dp"
|
||||
android:addStatesFromChildren="true" />
|
||||
|
||||
<!--
|
||||
<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="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
-->
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_marginBottom="1dp"
|
||||
tools:layout_marginStart="4dp"
|
||||
tools:layout_marginEnd="1dp"
|
||||
tools:layout_alignBottom="@id/viewStubContainer"
|
||||
tools:layout_alignEnd="@id/viewStubContainer"
|
||||
tools:paddingTop="@dimen/sc_footer_noverlay_padding_top"
|
||||
tools:paddingBottom="@dimen/sc_footer_noverlay_padding_bottom"
|
||||
android:id="@+id/bubbleFootView">
|
||||
<TextView
|
||||
android:id="@+id/bubbleFooterMessageTimeView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:maxLines="1"
|
||||
tools:textColor="?vctr_content_secondary"
|
||||
android:textSize="12sp"
|
||||
android:layout_gravity="bottom"
|
||||
tools:text="@tools:sample/date/hhmm" />
|
||||
<!-- We read maxWidth and maxHeight from code to guess footer size -->
|
||||
<ImageView
|
||||
android:id="@+id/bubbleFooterReadReceipt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="16dp"
|
||||
android:maxHeight="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_gravity="bottom"
|
||||
tools:tint="?vctr_content_secondary"
|
||||
tools:src="@drawable/ic_processing_msg"
|
||||
android:contentDescription="@string/footer_read_receipt_content_description" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<im.vector.app.core.ui.views.SendStateImageView
|
||||
android:id="@+id/messageSendStateImageView"
|
||||
android:layout_width="@dimen/item_event_message_state_size"
|
||||
android:layout_height="@dimen/item_event_message_state_size"
|
||||
android:layout_alignBottom="@id/bubbleWrapper"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:contentDescription="@string/event_status_a11y_sending"
|
||||
android:src="@drawable/ic_sending_message"
|
||||
android:visibility="gone"
|
||||
tools:tint="?vctr_content_tertiary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/eventSendingIndicator"
|
||||
android:layout_width="@dimen/item_event_message_state_size"
|
||||
android:layout_height="@dimen/item_event_message_state_size"
|
||||
android:layout_alignBottom="@id/bubbleWrapper"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:indeterminateTint="?vctr_content_secondary"
|
||||
android:visibility="gone"
|
||||
app:tint="?vctr_content_tertiary"
|
||||
tools:ignore="MissingPrefix"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/informationBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/bubbleWrapper"
|
||||
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.app.features.reactions.widget.ReactionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" /-->
|
||||
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/messageThreadSummaryConstraintLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/informationBottom"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingStart="13dp"
|
||||
android:paddingEnd="13dp"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:background="@drawable/rounded_rect_shape_8"
|
||||
android:contentDescription="@string/room_threads_filter"
|
||||
android:maxWidth="496dp"
|
||||
android:minWidth="144dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<include layout="@layout/view_thread_room_summary" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
Loading…
Add table
Reference in a new issue