2019-02-01 17:06:59 +00:00
|
|
|
package org.thoughtcrime.securesms.util;
|
|
|
|
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
import android.text.Spannable;
|
|
|
|
import android.text.SpannableString;
|
|
|
|
import android.text.Spanned;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.text.style.CharacterStyle;
|
|
|
|
|
|
|
|
import com.annimon.stream.Stream;
|
|
|
|
|
|
|
|
import org.whispersystems.libsignal.util.Pair;
|
|
|
|
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
|
|
|
|
|
|
|
public class SearchUtil {
|
|
|
|
|
|
|
|
public static Spannable getHighlightedSpan(@NonNull Locale locale,
|
|
|
|
@NonNull StyleFactory styleFactory,
|
|
|
|
@Nullable String text,
|
|
|
|
@Nullable String highlight)
|
|
|
|
{
|
|
|
|
if (TextUtils.isEmpty(text)) {
|
|
|
|
return new SpannableString("");
|
|
|
|
}
|
|
|
|
|
|
|
|
text = text.replaceAll("\n", " ");
|
|
|
|
|
|
|
|
return getHighlightedSpan(locale, styleFactory, new SpannableString(text), highlight);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Spannable getHighlightedSpan(@NonNull Locale locale,
|
|
|
|
@NonNull StyleFactory styleFactory,
|
|
|
|
@Nullable Spannable text,
|
|
|
|
@Nullable String highlight)
|
|
|
|
{
|
|
|
|
if (TextUtils.isEmpty(text)) {
|
|
|
|
return new SpannableString("");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (TextUtils.isEmpty(highlight)) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Pair<Integer, Integer>> ranges = getHighlightRanges(locale, text.toString(), highlight);
|
|
|
|
SpannableString spanned = new SpannableString(text);
|
|
|
|
|
|
|
|
for (Pair<Integer, Integer> range : ranges) {
|
|
|
|
spanned.setSpan(styleFactory.create(), range.first(), range.second(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return spanned;
|
|
|
|
}
|
|
|
|
|
|
|
|
static List<Pair<Integer, Integer>> getHighlightRanges(@NonNull Locale locale,
|
|
|
|
@NonNull String text,
|
|
|
|
@NonNull String highlight)
|
|
|
|
{
|
2019-02-28 19:55:12 +00:00
|
|
|
if (text.length() == 0) {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
|
|
|
|
2019-02-01 17:06:59 +00:00
|
|
|
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<>();
|
|
|
|
|
2019-02-28 19:55:12 +00:00
|
|
|
int lastHighlightEndIndex = 0;
|
2019-02-01 17:06:59 +00:00
|
|
|
|
|
|
|
for (String highlightToken : highlightTokens) {
|
2019-02-28 19:55:12 +00:00
|
|
|
int index;
|
|
|
|
|
|
|
|
do {
|
|
|
|
index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex);
|
|
|
|
lastHighlightEndIndex = index + highlightToken.length();
|
|
|
|
} while (index > 0 && !Character.isWhitespace(normalizedText.charAt(index - 1)));
|
|
|
|
|
|
|
|
if (index >= 0) {
|
|
|
|
ranges.add(new Pair<>(index, lastHighlightEndIndex));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index < 0 || lastHighlightEndIndex >= normalizedText.length()) {
|
|
|
|
break;
|
2019-02-01 17:06:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ranges.size() != highlightTokens.size()) {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface StyleFactory {
|
|
|
|
CharacterStyle create();
|
|
|
|
}
|
|
|
|
}
|