Implemented conversation search.

You can now search for messages within a specific conversation.
This commit is contained in:
Greyson Parrelli
2019-02-01 09:06:59 -08:00
parent 10631d7e71
commit 9f04c28bfd
27 changed files with 965 additions and 152 deletions

View File

@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.util;
import android.arch.lifecycle.MutableLiveData;
import java.io.Closeable;
/**
* Implementation of {@link android.arch.lifecycle.LiveData} that will handle closing the contained
* {@link Closeable} when the value changes.
*/
public class CloseableLiveData<E extends Closeable> extends MutableLiveData<E> {
@Override
public void setValue(E value) {
setValue(value, true);
}
public void setValue(E value, boolean closePrevious) {
E previous = getValue();
if (previous != null && closePrevious) {
Util.close(previous);
}
super.setValue(value);
}
public void close() {
E value = getValue();
if (value != null) {
Util.close(value);
}
}
}

View File

@@ -0,0 +1,96 @@
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)
{
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<String> textTokens = Stream.of(normalizedText.split("\\s")).filter(s -> s.trim().length() > 0).toList();
List<Pair<Integer, Integer>> ranges = new LinkedList<>();
int textListIndex = 0;
int textCharIndex = 0;
for (String highlightToken : highlightTokens) {
for (int i = textListIndex; i < textTokens.size(); i++) {
if (textTokens.get(i).startsWith(highlightToken)) {
textListIndex = i + 1;
ranges.add(new Pair<>(textCharIndex, textCharIndex + highlightToken.length()));
textCharIndex += textTokens.get(i).length() + 1;
break;
}
textCharIndex += textTokens.get(i).length() + 1;
}
}
if (ranges.size() != highlightTokens.size()) {
return Collections.emptyList();
}
return ranges;
}
public interface StyleFactory {
CharacterStyle create();
}
}