Fix sender name/label clipping in recycled conversation items.

Resolves signalapp/Signal-Android#14646
This commit is contained in:
jeffrey-signal
2026-04-15 11:50:11 -04:00
parent c30e3cc1b7
commit 2a8bd20bb0
5 changed files with 39 additions and 13 deletions
@@ -289,8 +289,7 @@ public class QuoteView extends ConstraintLayout implements RecipientForeverObser
QuoteViewColorTheme colorTheme = getColorTheme();
int foregroundColor = colorTheme.getForegroundColor(getContext());
authorView.setSender(name, foregroundColor);
authorView.setLabel(memberLabel, foregroundColor, colorTheme.getLabelBackgroundColor(getContext()));
authorView.bind(name, foregroundColor, memberLabel, foregroundColor, colorTheme.getLabelBackgroundColor(getContext()));
}
private boolean isStoryReply() {
@@ -2026,8 +2026,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (groupThread && !messageRecord.isOutgoing()) {
String senderName = recipient.getDisplayName(getContext());
int senderColor = colorizer.getIncomingGroupSenderColor(getContext(), messageRecord.getFromRecipient());
senderWithLabelView.setSender(senderName, senderColor);
senderWithLabelView.setLabel(conversationMessage.getMemberLabel());
senderWithLabelView.bind(senderName, senderColor, conversationMessage.getMemberLabel());
}
}
@@ -27,7 +27,7 @@ class SenderNameWithLabelView : AbstractComposeView {
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
init {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setViewCompositionStrategy(ViewCompositionStrategy.Default)
}
private var senderName: String by mutableStateOf("")
@@ -37,6 +37,28 @@ class SenderNameWithLabelView : AbstractComposeView {
private var memberLabel: MemberLabel? by mutableStateOf(null)
/**
* Sets sender and label state and forces fresh composition to avoid stale measurements from a previously bound item.
* Intended for use in a RecyclerView.
*/
fun bind(name: String, @ColorInt tintColor: Int, label: MemberLabel?) {
setSender(name, tintColor)
setLabel(label)
// AbstractComposeView caches measurements across RecyclerView recycling. This forces fresh composition on the
// next measure to avoid stale cached measurements during recycling that can cause sender name / label clipping.
disposeComposition()
}
/**
* Variant of [bind] with explicit label text/background colors.
*/
fun bind(name: String, @ColorInt tintColor: Int, label: MemberLabel?, @ColorInt textColor: Int, @ColorInt backgroundColor: Int) {
setSender(name, tintColor)
setLabel(label, textColor, backgroundColor)
disposeComposition()
}
fun setSender(name: String, @ColorInt tintColor: Int) {
senderName = name
senderColor = Color(tintColor)
@@ -631,8 +631,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
val tintColor = conversationContext.getColorizer().getIncomingGroupSenderColor(context, sender)
nameWithLabelView.apply {
setSender(sender.getDisplayName(context), tintColor)
setLabel(conversationMessage.memberLabel)
bind(sender.getDisplayName(context), tintColor, conversationMessage.memberLabel)
visible = true
}
+13 -6
View File
@@ -19,9 +19,9 @@
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/signal_colorPrimary" />
<org.thoughtcrime.securesms.conversation.v2.items.SenderNameWithLabelView
android:id="@+id/quote_author"
android:layout_width="wrap_content"
<FrameLayout
android:id="@+id/quote_author_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
@@ -34,7 +34,14 @@
app:layout_constraintStart_toEndOf="@id/quote_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"
app:layout_constraintVertical_chainStyle="packed" />
app:layout_constraintVertical_chainStyle="packed">
<org.thoughtcrime.securesms.conversation.v2.items.SenderNameWithLabelView
android:id="@+id/quote_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
<ViewStub
android:id="@+id/quote_attachment_name_stub"
@@ -47,7 +54,7 @@
app:layout_constraintBottom_toTopOf="@id/media_type"
app:layout_constraintEnd_toStartOf="@id/quote_missing_story_reaction_emoji"
app:layout_constraintStart_toEndOf="@id/quote_bar"
app:layout_constraintTop_toBottomOf="@id/quote_author"
app:layout_constraintTop_toBottomOf="@id/quote_author_holder"
app:layout_constraintWidth_default="spread"
app:layout_goneMarginBottom="8dp" />
@@ -142,7 +149,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="quote_attachment_name_stub,quote_author,quote_text,media_type" />
app:constraint_referenced_ids="quote_attachment_name_stub,quote_author_holder,quote_text,media_type" />
<ViewStub
android:id="@+id/quote_missing_text_stub"