Bubbles: continue exploration

This commit is contained in:
ganfra 2022-01-11 11:57:35 +01:00
parent ad63d3de1c
commit f7c9b36cef
8 changed files with 20 additions and 19 deletions

View File

@ -3,7 +3,7 @@
<declare-styleable name="MessageBubble"> <declare-styleable name="MessageBubble">
<attr name="incoming_style" format="boolean" /> <attr name="incoming_style" format="boolean" />
<attr name="show_background" format="boolean" /> <attr name="show_time_overlay" format="boolean" />
<attr name="is_first" format="boolean" /> <attr name="is_first" format="boolean" />
<attr name="is_last" format="boolean" /> <attr name="is_last" format="boolean" />
</declare-styleable> </declare-styleable>

View File

@ -358,6 +358,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
// Should be build if not cached or if model should be refreshed // Should be build if not cached or if model should be refreshed
if (modelCache[position] == null || modelCache[position]?.isCacheable(partialState) == false) { if (modelCache[position] == null || modelCache[position]?.isCacheable(partialState) == false) {
val prevEvent = currentSnapshot.prevOrNull(position) val prevEvent = currentSnapshot.prevOrNull(position)
val prevDisplayableEvent = currentSnapshot.subList(0, position).lastOrNull {
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId)
}
val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull { val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull {
timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId) timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId)
} }
@ -365,6 +368,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
val params = TimelineItemFactoryParams( val params = TimelineItemFactoryParams(
event = event, event = event,
prevEvent = prevEvent, prevEvent = prevEvent,
prevDisplayableEvent = prevDisplayableEvent,
nextEvent = nextEvent, nextEvent = nextEvent,
nextDisplayableEvent = nextDisplayableEvent, nextDisplayableEvent = nextDisplayableEvent,
partialState = partialState, partialState = partialState,

View File

@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
data class TimelineItemFactoryParams( data class TimelineItemFactoryParams(
val event: TimelineEvent, val event: TimelineEvent,
val prevEvent: TimelineEvent? = null, val prevEvent: TimelineEvent? = null,
val prevDisplayableEvent: TimelineEvent? = null,
val nextEvent: TimelineEvent? = null, val nextEvent: TimelineEvent? = null,
val nextDisplayableEvent: TimelineEvent? = null, val nextDisplayableEvent: TimelineEvent? = null,
val partialState: TimelineEventController.PartialState = TimelineEventController.PartialState(), val partialState: TimelineEventController.PartialState = TimelineEventController.PartialState(),

View File

@ -57,19 +57,21 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
fun create(params: TimelineItemFactoryParams): MessageInformationData { fun create(params: TimelineItemFactoryParams): MessageInformationData {
val event = params.event val event = params.event
val nextDisplayableEvent = params.nextDisplayableEvent val nextDisplayableEvent = params.nextDisplayableEvent
val prevEvent = params.prevEvent val prevDisplayableEvent = params.prevDisplayableEvent
val eventId = event.eventId val eventId = event.eventId
val isSentByMe = event.root.senderId == session.myUserId val isSentByMe = event.root.senderId == session.myUserId
val isFirstFromThisSender = nextDisplayableEvent?.root?.senderId != event.root.senderId
val isLastFromThisSender = prevEvent?.root?.senderId != event.root.senderId
val roomSummary = params.partialState.roomSummary val roomSummary = params.partialState.roomSummary
val date = event.root.localDateTime() val date = event.root.localDateTime()
val nextDate = nextDisplayableEvent?.root?.localDateTime() val nextDate = nextDisplayableEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60)) val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60))
?: false ?: false
val isFirstFromThisSender = nextDisplayableEvent?.root?.senderId != event.root.senderId || addDaySeparator
val isLastFromThisSender = prevDisplayableEvent?.root?.senderId != event.root.senderId || prevDisplayableEvent?.root?.localDateTime()?.toLocalDate() != date.toLocalDate()
val showInformation = val showInformation =
(addDaySeparator || (addDaySeparator ||
event.senderInfo.avatarUrl != nextDisplayableEvent?.senderInfo?.avatarUrl || event.senderInfo.avatarUrl != nextDisplayableEvent?.senderInfo?.avatarUrl ||

View File

@ -29,7 +29,6 @@ import im.vector.app.core.epoxy.onClick
import im.vector.app.core.files.LocalFilesHelper import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.view.MessageBubbleView
import im.vector.app.features.home.room.detail.timeline.view.MessageViewConfiguration import im.vector.app.features.home.room.detail.timeline.view.MessageViewConfiguration
import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.ImageContentRenderer
@ -72,7 +71,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
holder.mediaContentView.onClick(attributes.itemClickListener) holder.mediaContentView.onClick(attributes.itemClickListener)
holder.mediaContentView.setOnLongClickListener(attributes.itemLongClickListener) holder.mediaContentView.setOnLongClickListener(attributes.itemLongClickListener)
holder.playContentView.visibility = if (playable) View.VISIBLE else View.GONE holder.playContentView.visibility = if (playable) View.VISIBLE else View.GONE
(holder.view as? MessageViewConfiguration)?.displayBorder = false (holder.view as? MessageViewConfiguration)?.showTimeAsOverlay = false
} }
override fun unbind(holder: Holder) { override fun unbind(holder: Holder) {

View File

@ -54,7 +54,7 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri
render() render()
} }
override var displayBorder: Boolean = true override var showTimeAsOverlay: Boolean = true
set(value) { set(value) {
field = value field = value
render() render()
@ -66,7 +66,7 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri
inflate(context, R.layout.view_message_bubble, this) inflate(context, R.layout.view_message_bubble, this)
context.withStyledAttributes(attrs, R.styleable.MessageBubble) { context.withStyledAttributes(attrs, R.styleable.MessageBubble) {
isIncoming = getBoolean(R.styleable.MessageBubble_incoming_style, false) isIncoming = getBoolean(R.styleable.MessageBubble_incoming_style, false)
displayBorder = getBoolean(R.styleable.MessageBubble_show_background, true) showTimeAsOverlay = getBoolean(R.styleable.MessageBubble_show_time_overlay, true)
isFirstFromSender = getBoolean(R.styleable.MessageBubble_is_first, false) isFirstFromSender = getBoolean(R.styleable.MessageBubble_is_first, false)
isLastFromSender = getBoolean(R.styleable.MessageBubble_is_last, false) isLastFromSender = getBoolean(R.styleable.MessageBubble_is_last, false)
} }
@ -109,13 +109,14 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri
ConstraintSet().apply { ConstraintSet().apply {
clone(bubbleView) clone(bubbleView)
clear(R.id.viewStubContainer, ConstraintSet.END) clear(R.id.viewStubContainer, ConstraintSet.END)
if (displayBorder) { if (showTimeAsOverlay) {
connect(R.id.viewStubContainer, ConstraintSet.END, R.id.messageTimeView, ConstraintSet.START, 0) connect(R.id.viewStubContainer, ConstraintSet.END, R.id.messageTimeView, ConstraintSet.START, 0)
} else { } else {
connect(R.id.viewStubContainer, ConstraintSet.END, R.id.parent, ConstraintSet.END, 0) connect(R.id.viewStubContainer, ConstraintSet.END, R.id.parent, ConstraintSet.END, 0)
} }
applyTo(bubbleView) applyTo(bubbleView)
} }
} }
private fun createBackgroundDrawable(): Drawable { private fun createBackgroundDrawable(): Drawable {
@ -147,12 +148,8 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri
.setBottomRightCorner(bottomCornerFamily, bottomRadius) .setBottomRightCorner(bottomCornerFamily, bottomRadius)
} }
val shapeAppearanceModel = shapeAppearanceModelBuilder.build() val shapeAppearanceModel = shapeAppearanceModelBuilder.build()
val shapeDrawable = MaterialShapeDrawable(shapeAppearanceModel) return MaterialShapeDrawable(shapeAppearanceModel).apply {
if (displayBorder) { fillColor = ContextCompat.getColorStateList(context, backgroundColor)
shapeDrawable.fillColor = ContextCompat.getColorStateList(context, backgroundColor) }
} else {
shapeDrawable.fillColor = ContextCompat.getColorStateList(context, android.R.color.transparent)
}
return shapeDrawable
} }
} }

View File

@ -20,5 +20,5 @@ interface MessageViewConfiguration {
var isIncoming: Boolean var isIncoming: Boolean
var isFirstFromSender: Boolean var isFirstFromSender: Boolean
var isLastFromSender: Boolean var isLastFromSender: Boolean
var displayBorder: Boolean var showTimeAsOverlay: Boolean
} }

View File

@ -4,14 +4,12 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/palette_element_green"
tools:viewBindingIgnore="true"> tools:viewBindingIgnore="true">
<ImageView <ImageView
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:contentDescription="@string/a11y_image" android:contentDescription="@string/a11y_image"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"