aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java10
-rw-r--r--src/main/java/de/pixart/messenger/services/MessageSearchTask.java6
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java2
-rw-r--r--src/main/java/de/pixart/messenger/ui/SearchActivity.java16
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/interfaces/OnSearchResultsAvailable.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/FtsUtils.java94
-rw-r--r--src/main/java/de/pixart/messenger/utils/StylingHelper.java10
8 files changed, 128 insertions, 22 deletions
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 16f0f6908..b7973aca6 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -50,6 +50,7 @@ import de.pixart.messenger.entities.Roster;
import de.pixart.messenger.entities.ServiceDiscoveryResult;
import de.pixart.messenger.services.ShortcutService;
import de.pixart.messenger.utils.CryptoHelper;
+import de.pixart.messenger.utils.FtsUtils;
import de.pixart.messenger.utils.Resolver;
import de.pixart.messenger.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
@@ -227,6 +228,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_IDENTITIES_STATEMENT);
db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
+ db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
+ db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
+ db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
+ db.execSQL(CREATE_MESSAGE_DELETE_TRIGGER);
}
@Override
@@ -731,10 +736,11 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return list;
}
- public Cursor getMessageSearchCursor(String term) {
+ public Cursor getMessageSearchCursor(List<String> term) {
SQLiteDatabase db = this.getReadableDatabase();
String SQL = "SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + '.' + Conversation.CONTACTJID + ',' + Conversation.TABLENAME + '.' + Conversation.ACCOUNT + ',' + Conversation.TABLENAME + '.' + Conversation.MODE + " FROM " + Message.TABLENAME + " join " + Conversation.TABLENAME + " on " + Message.TABLENAME + '.' + Message.CONVERSATION + '=' + Conversation.TABLENAME + '.' + Conversation.UUID + " join messages_index ON messages_index.uuid=messages.uuid where " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + ',' + Message.ENCRYPTION_PGP + ',' + Message.ENCRYPTION_DECRYPTION_FAILED + ") AND messages_index.body MATCH ? ORDER BY " + Message.TIME_SENT + " DESC limit " + Config.MAX_SEARCH_RESULTS;
- return db.rawQuery(SQL, new String[]{'%' + term + '%'});
+ Log.d(Config.LOGTAG, "search term: " + FtsUtils.toMatchString(term));
+ return db.rawQuery(SQL, new String[]{FtsUtils.toMatchString(term)});
}
public Iterable<Message> getMessagesIterable(final Conversation conversation) {
diff --git a/src/main/java/de/pixart/messenger/services/MessageSearchTask.java b/src/main/java/de/pixart/messenger/services/MessageSearchTask.java
index 9a5c8b143..76c3800d2 100644
--- a/src/main/java/de/pixart/messenger/services/MessageSearchTask.java
+++ b/src/main/java/de/pixart/messenger/services/MessageSearchTask.java
@@ -54,18 +54,18 @@ public class MessageSearchTask implements Runnable, Cancellable {
private static final ReplacingSerialSingleThreadExecutor EXECUTOR = new ReplacingSerialSingleThreadExecutor(MessageSearchTask.class.getName());
private final XmppConnectionService xmppConnectionService;
- private final String term;
+ private final List<String> term;
private final OnSearchResultsAvailable onSearchResultsAvailable;
private boolean isCancelled = false;
- private MessageSearchTask(XmppConnectionService xmppConnectionService, String term, OnSearchResultsAvailable onSearchResultsAvailable) {
+ private MessageSearchTask(XmppConnectionService xmppConnectionService, List<String> term, OnSearchResultsAvailable onSearchResultsAvailable) {
this.xmppConnectionService = xmppConnectionService;
this.term = term;
this.onSearchResultsAvailable = onSearchResultsAvailable;
}
- public static void search(XmppConnectionService xmppConnectionService, String term, OnSearchResultsAvailable onSearchResultsAvailable) {
+ public static void search(XmppConnectionService xmppConnectionService, List<String> term, OnSearchResultsAvailable onSearchResultsAvailable) {
new MessageSearchTask(xmppConnectionService, term, onSearchResultsAvailable).executeInBackground();
}
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index c2a0378f7..32751d792 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -560,7 +560,7 @@ public class XmppConnectionService extends Service {
return find(getConversations(), account, jid);
}
- public void search(String term, OnSearchResultsAvailable onSearchResultsAvailable) {
+ public void search(List<String> term, OnSearchResultsAvailable onSearchResultsAvailable) {
MessageSearchTask.search(this, term, onSearchResultsAvailable);
}
diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
index 298bca6c0..7ce4ddc98 100644
--- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
@@ -61,6 +61,7 @@ import de.pixart.messenger.ui.util.DateSeparator;
import de.pixart.messenger.ui.util.Drawable;
import de.pixart.messenger.ui.util.ListViewUtils;
import de.pixart.messenger.ui.util.ShareUtil;
+import de.pixart.messenger.utils.FtsUtils;
import de.pixart.messenger.utils.MessageUtils;
import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard;
@@ -72,7 +73,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
private MessageAdapter messageListAdapter;
private final List<Message> messages = new ArrayList<>();
private WeakReference<Message> selectedMessageReference = new WeakReference<>(null);
- private final ChangeWatcher<String> currentSearch = new ChangeWatcher<>();
+ private final ChangeWatcher<List<String>> currentSearch = new ChangeWatcher<>();
@Override
public void onCreate(final Bundle savedInstanceState) {
@@ -150,13 +151,10 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
}
private void quote(Message message) {
- String text = MessageUtils.prepareQuote(message);
- final Conversational conversational = message.getConversation();
- switchToConversationAndQuote(wrap(message.getConversation()), text);
+ switchToConversationAndQuote(wrap(message.getConversation()), MessageUtils.prepareQuote(message));
}
private Conversation wrap(Conversational conversational) {
- final Conversation conversation;
if (conversational instanceof Conversation) {
return (Conversation) conversational;
} else {
@@ -202,12 +200,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
@Override
public void afterTextChanged(Editable s) {
- final String term = s.toString().trim();
+ final List<String> term = FtsUtils.parse(s.toString().trim());
if (!currentSearch.watch(term)) {
return;
}
- if (term.length() > 0) {
- xmppConnectionService.search(s.toString().trim(), this);
+ if (term.size() > 0) {
+ xmppConnectionService.search(term, this);
} else {
MessageSearchTask.cancelRunningTasks();
this.messages.clear();
@@ -218,7 +216,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
}
@Override
- public void onSearchResultsAvailable(String term, List<Message> messages) {
+ public void onSearchResultsAvailable(List<String> term, List<Message> messages) {
runOnUiThread(() -> {
this.messages.clear();
messageListAdapter.setHighlightedTerm(term);
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index 37eb00b76..d658b49bf 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -109,7 +109,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
+ "|(?:\\%[a-fA-F0-9]{2}))+");
boolean isResendable = false;
- private String highlightedText = null;
+ private List<String> highlightedTerm = null;
private static final Linkify.TransformFilter WEBURL_TRANSFORM_FILTER = (matcher, url) -> {
if (url == null) {
@@ -597,8 +597,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
}
StylingHelper.format(body, viewHolder.messageBody.getCurrentTextColor());
- if (highlightedText != null) {
- StylingHelper.highlight(activity, body, highlightedText, StylingHelper.isDarkText(viewHolder.messageBody));
+ if (highlightedTerm != null) {
+ StylingHelper.highlight(activity, body, highlightedTerm, StylingHelper.isDarkText(viewHolder.messageBody));
}
Linkify.addLinks(body, XMPP_PATTERN, "xmpp", XMPPURI_MATCH_FILTER, null);
Linkify.addLinks(body, Patterns.AUTOLINK_WEB_URL, "http", WEBURL_MATCH_FILTER, WEBURL_TRANSFORM_FILTER);
@@ -1161,8 +1161,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
}
- public void setHighlightedTerm(String term) {
- this.highlightedText = term;
+ public void setHighlightedTerm(List<String> term) {
+ this.highlightedTerm = term;
}
public interface OnQuoteListener {
diff --git a/src/main/java/de/pixart/messenger/ui/interfaces/OnSearchResultsAvailable.java b/src/main/java/de/pixart/messenger/ui/interfaces/OnSearchResultsAvailable.java
index 57f2a40cc..b0041fd66 100644
--- a/src/main/java/de/pixart/messenger/ui/interfaces/OnSearchResultsAvailable.java
+++ b/src/main/java/de/pixart/messenger/ui/interfaces/OnSearchResultsAvailable.java
@@ -34,6 +34,6 @@ import de.pixart.messenger.entities.Message;
public interface OnSearchResultsAvailable {
- void onSearchResultsAvailable(String term, List<Message> messages);
+ void onSearchResultsAvailable(List<String> term, List<Message> messages);
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/FtsUtils.java b/src/main/java/de/pixart/messenger/utils/FtsUtils.java
new file mode 100644
index 000000000..86eeeeeaf
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/utils/FtsUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package de.pixart.messenger.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+public class FtsUtils {
+
+ private static List<String> KEYWORDS = Arrays.asList("OR", "AND");
+
+ public static List<String> parse(String input) {
+ List<String> term = new ArrayList<>();
+ for (String part : input.split("\\s+")) {
+ if (part.isEmpty()) {
+ continue;
+ }
+ final String cleaned = part.substring(getStartIndex(part), getEndIndex(part) + 1);
+ if (isKeyword(cleaned)) {
+ term.add(part);
+ } else {
+ term.add(cleaned);
+ }
+ }
+ return term;
+ }
+
+ public static String toMatchString(List<String> terms) {
+ StringBuilder builder = new StringBuilder();
+ for (String term : terms) {
+ if (builder.length() != 0) {
+ builder.append(' ');
+ }
+ if (isKeyword(term)) {
+ builder.append(term.toUpperCase(Locale.ENGLISH));
+ } else if (term.contains("*") || term.startsWith("-")) {
+ builder.append(term);
+ } else {
+ builder.append('*').append(term).append('*');
+ }
+ }
+ return builder.toString();
+ }
+
+ public static boolean isKeyword(String term) {
+ return KEYWORDS.contains(term.toUpperCase(Locale.ENGLISH));
+ }
+
+ private static int getStartIndex(String term) {
+ int index = 0;
+ while (term.charAt(index) == '*') {
+ ++index;
+ }
+ return index;
+ }
+
+ private static int getEndIndex(String term) {
+ int index = term.length() - 1;
+ while (term.charAt(index) == '*') {
+ --index;
+ }
+ return index;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/StylingHelper.java b/src/main/java/de/pixart/messenger/utils/StylingHelper.java
index 21e074603..f429a2573 100644
--- a/src/main/java/de/pixart/messenger/utils/StylingHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/StylingHelper.java
@@ -91,7 +91,15 @@ public class StylingHelper {
format(editable, end, editable.length() - 1, textColor);
}
- public static void highlight(final Context context, final Editable editable, String needle, boolean dark) {
+ public static void highlight(final Context context, final Editable editable, List<String> needles, boolean dark) {
+ for (String needle : needles) {
+ if (!FtsUtils.isKeyword(needle)) {
+ highlight(context, editable, needle, dark);
+ }
+ }
+ }
+
+ private 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);