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:
parent
0d7c0bc29c
commit
037a333c37
|
@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = ""
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue