Use pseudo-bubbles for for ImageVideo items

Pseudo-bubbles: align like bubbles, but don't draw the actual bubbles.
Closes #8.

Change-Id: I8ff281858f67ed8a3ee5e4530e3ce02aa786b955
This commit is contained in:
SpiritCroc 2020-09-25 12:40:54 +02:00
parent 0d7c0bc29c
commit 037a333c37
4 changed files with 88 additions and 27 deletions

View File

@ -25,11 +25,9 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.core.view.children
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import kotlin.math.max import kotlin.math.max
import kotlin.math.round import kotlin.math.round
@ -84,7 +82,17 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener) holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener) holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener)
holder.bubbleMemberNameView.setOnLongClickListener(attributes.itemLongClickListener) holder.bubbleMemberNameView.setOnLongClickListener(attributes.itemLongClickListener)
if (contentInBubble) { if (hideSenderInformation()) {
holder.memberNameView.visibility = View.GONE
holder.timeView.visibility = View.GONE
holder.bubbleMemberNameView.visibility = View.GONE
holder.bubbleTimeView.visibility = View.GONE
if (attributes.informationData.isDirect) {
holder.avatarImageView.visibility = View.GONE
} else {
holder.avatarImageView.visibility = View.VISIBLE
}
} else if (contentInBubble) {
holder.memberNameView.visibility = View.GONE holder.memberNameView.visibility = View.GONE
holder.timeView.visibility = View.GONE holder.timeView.visibility = View.GONE
holder.bubbleMemberNameView.visibility = View.VISIBLE holder.bubbleMemberNameView.visibility = View.VISIBLE
@ -109,7 +117,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
holder.timeView.visibility = View.GONE holder.timeView.visibility = View.GONE
holder.avatarImageView.setOnLongClickListener(null) holder.avatarImageView.setOnLongClickListener(null)
holder.memberNameView.setOnLongClickListener(null) holder.memberNameView.setOnLongClickListener(null)
if (attributes.informationData.showInformation /* && contentInBubble && attributes.informationData.sentByMe */) { if (attributes.informationData.showInformation && !hideSenderInformation()/* && contentInBubble && attributes.informationData.sentByMe */) {
holder.bubbleTimeView.visibility = View.VISIBLE holder.bubbleTimeView.visibility = View.VISIBLE
holder.bubbleTimeView.text = attributes.informationData.time holder.bubbleTimeView.text = attributes.informationData.time
holder.bubbleMemberNameView.visibility = View.VISIBLE holder.bubbleMemberNameView.visibility = View.VISIBLE
@ -203,13 +211,18 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
} }
private fun infoInBubbles(context: Context): Boolean { private fun infoInBubbles(context: Context): Boolean {
return messageBubbleAllowed(context) && BubbleThemeUtils.getBubbleStyle(context) == BubbleThemeUtils.BUBBLE_STYLE_BOTH return BubbleThemeUtils.getBubbleStyle(context) == BubbleThemeUtils.BUBBLE_STYLE_BOTH &&
(messageBubbleAllowed(context) || pseudoBubbleAllowed())
} }
override fun shouldReverseBubble(): Boolean { override fun shouldReverseBubble(): Boolean {
return attributes.informationData.sentByMe return attributes.informationData.sentByMe
} }
private fun hideSenderInformation(): Boolean {
return pseudoBubbleAllowed() && false
}
open fun getBubbleMargin(density: Float, reverseBubble: Boolean): Int { open fun getBubbleMargin(density: Float, reverseBubble: Boolean): Int {
return round(96*density).toInt() return round(96*density).toInt()
} }
@ -231,7 +244,13 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
*/ */
bubbleView.setPadding(0, 0, 0, 0) bubbleView.setPadding(0, 0, 0, 0)
} }
BubbleThemeUtils.BUBBLE_STYLE_START, BubbleThemeUtils.BUBBLE_STYLE_BOTH -> { BubbleThemeUtils.BUBBLE_STYLE_START,
BubbleThemeUtils.BUBBLE_STYLE_BOTH,
BubbleThemeUtils.BUBBLE_STYLE_BOTH_HIDDEN,
BubbleThemeUtils.BUBBLE_STYLE_START_HIDDEN -> {
val longPadding: Int
val shortPadding: Int
if (BubbleThemeUtils.drawsActualBubbles(bubbleStyle)) {
if (attributes.informationData.showInformation) { if (attributes.informationData.showInformation) {
bubbleView.setBackgroundResource(if (reverseBubble) R.drawable.msg_bubble_outgoing else R.drawable.msg_bubble_incoming) bubbleView.setBackgroundResource(if (reverseBubble) R.drawable.msg_bubble_outgoing else R.drawable.msg_bubble_incoming)
} else { } else {
@ -244,6 +263,12 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
) )
) )
bubbleView.backgroundTintList = tintColor bubbleView.backgroundTintList = tintColor
longPadding = 20
shortPadding = 8
} else {
longPadding = 10
shortPadding = 0//if (attributes.informationData.showInformation && !hideSenderInformation()) { 8 } else { 0 }
}
val density = bubbleView.resources.displayMetrics.density val density = bubbleView.resources.displayMetrics.density
// TODO 96 = 2 * avatar size? // TODO 96 = 2 * avatar size?
if (reverseBubble) { if (reverseBubble) {
@ -261,17 +286,17 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
// TODO padding? // TODO padding?
if (reverseBubble) { if (reverseBubble) {
bubbleView.setPaddingRelative( bubbleView.setPaddingRelative(
round(8 * density).toInt(), round(shortPadding * density).toInt(),
round(8 * density).toInt(), round(shortPadding * density).toInt(),
round(20 * density).toInt(), round(longPadding * density).toInt(),
round(8 * density).toInt() round(shortPadding * density).toInt()
) )
} else { } else {
bubbleView.setPaddingRelative( bubbleView.setPaddingRelative(
round(20 * density).toInt(), round(longPadding * density).toInt(),
round(8 * density).toInt(), round(shortPadding * density).toInt(),
round(8 * density).toInt(), round(shortPadding * density).toInt(),
round(8 * density).toInt() round(shortPadding * density).toInt()
) )
} }
} }

