Implement single line timestamps on conversation items.
This commit is contained in:
parent
dc1e56de4e
commit
fe8fcb1394
9 changed files with 134 additions and 36 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue