mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Update conversations list UI.
This commit is contained in:
committed by
Cody Henthorne
parent
c84de8fa60
commit
e09d162c1e
@@ -5,10 +5,14 @@ import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -17,15 +21,20 @@ import androidx.core.content.ContextCompat;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class FromTextView extends EmojiTextView {
|
||||
public class FromTextView extends SimpleEmojiTextView {
|
||||
|
||||
private static final String TAG = Log.tag(FromTextView.class);
|
||||
|
||||
private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL);
|
||||
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif", Typeface.NORMAL);
|
||||
|
||||
public FromTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -45,20 +54,9 @@ public class FromTextView extends EmojiTextView {
|
||||
public void setText(Recipient recipient, boolean read, @Nullable String suffix) {
|
||||
String fromString = recipient.getDisplayName(getContext());
|
||||
|
||||
int typeface;
|
||||
|
||||
if (!read) {
|
||||
typeface = Typeface.BOLD;
|
||||
} else {
|
||||
typeface = Typeface.NORMAL;
|
||||
}
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
SpannableString fromSpan = new SpannableString(fromString);
|
||||
fromSpan.setSpan(new StyleSpan(typeface), 0, builder.length(),
|
||||
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
SpannableString fromSpan = new SpannableString(fromString);
|
||||
fromSpan.setSpan(getFontSpan(!read), 0, fromSpan.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
|
||||
if (recipient.isSelf()) {
|
||||
builder.append(getContext().getString(R.string.note_to_self));
|
||||
@@ -85,4 +83,8 @@ public class FromTextView extends EmojiTextView {
|
||||
|
||||
return mutedDrawable;
|
||||
}
|
||||
|
||||
private CharacterStyle getFontSpan(boolean isBold) {
|
||||
return isBold ? SpanUtil.getBoldSpan() : SpanUtil.getNormalSpan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import androidx.appcompat.widget.AppCompatTextView
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.whispersystems.libsignal.util.guava.Optional
|
||||
|
||||
open class SingleLineEmojiTextView @JvmOverloads constructor(
|
||||
open class SimpleEmojiTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
@@ -15,20 +15,16 @@ open class SingleLineEmojiTextView @JvmOverloads constructor(
|
||||
|
||||
private var bufferType: BufferType? = null
|
||||
|
||||
init {
|
||||
maxLines = 1
|
||||
}
|
||||
|
||||
override fun setText(text: CharSequence?, type: BufferType?) {
|
||||
bufferType = type
|
||||
val candidates = if (isInEditMode) null else EmojiProvider.getCandidates(text)
|
||||
if (SignalStore.settings().isPreferSystemEmoji || candidates == null || candidates.size() == 0) {
|
||||
super.setText(Optional.fromNullable(text).or(""), BufferType.NORMAL)
|
||||
} else {
|
||||
val newContent = if (width == 0) {
|
||||
val newContent = if (width == 0 || maxLines == -1) {
|
||||
text
|
||||
} else {
|
||||
TextUtils.ellipsize(text, paint, width.toFloat(), TextUtils.TruncateAt.END, false, null)
|
||||
TextUtils.ellipsize(text, paint, (width * maxLines).toFloat(), TextUtils.TruncateAt.END, false, null)
|
||||
}
|
||||
|
||||
val newCandidates = if (isInEditMode) null else EmojiProvider.getCandidates(newContent)
|
||||
@@ -47,9 +43,4 @@ open class SingleLineEmojiTextView @JvmOverloads constructor(
|
||||
setText(text, bufferType ?: BufferType.NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setMaxLines(maxLines: Int) {
|
||||
check(maxLines == 1) { "setMaxLines: $maxLines != 1" }
|
||||
super.setMaxLines(maxLines)
|
||||
}
|
||||
}
|
||||
@@ -836,8 +836,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
if (messageRequestAccepted) {
|
||||
linkifyMessageBody(styledText, batchSelected.isEmpty());
|
||||
}
|
||||
styledText = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.YELLOW), styledText, searchQuery);
|
||||
styledText = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), styledText, searchQuery);
|
||||
styledText = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.YELLOW), styledText, searchQuery, SearchUtil.STRICT);
|
||||
styledText = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), styledText, searchQuery, SearchUtil.STRICT);
|
||||
|
||||
if (hasExtraText(messageRecord)) {
|
||||
bodyText.setOverflowText(getLongMessageSpan(messageRecord));
|
||||
|
||||
@@ -101,7 +101,7 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
||||
v.setLayoutParams(new FrameLayout.LayoutParams(1, ViewUtil.dpToPx(100)));
|
||||
return new PlaceholderViewHolder(v);
|
||||
} else if (viewType == TYPE_HEADER) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.conversation_list_item_header, parent, false);
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.dsl_section_header, parent, false);
|
||||
return new HeaderViewHolder(v);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown type! " + viewType);
|
||||
@@ -297,7 +297,7 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
||||
|
||||
public HeaderViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
headerText = (TextView) itemView;
|
||||
headerText = itemView.findViewById(R.id.section_header);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
@@ -66,6 +65,7 @@ import org.thoughtcrime.securesms.util.Debouncer;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.SearchUtil;
|
||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -102,6 +102,8 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
private long lastSeen;
|
||||
private ThreadRecord thread;
|
||||
private boolean batchMode;
|
||||
private Locale locale;
|
||||
private String highlightSubstring;
|
||||
|
||||
private int unreadCount;
|
||||
private AvatarImageView contactPhotoImage;
|
||||
@@ -158,17 +160,19 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
observeDisplayBody(null);
|
||||
setSubjectViewText(null);
|
||||
|
||||
this.selectedThreads = selectedThreads;
|
||||
this.threadId = thread.getThreadId();
|
||||
this.glideRequests = glideRequests;
|
||||
this.unreadCount = thread.getUnreadCount();
|
||||
this.lastSeen = thread.getLastSeen();
|
||||
this.thread = thread;
|
||||
this.selectedThreads = selectedThreads;
|
||||
this.threadId = thread.getThreadId();
|
||||
this.glideRequests = glideRequests;
|
||||
this.unreadCount = thread.getUnreadCount();
|
||||
this.lastSeen = thread.getLastSeen();
|
||||
this.thread = thread;
|
||||
this.locale = locale;
|
||||
this.highlightSubstring = highlightSubstring;
|
||||
|
||||
if (highlightSubstring != null) {
|
||||
String name = recipient.get().isSelf() ? getContext().getString(R.string.note_to_self) : recipient.get().getDisplayName(getContext());
|
||||
|
||||
this.fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), name, highlightSubstring));
|
||||
this.fromView.setText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getBoldSpan, name, highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
} else {
|
||||
this.fromView.setText(recipient.get(), thread.isRead());
|
||||
}
|
||||
@@ -178,10 +182,6 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
|
||||
observeDisplayBody(getThreadDisplayBody(getContext(), thread));
|
||||
|
||||
this.subjectView.setTypeface(thread.isRead() ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
||||
this.subjectView.setTextColor(thread.isRead() ? ContextCompat.getColor(getContext(), R.color.signal_text_secondary)
|
||||
: ContextCompat.getColor(getContext(), R.color.signal_text_primary));
|
||||
|
||||
if (thread.getDate() > 0) {
|
||||
CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate());
|
||||
dateView.setText(date);
|
||||
@@ -213,13 +213,13 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
observeDisplayBody(null);
|
||||
setSubjectViewText(null);
|
||||
|
||||
this.selectedThreads = Collections.emptySet();
|
||||
this.glideRequests = glideRequests;
|
||||
this.selectedThreads = Collections.emptySet();
|
||||
this.glideRequests = glideRequests;
|
||||
this.locale = locale;
|
||||
this.highlightSubstring = highlightSubstring;
|
||||
|
||||
|
||||
fromView.setText(contact);
|
||||
fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), new SpannableString(fromView.getText()), highlightSubstring));
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.getE164().or(""), highlightSubstring));
|
||||
fromView.setText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getBoldSpan, new SpannableString(contact.getDisplayName(getContext())), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getBoldSpan, contact.getE164().or(""), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
dateView.setText("");
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
@@ -241,11 +241,13 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
observeDisplayBody(null);
|
||||
setSubjectViewText(null);
|
||||
|
||||
this.selectedThreads = Collections.emptySet();
|
||||
this.glideRequests = glideRequests;
|
||||
this.selectedThreads = Collections.emptySet();
|
||||
this.glideRequests = glideRequests;
|
||||
this.locale = locale;
|
||||
this.highlightSubstring = highlightSubstring;
|
||||
|
||||
fromView.setText(recipient.get(), true);
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), messageResult.getBodySnippet(), highlightSubstring));
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getBoldSpan, messageResult.getBodySnippet(), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs()));
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
@@ -346,10 +348,10 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
|
||||
private void setThumbnailSnippet(ThreadRecord thread) {
|
||||
if (thread.getSnippetUri() != null) {
|
||||
this.thumbnailView.setVisibility(View.VISIBLE);
|
||||
this.thumbnailView.setVisibility(VISIBLE);
|
||||
this.thumbnailView.setImageResource(glideRequests, thread.getSnippetUri());
|
||||
} else {
|
||||
this.thumbnailView.setVisibility(View.GONE);
|
||||
this.thumbnailView.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,11 +403,12 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
}
|
||||
|
||||
private void setUnreadIndicator(ThreadRecord thread) {
|
||||
if ((thread.isOutgoing() && !thread.isForcedUnread()) || thread.isRead()) {
|
||||
if ((thread.isOutgoing() && !thread.isForcedUnread()) || thread.isRead() || unreadCount == 0) {
|
||||
unreadIndicator.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
String count = unreadCount > 100 ? String.valueOf(unreadCount) : "+99";
|
||||
unreadIndicator.setText(unreadCount > 0 ? String.valueOf(unreadCount) : " ");
|
||||
unreadIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@@ -417,14 +420,18 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
return;
|
||||
}
|
||||
|
||||
fromView.setText(recipient, unreadCount == 0);
|
||||
if (highlightSubstring != null) {
|
||||
String name = recipient.isSelf() ? getContext().getString(R.string.note_to_self) : recipient.getDisplayName(getContext());
|
||||
fromView.setText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getBoldSpan, new SpannableString(name), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
} else {
|
||||
fromView.setText(recipient, unreadCount == 0);
|
||||
}
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, !batchMode);
|
||||
setRippleColor(recipient);
|
||||
}
|
||||
|
||||
private static @NonNull LiveData<SpannableString> getThreadDisplayBody(@NonNull Context context, @NonNull ThreadRecord thread) {
|
||||
int defaultTint = thread.isRead() ? ContextCompat.getColor(context, R.color.signal_text_secondary)
|
||||
: ContextCompat.getColor(context, R.color.signal_text_primary);
|
||||
int defaultTint = ContextCompat.getColor(context, R.color.signal_text_secondary);
|
||||
|
||||
if (!thread.isMessageRequestAccepted()) {
|
||||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint);
|
||||
@@ -520,8 +527,7 @@ public final class ConversationListItem extends ConstraintLayout
|
||||
: recipient.getShortDisplayName(context)) + ": ";
|
||||
|
||||
SpannableString spannable = new SpannableString(sender + body);
|
||||
spannable.setSpan(new TextAppearanceSpan(context, read ? R.style.Signal_Text_Preview_Medium_Secondary
|
||||
: R.style.Signal_Text_Preview_Medium_Primary),
|
||||
spannable.setSpan(SpanUtil.getBoldSpan(),
|
||||
0,
|
||||
sender.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
@@ -96,7 +96,7 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
|
||||
@Override
|
||||
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position, int type) {
|
||||
return new HeaderViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.search_result_list_divider, parent, false));
|
||||
.inflate(R.layout.dsl_section_header, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,7 +198,7 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
|
||||
|
||||
public HeaderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
titleView = itemView.findViewById(R.id.label);
|
||||
titleView = itemView.findViewById(R.id.section_header);
|
||||
}
|
||||
|
||||
public void bind(int headerType) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.annimon.stream.Stream;
|
||||
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -19,10 +20,14 @@ import java.util.Locale;
|
||||
|
||||
public class SearchUtil {
|
||||
|
||||
public static final int STRICT = 0;
|
||||
public static final int MATCH_ALL = 1;
|
||||
|
||||
public static Spannable getHighlightedSpan(@NonNull Locale locale,
|
||||
@NonNull StyleFactory styleFactory,
|
||||
@Nullable String text,
|
||||
@Nullable String highlight)
|
||||
@Nullable String highlight,
|
||||
int matchMode)
|
||||
{
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return new SpannableString("");
|
||||
@@ -30,13 +35,14 @@ public class SearchUtil {
|
||||
|
||||
text = text.replaceAll("\n", " ");
|
||||
|
||||
return getHighlightedSpan(locale, styleFactory, new SpannableString(text), highlight);
|
||||
return getHighlightedSpan(locale, styleFactory, new SpannableString(text), highlight, matchMode);
|
||||
}
|
||||
|
||||
public static Spannable getHighlightedSpan(@NonNull Locale locale,
|
||||
@NonNull StyleFactory styleFactory,
|
||||
@Nullable Spannable text,
|
||||
@Nullable String highlight)
|
||||
@Nullable String highlight,
|
||||
int matchMode)
|
||||
{
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return new SpannableString("");
|
||||
@@ -47,8 +53,24 @@ public class SearchUtil {
|
||||
return text;
|
||||
}
|
||||
|
||||
List<Pair<Integer, Integer>> ranges = getHighlightRanges(locale, text.toString(), highlight);
|
||||
SpannableString spanned = new SpannableString(text);
|
||||
List<Pair<Integer, Integer>> ranges;
|
||||
|
||||
switch (matchMode) {
|
||||
case STRICT:
|
||||
ranges = getStrictHighlightRanges(locale, text.toString(), highlight);
|
||||
break;
|
||||
case MATCH_ALL:
|
||||
ranges = getHighlightRanges(locale, text.toString(), highlight);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidParameterException("match mode must be STRICT or MATCH_ALL: " + matchMode);
|
||||
}
|
||||
if (matchMode == STRICT) {
|
||||
ranges = getStrictHighlightRanges(locale, text.toString(), highlight);
|
||||
} else {
|
||||
ranges = getHighlightRanges(locale, text.toString(), highlight);
|
||||
}
|
||||
|
||||
for (Pair<Integer, Integer> range : ranges) {
|
||||
spanned.setSpan(styleFactory.create(), range.first(), range.second(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
@@ -57,9 +79,9 @@ public class SearchUtil {
|
||||
return spanned;
|
||||
}
|
||||
|
||||
static List<Pair<Integer, Integer>> getHighlightRanges(@NonNull Locale locale,
|
||||
@NonNull String text,
|
||||
@NonNull String highlight)
|
||||
static List<Pair<Integer, Integer>> getStrictHighlightRanges(@NonNull Locale locale,
|
||||
@NonNull String text,
|
||||
@NonNull String highlight)
|
||||
{
|
||||
if (text.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
@@ -97,6 +119,39 @@ public class SearchUtil {
|
||||
return ranges;
|
||||
}
|
||||
|
||||
static List<Pair<Integer, Integer>> getHighlightRanges(@NonNull Locale locale,
|
||||
@NonNull String text,
|
||||
@NonNull String highlight)
|
||||
{
|
||||
if (text.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String normalizedText = text.toLowerCase(locale);
|
||||
String normalizedHighlight = highlight.toLowerCase(locale);
|
||||
List<String> highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList();
|
||||
|
||||
List<Pair<Integer, Integer>> ranges = new LinkedList<>();
|
||||
|
||||
int lastHighlightEndIndex = 0;
|
||||
|
||||
for (String highlightToken : highlightTokens) {
|
||||
int index = 0;
|
||||
lastHighlightEndIndex = 0;
|
||||
|
||||
while (index != -1) {
|
||||
index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex);
|
||||
if (index != -1) {
|
||||
lastHighlightEndIndex = index + highlightToken.length();
|
||||
ranges.add(new Pair<>(index, lastHighlightEndIndex));
|
||||
index = lastHighlightEndIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
public interface StyleFactory {
|
||||
CharacterStyle create();
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@ import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.DynamicDrawableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
@@ -34,6 +37,9 @@ public final class SpanUtil {
|
||||
|
||||
public static final String SPAN_PLACE_HOLDER = "<<<SPAN>>>";
|
||||
|
||||
private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL);
|
||||
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif", Typeface.NORMAL);
|
||||
|
||||
public static CharSequence italic(CharSequence sequence) {
|
||||
return italic(sequence, sequence.length());
|
||||
}
|
||||
@@ -205,4 +211,20 @@ public final class SpanUtil {
|
||||
builder.replace(index, index + SpanUtil.SPAN_PLACE_HOLDER.length(), span);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static CharacterStyle getBoldSpan() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
return new TypefaceSpan(BOLD_TYPEFACE);
|
||||
} else {
|
||||
return new StyleSpan(Typeface.BOLD);
|
||||
}
|
||||
}
|
||||
|
||||
public static CharacterStyle getNormalSpan() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
return new TypefaceSpan(LIGHT_TYPEFACE);
|
||||
} else {
|
||||
return new StyleSpan(Typeface.NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners android:radius="@dimen/unread_count_bubble_radius" />
|
||||
|
||||
<solid android:color="@color/core_ultramarine" />
|
||||
|
||||
<stroke
|
||||
android:color="@color/core_grey_95"
|
||||
android:width="2.5dp"/>
|
||||
|
||||
</shape>
|
||||
14
app/src/main/res/drawable/unread_count_background_new.xml
Normal file
14
app/src/main/res/drawable/unread_count_background_new.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners android:radius="@dimen/unread_count_bubble_radius" />
|
||||
|
||||
<solid android:color="@color/core_ultramarine" />
|
||||
|
||||
<stroke
|
||||
android:color="@color/core_white"
|
||||
android:width="2.5dp"/>
|
||||
|
||||
</shape>
|
||||
@@ -25,6 +25,7 @@
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarStyle"
|
||||
android:visibility="gone"
|
||||
app:contentInsetStart="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
@@ -34,14 +35,11 @@
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/toolbar_icon"
|
||||
android:layout_width="58dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:contentDescription="@string/conversation_list_settings_shortcut"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:layout_marginStart="@dimen/toolbar_avatar_margin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -50,11 +48,11 @@
|
||||
<View
|
||||
android:id="@+id/unread_payments_indicator"
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginBottom="29dp"
|
||||
android:layout_height="13dp"
|
||||
android:background="@drawable/unread_count_background"
|
||||
android:alpha="0"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/toolbar_icon"
|
||||
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
|
||||
tools:alpha="1" />
|
||||
@@ -63,7 +61,7 @@
|
||||
android:id="@+id/conversation_list_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginStart="26dp"
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/signal_text_primary"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
tools:text="Chats" />
|
||||
@@ -4,56 +4,58 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:layout_height="86dp"
|
||||
android:background="@drawable/conversation_list_item_background"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/container"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/conversation_list_item_avatar"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"
|
||||
android:foreground="@drawable/contact_photo_background"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:src="@drawable/ic_contact_picture" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/conversation_list_item_unread_indicator"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/unread_count_background"
|
||||
android:layout_height="@dimen/unread_count_bubble_diameter"
|
||||
android:background="@drawable/unread_count_background_new"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:gravity="center"
|
||||
android:paddingStart="7dp"
|
||||
android:paddingEnd="7dp"
|
||||
android:minWidth="@dimen/unread_count_bubble_diameter"
|
||||
android:padding="5sp"
|
||||
android:minHeight="@dimen/unread_count_bubble_diameter"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintEnd_toEndOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_avatar"
|
||||
tools:text="1" />
|
||||
android:layout_marginBottom="27dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_avatar_barrier"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_avatar"
|
||||
tools:text="9" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.FromTextView
|
||||
android:id="@+id/conversation_list_item_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:drawablePadding="5dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:fontFamily="sans-serif"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/signal_text_primary"
|
||||
app:layout_constraintStart_toEndOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_thumbnail"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintBottom_toTopOf="@id/conversation_list_item_summary"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Peter Parker" />
|
||||
|
||||
@@ -65,24 +67,24 @@
|
||||
android:paddingTop="2dp"
|
||||
android:visibility="gone"
|
||||
app:useSmallIcon="true"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_summary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_summary"
|
||||
app:layout_constraintTop_toBottomOf="@id/conversation_list_item_name"
|
||||
app:layout_constraintStart_toStartOf="@id/conversation_list_item_name"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.SingleLineEmojiTextView
|
||||
<org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView
|
||||
android:id="@+id/conversation_list_item_summary"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:maxLines="2"
|
||||
android:lines="2"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
android:layout_marginEnd="12dp"
|
||||
tools:text="I'll send those photos over to the Bugle ASAP."
|
||||
app:layout_constraintTop_toBottomOf="@id/conversation_list_item_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintStart_toEndOf="@id/conversation_list_item_alert"
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_status_barrier"/>
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_summary_barrier"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.TypingIndicatorView
|
||||
android:id="@+id/conversation_list_item_typing_indicator"
|
||||
@@ -97,18 +99,17 @@
|
||||
|
||||
<org.thoughtcrime.securesms.components.ThumbnailView
|
||||
android:id="@+id/conversation_list_item_thumbnail"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_toStartOf="@+id/conversation_list_item_date"
|
||||
android:contentDescription="@string/conversation_activity__attachment_thumbnail"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_metadata_barrier"/>
|
||||
tools:visibility="gone"
|
||||
app:thumbnail_radius="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/conversation_list_item_date"/>
|
||||
|
||||
<TextView
|
||||
android:id="@id/conversation_list_item_date"
|
||||
@@ -117,52 +118,58 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/signal_icon_tint_secondary"
|
||||
tools:text="Now"
|
||||
android:layout_marginTop="2dp"
|
||||
tools:text="10:00 am"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_name"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/conversation_list_item_archived"
|
||||
style="@style/Signal.Text.Caption"
|
||||
<LinearLayout
|
||||
android:id="@+id/conversation_list_item_status_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/archived_indicator_background"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:text="@string/conversation_list_item_view__archived"
|
||||
android:textColor="@color/core_white"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_status"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_status"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/conversation_list_item_date">
|
||||
|
||||
<org.thoughtcrime.securesms.components.DeliveryStatusView
|
||||
android:id="@+id/conversation_list_item_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="5dp"
|
||||
app:layout_goneMarginEnd="0dp"
|
||||
app:iconColor="@color/signal_icon_tint_secondary"
|
||||
app:layout_constraintEnd_toStartOf="@+id/conversation_list_item_archived"
|
||||
app:layout_constraintTop_toTopOf="@id/conversation_list_item_summary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/conversation_list_item_summary"/>
|
||||
<org.thoughtcrime.securesms.components.DeliveryStatusView
|
||||
android:id="@+id/conversation_list_item_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:iconColor="@color/signal_icon_tint_secondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/conversation_list_item_archived"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/archived_indicator_background"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:text="@string/conversation_list_item_view__archived"
|
||||
android:textColor="@color/core_white"
|
||||
tools:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/conversation_list_item_metadata_barrier"
|
||||
android:id="@+id/conversation_list_item_avatar_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="conversation_list_item_status,conversation_list_item_archived,conversation_list_item_date"/>
|
||||
android:layout_height="match_parent"
|
||||
app:barrierDirection="end"
|
||||
app:constraint_referenced_ids="conversation_list_item_avatar"
|
||||
app:barrierMargin="@dimen/unread_count_bubble_barrier_margin"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/conversation_list_item_status_barrier"
|
||||
android:id="@+id/conversation_list_item_summary_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="conversation_list_item_status,conversation_list_item_thumbnail"/>
|
||||
|
||||
app:barrierAllowsGoneWidgets="false"
|
||||
app:constraint_referenced_ids="conversation_list_item_thumbnail,conversation_list_item_status_container" />
|
||||
|
||||
</org.thoughtcrime.securesms.conversationlist.ConversationListItem>
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
|
||||
android:textStyle="bold"
|
||||
tools:text="Section Header" />
|
||||
</FrameLayout>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="24dp"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
tools:text="@string/CameraContacts_recent_contacts"/>
|
||||
@@ -27,7 +27,7 @@
|
||||
tools:alpha="1"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.SingleLineEmojiTextView
|
||||
<org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView
|
||||
android:id="@+id/recipient"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -43,6 +43,7 @@
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="@color/core_white"
|
||||
android:visibility="gone"
|
||||
android:maxLines="1"
|
||||
app:drawableStartCompat="@drawable/ic_arrow_right_16"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
||||
4
app/src/main/res/values-ldrtl/dimens.xml
Normal file
4
app/src/main/res/values-ldrtl/dimens.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="unread_count_bubble_barrier_margin">-4dp</dimen>
|
||||
</resources>
|
||||
@@ -29,4 +29,6 @@
|
||||
<dimen name="conversation_settings_button_strip_button_padding">16dp</dimen>
|
||||
|
||||
<dimen name="avatar_picker_image_width">160dp</dimen>
|
||||
|
||||
<dimen name="toolbar_avatar_margin">34dp</dimen>
|
||||
</resources>
|
||||
@@ -108,8 +108,9 @@
|
||||
<dimen name="conversation_list_fragment_archive_padding">16dp</dimen>
|
||||
<dimen name="contact_selection_actions_tap_area">10dp</dimen>
|
||||
|
||||
<dimen name="unread_count_bubble_radius">13sp</dimen>
|
||||
<dimen name="unread_count_bubble_diameter">26sp</dimen>
|
||||
<dimen name="unread_count_bubble_radius">12.5dp</dimen>
|
||||
<dimen name="unread_count_bubble_diameter">25dp</dimen>
|
||||
<dimen name="unread_count_bubble_barrier_margin">4dp</dimen>
|
||||
|
||||
<dimen name="insights_modal_title_margin_top">41dp</dimen>
|
||||
<dimen name="insights_modal_description_margin_top">12dp</dimen>
|
||||
@@ -200,4 +201,6 @@
|
||||
<dimen name="conversation_settings_button_strip_button_padding">12dp</dimen>
|
||||
|
||||
<dimen name="avatar_picker_image_width">100dp</dimen>
|
||||
|
||||
<dimen name="toolbar_avatar_margin">26dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -14,6 +14,64 @@ public class SearchUtilTest {
|
||||
|
||||
private static final Locale LOCALE = Locale.ENGLISH;
|
||||
|
||||
@Test
|
||||
public void getStrictHighlightRanges_singleHighlightToken() {
|
||||
String text = "abc";
|
||||
String highlight = "a";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 1)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStrictHighlightRanges_singleHighlightTokenWithNewLines() {
|
||||
String text = "123\n\n\nabc";
|
||||
String highlight = "a";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(6, 7)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStrictHighlightRanges_multipleHighlightTokens() {
|
||||
String text = "a bc";
|
||||
String highlight = "a b";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 1), new Pair<>(2, 3)), result);
|
||||
|
||||
|
||||
text = "abc def";
|
||||
highlight = "ab de";
|
||||
result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 2), new Pair<>(4, 6)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStrictHighlightRanges_onlyHighlightPrefixes() {
|
||||
String text = "abc";
|
||||
String highlight = "b";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
|
||||
text = "abc";
|
||||
highlight = "c";
|
||||
result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStrictHighlightRanges_resultNotInFirstToken() {
|
||||
String text = "abc def ghi";
|
||||
String highlight = "gh";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getStrictHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(8, 10)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_singleHighlightToken() {
|
||||
String text = "abc";
|
||||
@@ -24,51 +82,20 @@ public class SearchUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_singleHighlightTokenWithNewLines() {
|
||||
String text = "123\n\n\nabc";
|
||||
public void getHighlightRanges_singleHighlightTokenMultipleMatches() {
|
||||
String text = "blabla";
|
||||
String highlight = "a";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(6, 7)), result);
|
||||
assertEquals(Arrays.asList(new Pair<>(2, 3), new Pair<>(5, 6)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_multipleHighlightTokens() {
|
||||
String text = "a bc";
|
||||
String highlight = "a b";
|
||||
public void getHighlightRanges_multipleHighlightTokenMultipleMatches() {
|
||||
String text = "test search test string";
|
||||
String highlight = "test str";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 1), new Pair<>(2, 3)), result);
|
||||
|
||||
|
||||
text = "abc def";
|
||||
highlight = "ab de";
|
||||
result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 2), new Pair<>(4, 6)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_onlyHighlightPrefixes() {
|
||||
String text = "abc";
|
||||
String highlight = "b";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
|
||||
text = "abc";
|
||||
highlight = "c";
|
||||
result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_resultNotInFirstToken() {
|
||||
String text = "abc def ghi";
|
||||
String highlight = "gh";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(8, 10)), result);
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 4), new Pair<>(12, 16), new Pair<>(17,20)), result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user