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 59a778ac26..a99f4750e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java @@ -206,7 +206,7 @@ public final class ConversationReactionOverlay extends FrameLayout { contextMenu = new ConversationContextMenu(dropdownAnchor, getMenuActionItems(conversationMessage)); - conversationItem.setX(selectedConversationModel.getBubbleX()); + conversationItem.setX(selectedConversationModel.getSnapshotMetrics().getSnapshotOffset()); conversationItem.setY(selectedConversationModel.getItemY() + selectedConversationModel.getBubbleY() - statusBarHeight); Bitmap conversationItemSnapshot = selectedConversationModel.getBitmap(); @@ -215,7 +215,7 @@ public final class ConversationReactionOverlay extends FrameLayout { int overlayHeight = getHeight() - bottomNavigationBarHeight; int bubbleWidth = selectedConversationModel.getBubbleWidth(); - float endX = selectedConversationModel.getBubbleX(); + float endX = selectedConversationModel.getSnapshotMetrics().getSnapshotOffset(); float endY = conversationItem.getY(); float endApparentTop = endY; float endScale = 1f; @@ -346,7 +346,7 @@ public final class ConversationReactionOverlay extends FrameLayout { float offsetX = isMessageOnLeft ? scrubberRight + menuPadding : scrubberX - contextMenu.getMaxWidth() - menuPadding; contextMenu.show((int) offsetX, (int) Math.min(backgroundView.getY(), overlayHeight - contextMenu.getMaxHeight())); } else { - float contentX = selectedConversationModel.getBubbleX(); + float contentX = selectedConversationModel.getSnapshotMetrics().getContextMenuPadding(); float offsetX = isMessageOnLeft ? contentX : -contextMenu.getMaxWidth() + contentX + bubbleWidth; float menuTop = endApparentTop + (conversationItemSnapshot.getHeight() * endScale); @@ -859,7 +859,7 @@ public final class ConversationReactionOverlay extends FrameLayout { ObjectAnimator itemXAnim = new ObjectAnimator(); itemXAnim.setProperty(View.X); - itemXAnim.setFloatValues(selectedConversationModel.getBubbleX()); + itemXAnim.setFloatValues(selectedConversationModel.getSnapshotMetrics().getSnapshotOffset()); itemXAnim.setTarget(conversationItem); itemXAnim.setDuration(duration); animators.add(itemXAnim); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/SelectedConversationModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/SelectedConversationModel.kt index 0a23fe3b6f..d47a9b810a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/SelectedConversationModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/SelectedConversationModel.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation import android.graphics.Bitmap import android.net.Uri import android.view.View +import org.thoughtcrime.securesms.conversation.v2.items.InteractiveConversationElement /** * Contains information on a single selected conversation item. This is used when transitioning @@ -12,10 +13,10 @@ data class SelectedConversationModel( val bitmap: Bitmap, val itemX: Float, val itemY: Float, - val bubbleX: Float, val bubbleY: Float, val bubbleWidth: Int, val audioUri: Uri? = null, val isOutgoing: Boolean, - val focusedView: View? + val focusedView: View?, + val snapshotMetrics: InteractiveConversationElement.SnapshotMetrics ) 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 0cc56afb62..a2cc7d8f32 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 @@ -2868,15 +2868,18 @@ class ConversationFragment : val focusedView = if (container.isInputShowing || !container.isKeyboardShowing) null else itemView.rootView.findFocus() val bodyBubble = target.bubbleView val selectedConversationModel = SelectedConversationModel( - snapshot, - itemView.x, - itemView.y + binding.conversationItemRecycler.translationY, - if (target.getSnapshotStrategy() != null) itemView.x else bodyBubble.x, - bodyBubble.y, - bodyBubble.width, - audioUri, - messageRecord.isOutgoing, - focusedView + bitmap = snapshot, + itemX = itemView.x, + itemY = itemView.y + binding.conversationItemRecycler.translationY, + bubbleY = bodyBubble.y, + bubbleWidth = bodyBubble.width, + audioUri = audioUri, + isOutgoing = messageRecord.isOutgoing, + focusedView = focusedView, + snapshotMetrics = target.getSnapshotStrategy()?.snapshotMetrics ?: InteractiveConversationElement.SnapshotMetrics( + snapshotOffset = bodyBubble.x, + contextMenuPadding = bodyBubble.x + ) ) bodyBubble.visibility = View.INVISIBLE diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement.kt index d25a864630..699fd20b40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.conversation.v2.items import android.graphics.Canvas import android.view.View import android.view.ViewGroup +import androidx.annotation.Px import androidx.recyclerview.widget.RecyclerView import org.thoughtcrime.securesms.conversation.ConversationMessage import org.thoughtcrime.securesms.util.ProjectionList @@ -50,5 +51,21 @@ interface InteractiveConversationElement : ChatColorsDrawable.ChatColorsDrawable interface SnapshotStrategy { fun snapshot(canvas: Canvas) + + val snapshotMetrics: SnapshotMetrics } + + /** + * Metrics describing how the snapshot is oriented. + * + * @param snapshotOffset Describes the horizontal offset of the top-level that will be captured. + * This is used to ensure the content is translated appropriately. + * @param contextMenuPadding Describes the distance between the edge of the view's container to the + * edge of the content (for example, the bubble). This is used to position + * the context menu. + */ + data class SnapshotMetrics( + @Px val snapshotOffset: Float, + @Px val contextMenuPadding: Float + ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2TextOnlySnapshotStrategy.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2TextOnlySnapshotStrategy.kt index 0cb9a3b502..888a5b1ddf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2TextOnlySnapshotStrategy.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2TextOnlySnapshotStrategy.kt @@ -31,6 +31,11 @@ class V2TextOnlySnapshotStrategy( binding.senderBadge ) + override val snapshotMetrics = InteractiveConversationElement.SnapshotMetrics( + snapshotOffset = 0f, + contextMenuPadding = binding.conversationItemBodyWrapper.x + ) + override fun snapshot(canvas: Canvas) { val originalScales = viewsToRestoreScale.associateWith { Pair(it.scaleX, it.scaleY) } viewsToRestoreScale.forEach {