mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-03-02 15:36:32 +00:00
Fix CI V2 layout bounds when item has a reaction.
This commit is contained in:
committed by
Greyson Parrelli
parent
3040b70100
commit
4590655dc5
@@ -1118,7 +1118,7 @@ class ConversationFragment :
|
||||
|
||||
adapter.onHasWallpaperChanged(wallpaperEnabled)
|
||||
dateHeaderDecoration.hasWallpaper = wallpaperEnabled
|
||||
|
||||
|
||||
val navColor = if (wallpaperEnabled) {
|
||||
R.color.conversation_navigation_wallpaper
|
||||
} else {
|
||||
@@ -1992,7 +1992,7 @@ class ConversationFragment :
|
||||
} else if (wasAtBottom && !isAtBottom) {
|
||||
ViewUtil.fadeIn(binding.composeDivider, 500)
|
||||
}
|
||||
|
||||
|
||||
wasAtBottom = isAtBottom
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,24 @@ class V2ConversationItemLayout @JvmOverloads constructor(
|
||||
attrs: AttributeSet? = null
|
||||
) : ConstraintLayout(context, attrs) {
|
||||
|
||||
private var onMeasureListener: OnMeasureListener? = null
|
||||
private var onMeasureListeners: Set<OnMeasureListener> = emptySet()
|
||||
|
||||
/**
|
||||
* Set the onMeasureListener to be invoked by this view whenever onMeasure is called.
|
||||
*/
|
||||
fun setOnMeasureListener(onMeasureListener: OnMeasureListener?) {
|
||||
this.onMeasureListener = onMeasureListener
|
||||
fun addOnMeasureListener(onMeasureListener: OnMeasureListener) {
|
||||
this.onMeasureListeners += onMeasureListener
|
||||
}
|
||||
|
||||
fun removeOnMeasureListener(onMeasureListener: OnMeasureListener) {
|
||||
this.onMeasureListeners -= onMeasureListener
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
onMeasureListener?.onPreMeasure()
|
||||
onMeasureListeners.forEach { it.onPreMeasure() }
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
val remeasure = onMeasureListener?.onPostMeasure() ?: false
|
||||
val remeasure = onMeasureListeners.map { it.onPostMeasure() }.any { it }
|
||||
if (remeasure) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class V2ConversationItemShape(
|
||||
private var smallRadius: Float = 4f.dp
|
||||
|
||||
private var collapsedSpacing: Float = 1f.dp
|
||||
private var defaultSpacing: Float = 8f.dp
|
||||
private var defaultSpacing: Float = 6f.dp
|
||||
|
||||
private val clusterTimeout = 3.minutes
|
||||
}
|
||||
@@ -41,9 +41,6 @@ class V2ConversationItemShape(
|
||||
)
|
||||
private set
|
||||
|
||||
var spacing: Pair<Float, Float> = Pair(defaultSpacing, defaultSpacing)
|
||||
private set
|
||||
|
||||
/**
|
||||
* Sets the message spacing and corners based off the given information. This
|
||||
* updates the class state.
|
||||
@@ -59,25 +56,21 @@ class V2ConversationItemShape(
|
||||
|
||||
if (isSingularMessage(currentMessage, previousMessage, nextMessage, isGroupThread)) {
|
||||
setBodyBubbleCorners(isLtr, bigRadius, bigRadius, bigRadius, bigRadius)
|
||||
spacing = Pair(defaultSpacing, defaultSpacing)
|
||||
return MessageShape.SINGLE
|
||||
} else if (isStartOfMessageCluster(currentMessage, previousMessage, isGroupThread)) {
|
||||
val bottomEnd = if (currentMessage.isOutgoing) smallRadius else bigRadius
|
||||
val bottomStart = if (currentMessage.isOutgoing) bigRadius else smallRadius
|
||||
setBodyBubbleCorners(isLtr, bigRadius, bigRadius, bottomEnd, bottomStart)
|
||||
spacing = Pair(defaultSpacing, collapsedSpacing)
|
||||
return MessageShape.START
|
||||
} else if (isEndOfMessageCluster(currentMessage, nextMessage)) {
|
||||
val topStart = if (currentMessage.isOutgoing) bigRadius else smallRadius
|
||||
val topEnd = if (currentMessage.isOutgoing) smallRadius else bigRadius
|
||||
setBodyBubbleCorners(isLtr, topStart, topEnd, bigRadius, bigRadius)
|
||||
spacing = Pair(collapsedSpacing, defaultSpacing)
|
||||
return MessageShape.END
|
||||
} else {
|
||||
val start = if (currentMessage.isOutgoing) bigRadius else smallRadius
|
||||
val end = if (currentMessage.isOutgoing) smallRadius else bigRadius
|
||||
setBodyBubbleCorners(isLtr, start, end, end, start)
|
||||
spacing = Pair(collapsedSpacing, collapsedSpacing)
|
||||
return MessageShape.MIDDLE
|
||||
}
|
||||
}
|
||||
@@ -156,25 +149,28 @@ class V2ConversationItemShape(
|
||||
return abs(currentMessage.dateSent - previousMessage.dateSent) <= clusterTimeout.inWholeMilliseconds
|
||||
}
|
||||
|
||||
enum class MessageShape {
|
||||
enum class MessageShape(
|
||||
val topPadding: Float,
|
||||
val bottomPadding: Float
|
||||
) {
|
||||
/**
|
||||
* This message stands alone.
|
||||
*/
|
||||
SINGLE,
|
||||
SINGLE(defaultSpacing, defaultSpacing),
|
||||
|
||||
/**
|
||||
* This message is the start of a cluster
|
||||
*/
|
||||
START,
|
||||
START(defaultSpacing, collapsedSpacing),
|
||||
|
||||
/**
|
||||
* This message is the end of a cluster
|
||||
*/
|
||||
END,
|
||||
END(collapsedSpacing, defaultSpacing),
|
||||
|
||||
/**
|
||||
* This message is in the middle of a cluster
|
||||
*/
|
||||
MIDDLE
|
||||
MIDDLE(collapsedSpacing, collapsedSpacing)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package org.thoughtcrime.securesms.conversation.v2.items
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
@@ -24,7 +26,6 @@ import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.Projection
|
||||
import org.thoughtcrime.securesms.util.ProjectionList
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.hasNoBubble
|
||||
@@ -81,8 +82,10 @@ class V2TextOnlyViewHolder<Model : MappingModel<Model>>(
|
||||
override val contactPhotoHolderView: View? = binding.senderPhoto
|
||||
override val badgeImageView: View? = binding.senderBadge
|
||||
|
||||
private var reactionMeasureListener: ReactionMeasureListener = ReactionMeasureListener()
|
||||
|
||||
init {
|
||||
binding.root.setOnMeasureListener(footerDelegate)
|
||||
binding.root.addOnMeasureListener(footerDelegate)
|
||||
}
|
||||
|
||||
override fun bind(model: Model) {
|
||||
@@ -116,10 +119,12 @@ class V2TextOnlyViewHolder<Model : MappingModel<Model>>(
|
||||
presentFooterExpiry(shape)
|
||||
presentAlert()
|
||||
presentSender()
|
||||
presentReactions()
|
||||
|
||||
val (topPadding, bottomPadding) = shapeDelegate.spacing
|
||||
ViewUtil.setPaddingTop(itemView, topPadding.toInt())
|
||||
ViewUtil.setPaddingBottom(itemView, bottomPadding.toInt())
|
||||
itemView.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = shape.topPadding.toInt()
|
||||
bottomMargin = shape.bottomPadding.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAdapterPosition(recyclerView: RecyclerView): Int = bindingAdapterPosition
|
||||
@@ -245,6 +250,16 @@ class V2TextOnlyViewHolder<Model : MappingModel<Model>>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentReactions() {
|
||||
if (conversationMessage.messageRecord.reactions.isEmpty()) {
|
||||
binding.conversationItemReactions.clear()
|
||||
binding.root.removeOnMeasureListener(reactionMeasureListener)
|
||||
} else {
|
||||
reactionMeasureListener.onPostMeasure()
|
||||
binding.root.addOnMeasureListener(reactionMeasureListener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentFooterBackground(shape: V2ConversationItemShape.MessageShape) {
|
||||
if (!binding.conversationItemBody.isJumbomoji ||
|
||||
!conversationContext.hasWallpaper() ||
|
||||
@@ -347,4 +362,12 @@ class V2TextOnlyViewHolder<Model : MappingModel<Model>>(
|
||||
override fun disallowSwipe(latestDownX: Float, latestDownY: Float): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private inner class ReactionMeasureListener : V2ConversationItemLayout.OnMeasureListener {
|
||||
override fun onPreMeasure() = Unit
|
||||
|
||||
override fun onPostMeasure(): Boolean {
|
||||
return binding.conversationItemReactions.setReactions(conversationMessage.messageRecord.reactions, binding.conversationItemBodyWrapper.width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ public class ReactionsConversationView extends LinearLayout {
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setReactions(@NonNull List<ReactionRecord> records, int bubbleWidth) {
|
||||
public boolean setReactions(@NonNull List<ReactionRecord> records, int bubbleWidth) {
|
||||
if (records.equals(this.records) && this.bubbleWidth == bubbleWidth) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.records.clear();
|
||||
@@ -102,6 +102,8 @@ public class ReactionsConversationView extends LinearLayout {
|
||||
ViewUtil.setLeftMargin(this, OUTER_MARGIN);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static @NonNull List<Reaction> buildSortedReactionsList(@NonNull List<ReactionRecord> records) {
|
||||
|
||||
Reference in New Issue
Block a user