Implement single line timestamps on conversation items.

This commit is contained in:
Lucio Maciel 2021-08-12 14:25:52 -03:00 committed by Cody Henthorne
parent dc1e56de4e
commit fe8fcb1394
9 changed files with 134 additions and 36 deletions

View file

@ -1,12 +1,16 @@
package org.thoughtcrime.securesms.components.emoji;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.text.Annotation;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextDirectionHeuristic;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
@ -19,6 +23,7 @@ import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.content.ContextCompat;
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;
@ -36,16 +41,19 @@ public class EmojiTextView extends AppCompatTextView {
private static final char ELLIPSIS = '…';
private boolean forceCustom;
private CharSequence previousText;
private BufferType previousBufferType;
private float originalFontSize;
private boolean useSystemEmoji;
private boolean sizeChangeInProgress;
private int maxLength;
private CharSequence overflowText;
private CharSequence previousOverflowText;
private boolean renderMentions;
private boolean forceCustom;
private CharSequence previousText;
private BufferType previousBufferType;
private float originalFontSize;
private boolean useSystemEmoji;
private boolean sizeChangeInProgress;
private int maxLength;
private CharSequence overflowText;
private CharSequence previousOverflowText;
private boolean renderMentions;
private boolean measureLastLine;
private int lastLineWidth = -1;
private TextDirectionHeuristic textDirection;
private MentionRendererDelegate mentionRendererDelegate;
@ -61,10 +69,11 @@ public class EmojiTextView extends AppCompatTextView {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0);
scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false);
maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1);
forceCustom = a.getBoolean(R.styleable.EmojiTextView_emoji_forceCustom, false);
renderMentions = a.getBoolean(R.styleable.EmojiTextView_emoji_renderMentions, true);
scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false);
maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1);
forceCustom = a.getBoolean(R.styleable.EmojiTextView_emoji_forceCustom, false);
renderMentions = a.getBoolean(R.styleable.EmojiTextView_emoji_renderMentions, true);
measureLastLine = a.getBoolean(R.styleable.EmojiTextView_measureLastLine, false);
a.recycle();
a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textSize});
@ -74,6 +83,8 @@ public class EmojiTextView extends AppCompatTextView {
if (renderMentions) {
mentionRendererDelegate = new MentionRendererDelegate(getContext(), ContextCompat.getColor(getContext(), R.color.transparent_black_20));
}
textDirection = getLayoutDirection() == LAYOUT_DIRECTION_LTR ? TextDirectionHeuristics.FIRSTSTRONG_RTL : TextDirectionHeuristics.ANYRTL_LTR;
}
@Override
@ -139,6 +150,34 @@ public class EmojiTextView extends AppCompatTextView {
}
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
CharSequence text = getText();
if (!measureLastLine || text == null || text.length() == 0) {
lastLineWidth = -1;
} else {
Layout layout = getLayout();
int lines = layout.getLineCount();
int start = layout.getLineStart(lines - 1);
int count = text.length() - start;
if ((getLayoutDirection() == LAYOUT_DIRECTION_LTR && textDirection.isRtl(text, start, count)) ||
(getLayoutDirection() == LAYOUT_DIRECTION_RTL && !textDirection.isRtl(text, start, count))) {
lastLineWidth = getMeasuredWidth();
} else {
lastLineWidth = (int) getPaint().measureText(text, start, text.length());
}
}
}
public int getLastLineWidth() {
return lastLineWidth;
}
public boolean isSingleLine() {
return getLayout().getLineCount() == 1;
}
public void setOverflowText(@Nullable CharSequence overflowText) {
this.overflowText = overflowText;
setText(previousText, BufferType.SPANNABLE);

View file

@ -166,13 +166,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private static final Rect SWIPE_RECT = new Rect();
private ConversationMessage conversationMessage;
private MessageRecord messageRecord;
private Locale locale;
private boolean groupThread;
private LiveRecipient recipient;
private GlideRequests glideRequests;
private ValueAnimator pulseOutlinerAlphaAnimator;
private ConversationMessage conversationMessage;
private MessageRecord messageRecord;
private Optional<MessageRecord> nextMessageRecord;
private Locale locale;
private boolean groupThread;
private LiveRecipient recipient;
private GlideRequests glideRequests;
private ValueAnimator pulseOutlinerAlphaAnimator;
protected ConversationItemBodyBubble bodyBubble;
protected View reply;
@ -202,9 +203,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private Stub<ViewOnceMessageView> revealableStub;
private @Nullable EventListener eventListener;
private int defaultBubbleColor;
private int defaultBubbleColorForWallpaper;
private int measureCalls;
private int defaultBubbleColor;
private int defaultBubbleColorForWallpaper;
private int measureCalls;
private boolean updatingFooter;
private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener();
private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener();
@ -300,6 +302,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
this.conversationMessage = conversationMessage;
this.messageRecord = conversationMessage.getMessageRecord();
this.nextMessageRecord = nextMessageRecord;
this.locale = locale;
this.glideRequests = glideRequests;
this.batchSelected = batchSelected;
@ -386,6 +389,35 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
}
}
if (!updatingFooter && !isCaptionlessMms(messageRecord) && !isViewOnceMessage(messageRecord) && isFooterVisible(messageRecord, nextMessageRecord, groupThread)) {
int footerWidth = footer.getMeasuredWidth();
int availableWidth = getAvailableMessageBubbleWidth(bodyText);
if (bodyText.isSingleLine()) {
int maxBubbleWidth = hasBigImageLinkPreview(messageRecord) || hasThumbnail(messageRecord) ? readDimen(R.dimen.media_bubble_max_width) : getMaxBubbleWidth();
int bodyMargins = ViewUtil.getLeftMargin(bodyText) + ViewUtil.getRightMargin(bodyText);
int sizeWithMargins = bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins;
int minSize = Math.min(maxBubbleWidth, Math.max(bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins, bodyBubble.getMeasuredWidth()));
if (hasQuote(messageRecord) && sizeWithMargins < availableWidth) {
ViewUtil.setTopMargin(footer, readDimen(R.dimen.message_bubble_same_line_footer_bottom_margin));
needsMeasure = true;
updatingFooter = true;
} else if (sizeWithMargins != bodyText.getMeasuredWidth() && sizeWithMargins <= minSize) {
bodyBubble.getLayoutParams().width = minSize;
ViewUtil.setTopMargin(footer, readDimen(R.dimen.message_bubble_same_line_footer_bottom_margin));
needsMeasure = true;
updatingFooter = true;
}
}
if (!updatingFooter && bodyText.getLastLineWidth() + ViewUtil.dpToPx(6) + footerWidth <= bodyText.getMeasuredWidth()) {
ViewUtil.setTopMargin(footer, readDimen(R.dimen.message_bubble_same_line_footer_bottom_margin));
updatingFooter = true;
needsMeasure = true;
} else if (!updatingFooter && ViewUtil.getTopMargin(footer) == readDimen(R.dimen.message_bubble_same_line_footer_bottom_margin)) {
ViewUtil.setTopMargin(footer, readDimen(R.dimen.message_bubble_default_footer_bottom_margin));
needsMeasure = true;
}
}
if (hasSharedContact(messageRecord)) {
int contactWidth = sharedContactStub.get().getMeasuredWidth();
int availableWidth = getAvailableMessageBubbleWidth(sharedContactStub.get());
@ -396,7 +428,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
}
}
if (!hasNoBubble(messageRecord)) {
if (hasAudio(messageRecord)) {
ConversationItemFooter activeFooter = getActiveFooter(messageRecord);
int availableWidth = getAvailableMessageBubbleWidth(footer);
@ -415,6 +447,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
}
} else {
measureCalls = 0;
updatingFooter = false;
}
}
@ -449,6 +482,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
return availableWidth;
}
private int getMaxBubbleWidth() {
int paddings = getPaddingLeft() + getPaddingRight() + ViewUtil.getLeftMargin(bodyBubble) + ViewUtil.getRightMargin(bodyBubble);
if (groupThread && !messageRecord.isOutgoing()) {
paddings += contactPhoto.getLayoutParams().width + ViewUtil.getLeftMargin(contactPhoto) + ViewUtil.getRightMargin(contactPhoto);
}
return getMeasuredWidth() - paddings;
}
private void initializeAttributes() {
defaultBubbleColor = ContextCompat.getColor(context, R.color.signal_background_secondary);
defaultBubbleColorForWallpaper = ContextCompat.getColor(context, R.color.conversation_item_wallpaper_bubble_color);
@ -532,6 +573,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private void setBubbleState(MessageRecord messageRecord, @NonNull Recipient recipient, boolean hasWallpaper, @NonNull Colorizer colorizer) {
this.hasWallpaper = hasWallpaper;
ViewUtil.updateLayoutParams(bodyBubble, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
bodyText.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_text_primary));
bodyText.setLinkTextColor(ContextCompat.getColor(getContext(), R.color.signal_text_primary));
@ -1312,6 +1354,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private void setFooter(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, @NonNull Locale locale, boolean isGroupThread, boolean hasWallpaper) {
ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ViewUtil.setTopMargin(footer, readDimen(R.dimen.message_bubble_default_footer_bottom_margin));
footer.setVisibility(GONE);
stickerFooter.setVisibility(GONE);

View file

@ -243,6 +243,10 @@ public final class ViewUtil {
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
}
public static int getTopMargin(@NonNull View view) {
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin;
}
public static void setLeftMargin(@NonNull View view, int margin) {
if (isLtr(view)) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;

View file

@ -177,11 +177,12 @@
android:textColorLink="@color/signal_text_primary"
app:emoji_maxLength="1000"
app:scaleEmojis="true"
app:measureLastLine="true"
tools:text="Mango pickle lorem ipsum" />
<org.thoughtcrime.securesms.components.ConversationItemFooter
android:id="@+id/conversation_item_footer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
android:layout_marginTop="-4dp"
@ -201,7 +202,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
android:layout_marginTop="3dp"
android:layout_marginTop="-5dp"
android:layout_marginBottom="@dimen/message_bubble_bottom_padding"
android:layout_gravity="end"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="@dimen/message_bubble_horizontal_padding"

View file

@ -107,16 +107,18 @@
android:textColorLink="@color/signal_text_primary"
app:emoji_maxLength="1000"
app:scaleEmojis="true"
app:measureLastLine="true"
tools:text="Mango pickle lorem ipsum" />
<org.thoughtcrime.securesms.components.ConversationItemFooter
android:id="@+id/conversation_item_footer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
android:layout_marginTop="-4dp"
android:layout_marginEnd="@dimen/message_bubble_horizontal_padding"
android:layout_marginBottom="@dimen/message_bubble_bottom_padding"
android:layout_gravity="end"
android:alpha="0.7"
android:clipChildren="false"
android:clipToPadding="false"

View file

@ -129,12 +129,14 @@
android:textColorLink="@color/conversation_item_sent_text_primary_color"
app:emoji_maxLength="1000"
app:scaleEmojis="true"
app:measureLastLine="true"
tools:text="Mango pickle lorem ipsum" />
<org.thoughtcrime.securesms.components.ConversationItemFooter
android:id="@+id/conversation_item_footer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
android:layout_marginTop="-5dp"
android:layout_marginEnd="@dimen/message_bubble_horizontal_padding"
@ -151,16 +153,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="3dp"
android:paddingStart="@dimen/message_bubble_horizontal_padding"
android:paddingEnd="@dimen/message_bubble_horizontal_padding"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:layout_marginTop="-5dp"
android:layout_marginBottom="@dimen/message_bubble_bottom_padding"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="@dimen/message_bubble_horizontal_padding"
android:paddingTop="3dp"
android:paddingEnd="@dimen/message_bubble_horizontal_padding"
android:paddingBottom="3dp"
android:visibility="gone"
app:footer_mode="outgoing"
app:footer_icon_color="@color/signal_icon_tint_secondary"
app:footer_mode="outgoing"
app:footer_reveal_dot_color="@color/signal_icon_tint_secondary"
app:footer_text_color="@color/signal_text_secondary" />

View file

@ -60,11 +60,12 @@
android:textColorLink="@color/conversation_item_sent_text_primary_color"
app:emoji_maxLength="1000"
app:scaleEmojis="true"
app:measureLastLine="true"
tools:text="Mango pickle lorem ipsum" />
<org.thoughtcrime.securesms.components.ConversationItemFooter
android:id="@+id/conversation_item_footer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_horizontal_padding"
android:layout_marginTop="-5dp"

View file

@ -112,6 +112,7 @@
</declare-styleable>
<declare-styleable name="EmojiTextView">
<attr name="measureLastLine" format="boolean" />
<attr name="scaleEmojis" format="boolean" />
<attr name="emoji_maxLength" format="integer" />
<attr name="emoji_forceCustom" format="boolean" />

View file

@ -54,6 +54,8 @@
<dimen name="media_bubble_sticker_dimens">175dp</dimen>
<dimen name="media_bubble_gif_width">240dp</dimen>
<dimen name="message_audio_width">200dp</dimen>
<dimen name="message_bubble_default_footer_bottom_margin">-4dp</dimen>
<dimen name="message_bubble_same_line_footer_bottom_margin">-24dp</dimen>
<dimen name="media_picker_folder_width">175dp</dimen>
<dimen name="media_picker_item_width">85dp</dimen>