From 40008bddc770f757b816da190795ed5ba6ce9699 Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Mon, 24 Nov 2025 19:21:45 -0500 Subject: [PATCH] Fix position of message reaction overlay in RTL. --- .../ConversationReactionDelegate.java | 28 +++++++++++++++---- .../ConversationReactionOverlay.java | 7 ----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java index 949f1516e8..612148b9f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation; import android.app.Activity; import android.graphics.PointF; import android.view.MotionEvent; +import android.view.View; import androidx.annotation.NonNull; @@ -12,7 +13,7 @@ import org.thoughtcrime.securesms.util.views.Stub; /** * Delegate class that mimics the ConversationReactionOverlay public API - * + *

* This allows us to properly stub out the ConversationReactionOverlay View class while still * respecting listeners and other positional information that can be set BEFORE we want to actually * resolve the view. @@ -35,10 +36,10 @@ public final class ConversationReactionDelegate { } public void show(@NonNull Activity activity, - @NonNull Recipient conversationRecipient, - @NonNull ConversationMessage conversationMessage, - boolean isNonAdminInAnnouncementGroup, - @NonNull SelectedConversationModel selectedConversationModel) + @NonNull Recipient conversationRecipient, + @NonNull ConversationMessage conversationMessage, + boolean isNonAdminInAnnouncementGroup, + @NonNull SelectedConversationModel selectedConversationModel) { resolveOverlay().show(activity, conversationRecipient, conversationMessage, lastSeenDownPoint, isNonAdminInAnnouncementGroup, selectedConversationModel); } @@ -91,7 +92,22 @@ public final class ConversationReactionDelegate { } private @NonNull ConversationReactionOverlay resolveOverlay() { - ConversationReactionOverlay overlay = overlayStub.get(); + boolean wasAlreadyResolved = overlayStub.resolved(); + ConversationReactionOverlay overlay = overlayStub.get(); + + if (!wasAlreadyResolved && (overlay.getWidth() == 0 || overlay.getHeight() == 0)) { + // force immediate measurement and layout after ViewStub inflation to ensure proper dimensions before first use + // without doing this, the overlay child views will be positioned off screen in RTL layout direction because of negative values. + + View parent = (View) overlay.getParent(); + if (parent != null) { + int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.EXACTLY); + overlay.measure(widthSpec, heightSpec); + overlay.layout(0, 0, overlay.getMeasuredWidth(), overlay.getMeasuredHeight()); + } + } + overlay.requestFitSystemWindows(); overlay.setOnHideListener(onHideListener); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java index b9ee71bf9a..f8d1b8397e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; @@ -16,7 +15,6 @@ import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; -import android.view.Window; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.FrameLayout; @@ -45,10 +43,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.WindowUtil; import java.util.ArrayList; import java.util.List; @@ -68,7 +64,6 @@ public final class ConversationReactionOverlay extends FrameLayout { private final Boundary verticalScrubBoundary = new Boundary(); private final PointF deadzoneTouchPoint = new PointF(); - private Activity activity; private Recipient conversationRecipient; private MessageRecord messageRecord; private SelectedConversationModel selectedConversationModel; @@ -196,8 +191,6 @@ public final class ConversationReactionOverlay extends FrameLayout { setVisibility(View.INVISIBLE); - this.activity = activity; - ViewKt.doOnLayout(this, v -> { showAfterLayout(activity, conversationMessage, lastSeenDownPoint, isMessageOnLeft); return Unit.INSTANCE;