diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java index 136dbfb371..d44efa9c37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java @@ -32,6 +32,7 @@ import androidx.core.view.GestureDetectorCompat; import androidx.core.view.ViewKt; import androidx.core.widget.TextViewCompat; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; import org.thoughtcrime.securesms.components.mention.MentionAnnotation; @@ -73,6 +74,7 @@ public class EmojiTextView extends AppCompatTextView { private boolean isJumbomoji; private boolean forceJumboEmoji; private boolean renderSpoilers; + private boolean shrinkWrap; private MentionRendererDelegate mentionRendererDelegate; private SpoilerRendererDelegate spoilerRendererDelegate; @@ -96,6 +98,7 @@ public class EmojiTextView extends AppCompatTextView { measureLastLine = a.getBoolean(R.styleable.EmojiTextView_measureLastLine, false); forceJumboEmoji = a.getBoolean(R.styleable.EmojiTextView_emoji_forceJumbo, false); renderSpoilers = a.getBoolean(R.styleable.EmojiTextView_emoji_renderSpoilers, false); + shrinkWrap = a.getBoolean(R.styleable.EmojiTextView_emoji_shrinkWrap, false); a.recycle(); a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.textSize }); @@ -224,6 +227,25 @@ public class EmojiTextView extends AppCompatTextView { widthMeasureSpec = applyWidthMeasureRoundingFix(widthMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int mode = MeasureSpec.getMode(widthMeasureSpec); + if (shrinkWrap && getLayout() != null && mode == MeasureSpec.AT_MOST) { + Layout layout = getLayout(); + + float maxLineWidth = 0f; + for (int i = 0; i < layout.getLineCount(); i++) { + if (layout.getLineWidth(i) > maxLineWidth) { + maxLineWidth = layout.getLineWidth(i); + } + } + + int desiredWidth = (int) maxLineWidth + getPaddingLeft() + getPaddingRight(); + if (getMeasuredWidth() > desiredWidth) { + widthMeasureSpec = MeasureSpec.makeMeasureSpec(desiredWidth, mode); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + CharSequence text = getText(); if (getLayout() == null || !measureLastLine || text == null || text.length() == 0) { lastLineWidth = -1; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout.kt new file mode 100644 index 0000000000..b51192149f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/ShrinkWrapLinearLayout.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.conversation.v2.items + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.LinearLayoutCompat + +/** + * Custom LinearLayoutCompat that will intercept EXACTLY measure-specs and + * overwrite them with AT_MOST. This guarantees that wrap_content is respected + * when the Layout is within a constraintlayout. + */ +class ShrinkWrapLinearLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : LinearLayoutCompat(context, attrs) { + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val shrinkWrapWidthSpec = shrinkWrapWidthMeasureSpec(widthMeasureSpec) + super.onMeasure(shrinkWrapWidthSpec, heightMeasureSpec) + } + + private fun shrinkWrapWidthMeasureSpec(widthMeasureSpec: Int): Int { + val mode = MeasureSpec.getMode(widthMeasureSpec) + val size = MeasureSpec.getSize(widthMeasureSpec) + + return if (mode == MeasureSpec.EXACTLY) { + MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST) + } else { + widthMeasureSpec + } + } +} 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 431e8e9d79..01af7deb45 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 @@ -56,7 +56,7 @@ tools:visibility="gone" /> - - + + app:layout_constraintEnd_toEndOf="@id/conversation_item_body_wrapper" + app:layout_constraintTop_toTopOf="@id/conversation_item_footer_date" /> 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 68e7f61335..38e4379106 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 @@ -60,6 +60,7 @@ app:emoji_maxLength="1000" app:emoji_renderMentions="true" app:emoji_renderSpoilers="true" + app:emoji_shrinkWrap="true" app:measureLastLine="true" app:scaleEmojis="true" tools:text="Mango pickle lorem ipsum" /> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 069a557d54..afd7a354f3 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -153,6 +153,7 @@ +