View File

@ -102,8 +102,21 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
fun updateMessageBubble(holder: H) { fun updateMessageBubble(holder: H) {
val bubbleStyleSetting = BubbleThemeUtils.getBubbleStyle(holder.checkableBackground.context) val bubbleStyleSetting = BubbleThemeUtils.getBubbleStyle(holder.checkableBackground.context)
val bubbleStyle = if (messageBubbleAllowed(holder.checkableBackground.context)) bubbleStyleSetting else BubbleThemeUtils.BUBBLE_STYLE_NONE val bubbleStyle = when {
val reverseBubble = shouldReverseBubble() && bubbleStyle == BubbleThemeUtils.BUBBLE_STYLE_BOTH 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) setBubbleLayout(holder, bubbleStyle, bubbleStyleSetting, reverseBubble)
} }
@ -116,6 +129,10 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
return false return false
} }
open fun pseudoBubbleAllowed(): Boolean {
return false
}
@CallSuper @CallSuper
open fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) { open fun setBubbleLayout(holder: H, bubbleStyle: String, bubbleStyleSetting: String, reverseBubble: Boolean) {
val defaultDirection = holder.readReceiptsView.resources.configuration.layoutDirection; val defaultDirection = holder.readReceiptsView.resources.configuration.layoutDirection;
@ -123,7 +140,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
val reverseDirection = if (defaultRtl) View.LAYOUT_DIRECTION_LTR else 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 // Always keep read receipts of others on other side for dual side bubbles
val dualBubbles = bubbleStyleSetting == BubbleThemeUtils.BUBBLE_STYLE_BOTH val dualBubbles = BubbleThemeUtils.drawsDualSide(bubbleStyleSetting)
val receiptParent = holder.readReceiptsView.parent val receiptParent = holder.readReceiptsView.parent
if (receiptParent is LinearLayout) { if (receiptParent is LinearLayout) {

View File

@ -84,6 +84,10 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
override fun getViewType() = STUB_ID override fun getViewType() = STUB_ID
override fun messageBubbleAllowed(context: Context): Boolean { override fun messageBubbleAllowed(context: Context): Boolean {
return false
}
override fun pseudoBubbleAllowed(): Boolean {
return true return true
} }

View File

@ -12,6 +12,13 @@ object BubbleThemeUtils {
const val BUBBLE_STYLE_NONE = "none" const val BUBBLE_STYLE_NONE = "none"
const val BUBBLE_STYLE_START = "start" const val BUBBLE_STYLE_START = "start"
const val BUBBLE_STYLE_BOTH = "both" const val BUBBLE_STYLE_BOTH = "both"
// Special case of BUBBLE_STYLE_BOTH, to allow non-bubble items align to the sender either way
// (not meant for user setting, but internal use)
const val BUBBLE_STYLE_BOTH_HIDDEN = "both_hidden"
// As above, so for single bubbles side
const val BUBBLE_STYLE_START_HIDDEN = "start_hidden"
private var mBubbleStyle: String = "" private var mBubbleStyle: String = ""
fun getBubbleStyle(context: Context): String { fun getBubbleStyle(context: Context): String {
@ -21,6 +28,14 @@ object BubbleThemeUtils {
return mBubbleStyle return mBubbleStyle
} }
fun drawsActualBubbles(bubbleStyle: String): Boolean {
return bubbleStyle == BUBBLE_STYLE_START || bubbleStyle == BUBBLE_STYLE_BOTH
}
fun drawsDualSide(bubbleStyle: String): Boolean {
return bubbleStyle == BUBBLE_STYLE_BOTH || bubbleStyle == BUBBLE_STYLE_BOTH_HIDDEN
}
fun invalidateBubbleStyle() { fun invalidateBubbleStyle() {
mBubbleStyle = "" mBubbleStyle = ""
} }