From bae77ff8fe587e981caa44ba27f5371b7a6664f6 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Mon, 30 Apr 2018 23:51:01 +0200 Subject: properly cancel pending searchs and scroll to bottom after refresh --- src/main/java/de/pixart/messenger/Config.java | 1 + .../messenger/persistance/DatabaseBackend.java | 2 +- .../messenger/services/MessageSearchTask.java | 43 ++++++++++++------ .../pixart/messenger/ui/ConversationFragment.java | 16 ++----- .../de/pixart/messenger/ui/SearchActivity.java | 10 ++++- .../de/pixart/messenger/ui/util/ListViewUtils.java | 52 ++++++++++++++++++++++ .../utils/ReplacingSerialSingleThreadExecutor.java | 7 +++ .../utils/SerialSingleThreadExecutor.java | 37 +++++++++++---- 8 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 src/main/java/de/pixart/messenger/ui/util/ListViewUtils.java (limited to 'src/main') diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index 4e1c86372..3e946c397 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -92,6 +92,7 @@ public final class Config { public static final int PAGE_SIZE = 50; public static final int MAX_NUM_PAGES = 3; + public static final int MAX_SEARCH_RESULTS = 300; public static final int REFRESH_UI_INTERVAL = 500; diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index d55f36bdf..b407de339 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -719,7 +719,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Cursor getMessageSearchCursor(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 + " where " + Message.BODY + " LIKE ? limit 200"; + 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 + " where " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + ',' + Message.ENCRYPTION_PGP + ',' + Message.ENCRYPTION_DECRYPTION_FAILED + ") AND " + Message.BODY + " LIKE ? ORDER BY " + Message.TIME_SENT + " DESC limit " + Config.MAX_SEARCH_RESULTS; return db.rawQuery(SQL, new String[]{'%' + term + '%'}); } diff --git a/src/main/java/de/pixart/messenger/services/MessageSearchTask.java b/src/main/java/de/pixart/messenger/services/MessageSearchTask.java index 4911eff75..9a5c8b143 100644 --- a/src/main/java/de/pixart/messenger/services/MessageSearchTask.java +++ b/src/main/java/de/pixart/messenger/services/MessageSearchTask.java @@ -69,6 +69,10 @@ public class MessageSearchTask implements Runnable, Cancellable { new MessageSearchTask(xmppConnectionService, term, onSearchResultsAvailable).executeInBackground(); } + public static void cancelRunningTasks() { + EXECUTOR.cancelRunningTasks(); + } + @Override public void cancel() { this.isCancelled = true; @@ -82,20 +86,31 @@ public class MessageSearchTask implements Runnable, Cancellable { final HashMap conversationCache = new HashMap<>(); final List result = new ArrayList<>(); cursor = xmppConnectionService.databaseBackend.getMessageSearchCursor(term); - while (cursor.moveToNext()) { - final String conversationUuid = cursor.getString(cursor.getColumnIndex(Message.CONVERSATION)); - Conversational conversation; - if (conversationCache.containsKey(conversationUuid)) { - conversation = conversationCache.get(conversationUuid); - } else { - String accountUuid = cursor.getString(cursor.getColumnIndex(Conversation.ACCOUNT)); - String contactJid = cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)); - int mode = cursor.getInt(cursor.getColumnIndex(Conversation.MODE)); - conversation = findOrGenerateStub(conversationUuid, accountUuid, contactJid, mode); - conversationCache.put(conversationUuid, conversation); - } - Message message = IndividualMessage.fromCursor(cursor, conversation); - result.add(message); + if (isCancelled) { + Log.d(Config.LOGTAG, "canceled search task"); + return; + } + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToLast(); + do { + if (isCancelled) { + Log.d(Config.LOGTAG, "canceled search task"); + return; + } + final String conversationUuid = cursor.getString(cursor.getColumnIndex(Message.CONVERSATION)); + Conversational conversation; + if (conversationCache.containsKey(conversationUuid)) { + conversation = conversationCache.get(conversationUuid); + } else { + String accountUuid = cursor.getString(cursor.getColumnIndex(Conversation.ACCOUNT)); + String contactJid = cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)); + int mode = cursor.getInt(cursor.getColumnIndex(Conversation.MODE)); + conversation = findOrGenerateStub(conversationUuid, accountUuid, contactJid, mode); + conversationCache.put(conversationUuid, conversation); + } + Message message = IndividualMessage.fromCursor(cursor, conversation); + result.add(message); + } while (cursor.moveToPrevious()); } long stopTimestamp = SystemClock.elapsedRealtime(); Log.d(Config.LOGTAG, "found " + result.size() + " messages in " + (stopTimestamp - startTimestamp) + "ms"); diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index 4207a1b5e..6d45cf306 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -97,6 +97,7 @@ import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.util.ActivityResult; import de.pixart.messenger.ui.util.AttachmentTool; import de.pixart.messenger.ui.util.ConversationMenuConfigurator; +import de.pixart.messenger.ui.util.ListViewUtils; import de.pixart.messenger.ui.util.PendingItem; import de.pixart.messenger.ui.util.PresenceSelector; import de.pixart.messenger.ui.util.ScrollState; @@ -2253,22 +2254,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void setSelection(int pos, boolean jumpToBottom) { - setSelection(this.binding.messagesView, pos, jumpToBottom); - this.binding.messagesView.post(() -> setSelection(this.binding.messagesView, pos, jumpToBottom)); + ListViewUtils.setSelection(this.binding.messagesView, pos, jumpToBottom); + this.binding.messagesView.post(() -> ListViewUtils.setSelection(this.binding.messagesView, pos, jumpToBottom)); this.binding.messagesView.post(this::fireReadEvent); } - private static void setSelection(final ListView listView, int pos, boolean jumpToBottom) { - if (jumpToBottom) { - final View lastChild = listView.getChildAt(listView.getChildCount() - 1); - if (lastChild != null) { - listView.setSelectionFromTop(pos, -lastChild.getHeight()); - return; - } - } - listView.setSelection(pos); - } - private boolean scrolledToBottom() { return this.binding != null && scrolledToBottom(this.binding.messagesView); } diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java index 2120cac3e..28e4ef5b4 100644 --- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java @@ -33,6 +33,7 @@ import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.text.Editable; +import android.text.InputType; import android.text.TextWatcher; import android.view.Menu; import android.view.MenuItem; @@ -44,10 +45,12 @@ import java.util.List; import de.pixart.messenger.R; import de.pixart.messenger.databinding.ActivitySearchBinding; import de.pixart.messenger.entities.Message; +import de.pixart.messenger.services.MessageSearchTask; import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.interfaces.OnSearchResultsAvailable; import de.pixart.messenger.ui.util.Color; import de.pixart.messenger.ui.util.Drawable; +import de.pixart.messenger.ui.util.ListViewUtils; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.showKeyboard; @@ -75,6 +78,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc EditText searchField = searchActionMenuItem.getActionView().findViewById(R.id.search_field); searchField.addTextChangedListener(this); searchField.setHint(R.string.search_messages); + searchField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); showKeyboard(searchField); return super.onCreateOptionsMenu(menu); } @@ -125,6 +129,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc if (term.length() > 0) { xmppConnectionService.search(s.toString().trim(), this); } else { + MessageSearchTask.cancelRunningTasks(); this.messages.clear(); messageListAdapter.notifyDataSetChanged(); changeBackground(false, false); @@ -133,11 +138,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc @Override public void onSearchResultsAvailable(String term, List messages) { - this.messages.clear(); - this.messages.addAll(messages); runOnUiThread(() -> { + this.messages.clear(); + this.messages.addAll(messages); messageListAdapter.notifyDataSetChanged(); changeBackground(true, messages.size() > 0); + ListViewUtils.scrollToBottom(this.binding.searchResults); }); } diff --git a/src/main/java/de/pixart/messenger/ui/util/ListViewUtils.java b/src/main/java/de/pixart/messenger/ui/util/ListViewUtils.java new file mode 100644 index 000000000..c35ec3724 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/ListViewUtils.java @@ -0,0 +1,52 @@ +/* +* 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.ui.util; + +import android.view.View; +import android.widget.ListView; + +public class ListViewUtils { + public static void scrollToBottom(final ListView listView) { + int count = listView.getAdapter().getCount(); + if (count > 0) { + setSelection(listView, count - 1, true); + } + } + + public static void setSelection(final ListView listView, int pos, boolean jumpToBottom) { + if (jumpToBottom) { + final View lastChild = listView.getChildAt(listView.getChildCount() - 1); + if (lastChild != null) { + listView.setSelectionFromTop(pos, -lastChild.getHeight()); + return; + } + } + listView.setSelection(pos); + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/ReplacingSerialSingleThreadExecutor.java b/src/main/java/de/pixart/messenger/utils/ReplacingSerialSingleThreadExecutor.java index 7bd33eff4..bf4d6e8c2 100644 --- a/src/main/java/de/pixart/messenger/utils/ReplacingSerialSingleThreadExecutor.java +++ b/src/main/java/de/pixart/messenger/utils/ReplacingSerialSingleThreadExecutor.java @@ -18,4 +18,11 @@ public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecu } super.execute(r); } + + public synchronized void cancelRunningTasks() { + tasks.clear(); + if (active != null && active instanceof Cancellable) { + ((Cancellable) active).cancel(); + } + } } diff --git a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java index 1a1280d5a..500b03c5b 100644 --- a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java +++ b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java @@ -11,10 +11,10 @@ import de.pixart.messenger.Config; public class SerialSingleThreadExecutor implements Executor { + final ArrayDeque tasks = new ArrayDeque<>(); private final Executor executor = Executors.newSingleThreadExecutor(); - final ArrayDeque tasks = new ArrayDeque<>(); - protected Runnable active; private final String name; + protected Runnable active; public SerialSingleThreadExecutor(String name) { this(name, false); @@ -28,13 +28,7 @@ public class SerialSingleThreadExecutor implements Executor { } public synchronized void execute(final Runnable r) { - tasks.offer(() -> { - try { - r.run(); - } finally { - scheduleNext(); - } - }); + tasks.offer(new Runner(r)); if (active == null) { scheduleNext(); } @@ -49,4 +43,29 @@ public class SerialSingleThreadExecutor implements Executor { } } } + + private class Runner implements Runnable, Cancellable { + + private final Runnable runnable; + + private Runner(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void cancel() { + if (runnable instanceof Cancellable) { + ((Cancellable) runnable).cancel(); + } + } + + @Override + public void run() { + try { + runnable.run(); + } finally { + scheduleNext(); + } + } + } } \ No newline at end of file -- cgit v1.2.3