highlight search term in search results

This commit is contained in:
Daniel Gultsch 2018-04-28 23:50:35 +02:00
parent effeb7b585
commit 3011f875eb
49 changed files with 59 additions and 5 deletions

View file

@ -140,7 +140,7 @@
transform="translate(0,-2)"> transform="translate(0,-2)">
<g <g
id="g3759" id="g3759"
style="fill:#c64545;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)"> style="fill:#ad4545;fill-opacity:1;stroke:none;fill-rule:nonzero;filter:url(#filter3811)">
<path <path
style="display:none" style="display:none"
d="m 8,6 c 2,2 4,6 4,10 L 16,6 z" d="m 8,6 c 2,2 4,6 4,10 L 16,6 z"

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -207,6 +207,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
} else { } else {
MessageSearchTask.cancelRunningTasks(); MessageSearchTask.cancelRunningTasks();
this.messages.clear(); this.messages.clear();
messageListAdapter.setHighlightedTerm(null);
messageListAdapter.notifyDataSetChanged(); messageListAdapter.notifyDataSetChanged();
changeBackground(false, false); changeBackground(false, false);
} }
@ -216,6 +217,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
public void onSearchResultsAvailable(String term, List<Message> messages) { public void onSearchResultsAvailable(String term, List<Message> messages) {
runOnUiThread(() -> { runOnUiThread(() -> {
this.messages.clear(); this.messages.clear();
messageListAdapter.setHighlightedTerm(term);
DateSeparator.addAll(messages); DateSeparator.addAll(messages);
this.messages.addAll(messages); this.messages.addAll(messages);
messageListAdapter.notifyDataSetChanged(); messageListAdapter.notifyDataSetChanged();

View file

@ -99,6 +99,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
+ "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])" + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
+ "|(?:\\%[a-fA-F0-9]{2}))+"); + "|(?:\\%[a-fA-F0-9]{2}))+");
private String highlightedText = null;
private static final Linkify.TransformFilter WEBURL_TRANSFORM_FILTER = (matcher, url) -> { private static final Linkify.TransformFilter WEBURL_TRANSFORM_FILTER = (matcher, url) -> {
if (url == null) { if (url == null) {
return null; return null;
@ -548,6 +550,9 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} }
StylingHelper.format(body, viewHolder.messageBody.getCurrentTextColor()); StylingHelper.format(body, viewHolder.messageBody.getCurrentTextColor());
if (highlightedText != null) {
StylingHelper.highlight(activity, body, highlightedText, StylingHelper.isDarkText(viewHolder.messageBody));
}
Linkify.addLinks(body, XMPP_PATTERN, "xmpp", XMPPURI_MATCH_FILTER, null); Linkify.addLinks(body, XMPP_PATTERN, "xmpp", XMPPURI_MATCH_FILTER, null);
Linkify.addLinks(body, Patterns.AUTOLINK_WEB_URL, "http", WEBURL_MATCH_FILTER, WEBURL_TRANSFORM_FILTER); Linkify.addLinks(body, Patterns.AUTOLINK_WEB_URL, "http", WEBURL_MATCH_FILTER, WEBURL_TRANSFORM_FILTER);
@ -1003,6 +1008,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} }
} }
public void setHighlightedTerm(String term) {
this.highlightedText = term;
}
public interface OnQuoteListener { public interface OnQuoteListener {
void onQuote(String text); void onQuote(String text);
} }

View file

@ -29,22 +29,28 @@
package eu.siacs.conversations.utils; package eu.siacs.conversations.utils;
import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.v4.content.ContextCompat;
import android.text.Editable; import android.text.Editable;
import android.text.ParcelableSpan; import android.text.ParcelableSpan;
import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan; import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.text.QuoteSpan; import eu.siacs.conversations.ui.text.QuoteSpan;
@ -85,6 +91,24 @@ public class StylingHelper {
format(editable, end, editable.length() - 1, textColor); format(editable, end, editable.length() - 1, textColor);
} }
public static void highlight(final Context context, final Editable editable, String needle, boolean dark) {
final int length = needle.length();
String string = editable.toString();
int start = indexOfIgnoreCase(string, needle, 0);
while (start != -1) {
int end = start + length;
editable.setSpan(new BackgroundColorSpan(ContextCompat.getColor(context, dark ? R.color.deep_purple_a100 : R.color.deep_purple_a200)), start, end, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
editable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, dark ? R.color.black87 : R.color.white)), start, end, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
start = indexOfIgnoreCase(string, needle, start + length);
}
}
public static boolean isDarkText(TextView textView) {
int argb = textView.getCurrentTextColor();
return Color.red(argb) + Color.green(argb) + Color.blue(argb) == 0;
}
private static ParcelableSpan createSpanForStyle(ImStyleParser.Style style) { private static ParcelableSpan createSpanForStyle(ImStyleParser.Style style) {
switch (style.getKeyword()) { switch (style.getKeyword()) {
case "*": case "*":
@ -114,6 +138,25 @@ public class StylingHelper {
return Color.argb(Math.round(Color.alpha(c) * 0.6f), Color.red(c), Color.green(c), Color.blue(c)); return Color.argb(Math.round(Color.alpha(c) * 0.6f), Color.red(c), Color.green(c), Color.blue(c));
} }
private static int indexOfIgnoreCase(final String haystack, final String needle, final int start) {
if (haystack == null || needle == null) {
return -1;
}
final int endLimit = haystack.length() - needle.length() + 1;
if (start > endLimit) {
return -1;
}
if (needle.length() == 0) {
return start;
}
for (int i = start; i < endLimit; i++) {
if (haystack.regionMatches(true, i, needle, 0, needle.length())) {
return i;
}
}
return -1;
}
public static class MessageEditorStyler implements TextWatcher { public static class MessageEditorStyler implements TextWatcher {
private final EditText mEditText; private final EditText mEditText;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

After

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 B

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 936 B

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 935 B

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 B

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB