From 3563efc7deba8d687ff48f8739473690b5bd5be6 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 15 Jun 2018 09:20:08 -0700 Subject: [PATCH] Update search query results when messages disappear. Previously, if a message disappeared while looking at it in the search results, it'd still stick around. Now they'll disappear from the results in real-time. --- .../securesms/database/CursorList.java | 18 +++++++-- .../securesms/database/SearchDatabase.java | 4 +- .../securesms/search/SearchViewModel.java | 37 ++++++++++++++++--- .../securesms/search/model/SearchResult.java | 14 +++++++ 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/org/thoughtcrime/securesms/database/CursorList.java b/src/org/thoughtcrime/securesms/database/CursorList.java index 0223f08a14..4d1436f367 100644 --- a/src/org/thoughtcrime/securesms/database/CursorList.java +++ b/src/org/thoughtcrime/securesms/database/CursorList.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.database; +import android.database.ContentObserver; import android.database.Cursor; +import android.database.DataSetObserver; import android.database.MatrixCursor; import android.support.annotation.NonNull; @@ -22,8 +24,6 @@ import java.util.ListIterator; */ public class CursorList implements List, Closeable { - private static final Cursor EMPTY_CURSOR = new MatrixCursor(new String[] { "a" }, 0); - private final Cursor cursor; private final ModelBuilder modelBuilder; @@ -34,7 +34,11 @@ public class CursorList implements List, Closeable { public static CursorList emptyList() { //noinspection ConstantConditions,unchecked - return (CursorList) new CursorList(EMPTY_CURSOR, null); + return (CursorList) new CursorList(emptyCursor(), null); + } + + private static Cursor emptyCursor() { + return new MatrixCursor(new String[] { "a" }, 0); } @Override @@ -183,6 +187,14 @@ public class CursorList implements List, Closeable { } } + public void registerContentObserver(@NonNull ContentObserver observer) { + cursor.registerContentObserver(observer); + } + + public void unregisterContentObserver(@NonNull ContentObserver observer) { + cursor.unregisterContentObserver(observer); + } + public interface ModelBuilder { T build(@NonNull Cursor cursor); } diff --git a/src/org/thoughtcrime/securesms/database/SearchDatabase.java b/src/org/thoughtcrime/securesms/database/SearchDatabase.java index 127b988817..161ffbc2fb 100644 --- a/src/org/thoughtcrime/securesms/database/SearchDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SearchDatabase.java @@ -83,7 +83,9 @@ public class SearchDatabase extends Database { String prefixQuery = query + '*'; - return db.rawQuery(MESSAGES_QUERY, new String[] { prefixQuery, prefixQuery }); + Cursor cursor = db.rawQuery(MESSAGES_QUERY, new String[] { prefixQuery, prefixQuery }); + setNotifyConverationListListeners(cursor); + return cursor; } } diff --git a/src/org/thoughtcrime/securesms/search/SearchViewModel.java b/src/org/thoughtcrime/securesms/search/SearchViewModel.java index d05aeeb4cc..fd80a2371a 100644 --- a/src/org/thoughtcrime/securesms/search/SearchViewModel.java +++ b/src/org/thoughtcrime/securesms/search/SearchViewModel.java @@ -4,7 +4,10 @@ import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.ViewModel; import android.arch.lifecycle.ViewModelProvider; +import android.database.ContentObserver; +import android.os.Handler; import android.support.annotation.NonNull; +import android.text.TextUtils; import org.thoughtcrime.securesms.search.model.SearchResult; import org.thoughtcrime.securesms.util.Debouncer; @@ -19,16 +22,25 @@ import org.thoughtcrime.securesms.util.Debouncer; */ class SearchViewModel extends ViewModel { - private final ClosingLiveData searchResult; - private final SearchRepository searchRepository; - private final Debouncer debouncer; + private final ObservingLiveData searchResult; + private final SearchRepository searchRepository; + private final Debouncer debouncer; private String lastQuery; SearchViewModel(@NonNull SearchRepository searchRepository) { - this.searchResult = new ClosingLiveData(); + this.searchResult = new ObservingLiveData(); this.searchRepository = searchRepository; this.debouncer = new Debouncer(500); + + searchResult.registerContentObserver(new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + if (!TextUtils.isEmpty(getLastQuery())) { + searchRepository.query(getLastQuery(), searchResult::postValue); + } + } + }); } LiveData getSearchResult() { @@ -54,23 +66,36 @@ class SearchViewModel extends ViewModel { /** * Ensures that the previous {@link SearchResult} is always closed whenever we set a new one. */ - private static class ClosingLiveData extends MutableLiveData { + private static class ObservingLiveData extends MutableLiveData { + + private ContentObserver observer; @Override public void setValue(SearchResult value) { SearchResult previous = getValue(); + if (previous != null) { + previous.unregisterContentObserver(observer); previous.close(); } + + value.registerContentObserver(observer); + super.setValue(value); } - public void close() { + void close() { SearchResult value = getValue(); + if (value != null) { + value.unregisterContentObserver(observer); value.close(); } } + + void registerContentObserver(@NonNull ContentObserver observer) { + this.observer = observer; + } } public static class Factory extends ViewModelProvider.NewInstanceFactory { diff --git a/src/org/thoughtcrime/securesms/search/model/SearchResult.java b/src/org/thoughtcrime/securesms/search/model/SearchResult.java index e13f4b5090..9c2577125d 100644 --- a/src/org/thoughtcrime/securesms/search/model/SearchResult.java +++ b/src/org/thoughtcrime/securesms/search/model/SearchResult.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.search.model; +import android.database.ContentObserver; +import android.database.DataSetObserver; import android.support.annotation.NonNull; import org.thoughtcrime.securesms.database.CursorList; @@ -56,6 +58,18 @@ public class SearchResult { return size() == 0; } + public void registerContentObserver(@NonNull ContentObserver observer) { + contacts.registerContentObserver(observer); + conversations.registerContentObserver(observer); + messages.registerContentObserver(observer); + } + + public void unregisterContentObserver(@NonNull ContentObserver observer) { + contacts.unregisterContentObserver(observer); + conversations.unregisterContentObserver(observer); + messages.unregisterContentObserver(observer); + } + public void close() { contacts.close(); conversations.close();