Fix alignment of read receipts for dual-side bubbles

Change-Id: I60d6dde73071fa28d069cba87850cd85d6ddd36b
This commit is contained in:
SpiritCroc 2021-04-14 17:22:34 +02:00
parent d568523b8b
commit 221be6947d
3 changed files with 115 additions and 39 deletions

View File

@ -0,0 +1,55 @@
package im.vector.app.core.ui.views
import android.content.Context
import android.view.ViewGroup
import androidx.core.view.children
import im.vector.app.features.themes.BubbleThemeUtils
interface BubbleDependentView<H> {
fun updateMessageBubble(context: Context, holder: H) {
val bubbleStyleSetting = BubbleThemeUtils.getBubbleStyle(context)
val bubbleStyle = when {
messageBubbleAllowed(context) -> {
bubbleStyleSetting
}
bubbleStyleSetting == BubbleThemeUtils.BUBBLE_STYLE_BOTH && pseudoBubbleAllowed() -> {
BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN
}
bubbleStyleSetting == BubbleThemeUtils.BUBBLE_STYLE_START && pseudoBubbleAllowed() -> {
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN
}
else -> {
BubbleThemeUtils.BUBBLE_STYLE_NONE
}
}
val reverseBubble = shouldReverseBubble() && BubbleThemeUtils.drawsDualSide(bubbleStyle)
setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
}
fun messageBubbleAllowed(context: Context): Boolean {
return false
}
fun shouldReverseBubble(): Boolean {
return false
}
fun pseudoBubbleAllowed(): Boolean {
return false
}
fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean)
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

@ -31,13 +31,14 @@ import im.vector.app.R
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.utils.DimensionConverter
import im.vector.app.features.themes.BubbleThemeUtils
/**
* Children must override getViewType()
*/
abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>(), ItemWithEvents {
abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>(), ItemWithEvents, BubbleDependentView<H> {
// To use for instance when opening a permalink with an eventId
@EpoxyAttribute
@ -61,7 +62,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
}
holder.checkableBackground.isChecked = highlighted
updateMessageBubble(holder)
updateMessageBubble(holder.checkableBackground.context, holder)
}
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
@ -82,52 +83,20 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
return false
}
protected 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
}
}
}
fun updateMessageBubble(holder: H) {
val bubbleStyleSetting = BubbleThemeUtils.getBubbleStyle(holder.checkableBackground.context)
val bubbleStyle = when {
messageBubbleAllowed(holder.checkableBackground.context) -> {
bubbleStyleSetting
}
bubbleStyleSetting == BubbleThemeUtils.BUBBLE_STYLE_BOTH && pseudoBubbleAllowed() -> {
BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN
}
bubbleStyleSetting == BubbleThemeUtils.BUBBLE_STYLE_START && pseudoBubbleAllowed() -> {
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN
}
else -> {
BubbleThemeUtils.BUBBLE_STYLE_NONE
}
}
val reverseBubble = shouldReverseBubble() && BubbleThemeUtils.drawsDualSide(bubbleStyle)
setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
}
open fun messageBubbleAllowed(context: Context): Boolean {
override fun messageBubbleAllowed(context: Context): Boolean {
return false
}
open fun shouldReverseBubble(): Boolean {
override fun shouldReverseBubble(): Boolean {
return false
}
open fun pseudoBubbleAllowed(): Boolean {
override fun pseudoBubbleAllowed(): Boolean {
return false
}
@CallSuper
open fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
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

View File

@ -16,17 +16,24 @@
package im.vector.app.features.home.room.detail.timeline.item
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.RelativeLayout
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.ui.views.BubbleDependentView
import im.vector.app.core.ui.views.ReadReceiptsView
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 {
abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(), ItemWithEvents, BubbleDependentView<ReadReceiptsItem.Holder> {
@EpoxyAttribute lateinit var eventId: String
@EpoxyAttribute lateinit var readReceipts: List<ReadReceiptData>
@ -40,6 +47,8 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
override fun bind(holder: Holder) {
super.bind(holder)
holder.readReceiptsView.render(readReceipts, avatarRenderer, clickListener)
updateMessageBubble(holder.readReceiptsView.context, holder)
}
override fun unbind(holder: Holder) {
@ -50,4 +59,47 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
class Holder : VectorEpoxyHolder() {
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)
}
}