diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 3d17c2529e..36d7663438 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -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 } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout.kt index dbe79585ea..b7816edb1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout.kt @@ -18,20 +18,24 @@ class V2ConversationItemLayout @JvmOverloads constructor( attrs: AttributeSet? = null ) : ConstraintLayout(context, attrs) { - private var onMeasureListener: OnMeasureListener? = null + private var onMeasureListeners: Set = 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) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape.kt index a63a0afce2..07615dc5da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape.kt @@ -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 = 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) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt index 7992dff6a1..9b576d61ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder.kt @@ -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>( 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>( presentFooterExpiry(shape) presentAlert() presentSender() + presentReactions() - val (topPadding, bottomPadding) = shapeDelegate.spacing - ViewUtil.setPaddingTop(itemView, topPadding.toInt()) - ViewUtil.setPaddingBottom(itemView, bottomPadding.toInt()) + itemView.updateLayoutParams { + topMargin = shape.topPadding.toInt() + bottomMargin = shape.bottomPadding.toInt() + } } override fun getAdapterPosition(recyclerView: RecyclerView): Int = bindingAdapterPosition @@ -245,6 +250,16 @@ class V2TextOnlyViewHolder>( } } + 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>( 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) + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java index 18ceaec90d..0f71f9a661 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java @@ -63,9 +63,9 @@ public class ReactionsConversationView extends LinearLayout { removeAllViews(); } - public void setReactions(@NonNull List records, int bubbleWidth) { + public boolean setReactions(@NonNull List 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 buildSortedReactionsList(@NonNull List records) { diff --git a/app/src/main/res/layout/v2_conversation_item_text_only_incoming.xml b/app/src/main/res/layout/v2_conversation_item_text_only_incoming.xml index 2626a97ca3..5c96ab02e5 100644 --- a/app/src/main/res/layout/v2_conversation_item_text_only_incoming.xml +++ b/app/src/main/res/layout/v2_conversation_item_text_only_incoming.xml @@ -62,7 +62,6 @@ android:orientation="vertical" app:cardElevation="0dp" app:layout_constrainedWidth="true" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toEndOf="@id/contact_photo" @@ -157,7 +156,6 @@ android:layout_height="wrap_content" android:layout_marginTop="-4dp" android:orientation="horizontal" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/conversation_item_body_wrapper" app:layout_constraintTop_toBottomOf="@id/conversation_item_body_wrapper" app:rcv_outgoing="false" /> diff --git a/app/src/main/res/layout/v2_conversation_item_text_only_outgoing.xml b/app/src/main/res/layout/v2_conversation_item_text_only_outgoing.xml index 58c60d5b5c..8504696df5 100644 --- a/app/src/main/res/layout/v2_conversation_item_text_only_outgoing.xml +++ b/app/src/main/res/layout/v2_conversation_item_text_only_outgoing.xml @@ -35,7 +35,6 @@ android:layout_marginStart="48dp" app:cardElevation="0dp" app:layout_constrainedWidth="true" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/conversation_item_alert" app:layout_constraintHorizontal_bias="1" app:layout_constraintStart_toStartOf="parent" @@ -152,7 +151,6 @@ android:layout_height="wrap_content" android:layout_marginTop="-4dp" android:orientation="horizontal" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/conversation_item_body_wrapper" app:layout_constraintTop_toBottomOf="@id/conversation_item_body_wrapper" app:rcv_outgoing="false" />