package org.tm.archive.conversation; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import org.signal.core.util.ThreadUtil; import org.tm.archive.search.MessageResult; import org.tm.archive.search.SearchRepository; import org.tm.archive.util.Debouncer; import java.util.Collections; import java.util.List; public class ConversationSearchViewModel extends ViewModel { private final SearchRepository searchRepository; private final MutableLiveData result; private final Debouncer debouncer; private boolean firstSearch; private boolean searchOpen; private String activeQuery; private long activeThreadId; public ConversationSearchViewModel(@NonNull String noteToSelfTitle) { result = new MutableLiveData<>(); debouncer = new Debouncer(500); searchRepository = new SearchRepository(noteToSelfTitle); } public @NonNull LiveData getSearchResults() { return result; } public void onQueryUpdated(@NonNull String query, long threadId, boolean forced) { if (firstSearch && query.length() < 2) { result.postValue(new SearchResult(Collections.emptyList(), 0)); return; } if (query.equals(activeQuery) && !forced) { return; } updateQuery(query, threadId); } public void onMissingResult() { if (activeQuery != null) { updateQuery(activeQuery, activeThreadId); } } public void onMoveUp() { if (result.getValue() == null) { return; } debouncer.clear(); List messages = result.getValue().getResults(); int position = Math.min(result.getValue().getPosition() + 1, messages.size() - 1); result.setValue(new SearchResult(messages, position)); } public void onMoveDown() { if (result.getValue() == null) { return; } debouncer.clear(); List messages = result.getValue().getResults(); int position = Math.max(result.getValue().getPosition() - 1, 0); result.setValue(new SearchResult(messages, position)); } public void onSearchOpened() { searchOpen = true; firstSearch = true; } public void onSearchClosed() { searchOpen = false; debouncer.clear(); } private void updateQuery(@NonNull String query, long threadId) { activeQuery = query; activeThreadId = threadId; debouncer.publish(() -> { firstSearch = false; searchRepository.query(query, threadId, messages -> { ThreadUtil.runOnMain(() -> { if (searchOpen && query.equals(activeQuery)) { result.setValue(new SearchResult(messages, 0)); } }); }); }); } public static class SearchResult { private final List results; private final int position; SearchResult(@NonNull List results, int position) { this.results = results; this.position = position; } public List getResults() { return results; } public int getPosition() { return position; } } public static class Factory extends ViewModelProvider.NewInstanceFactory { private final String noteToSelfTitle; public Factory(@NonNull String noteToSelfTitle) { this.noteToSelfTitle = noteToSelfTitle; } @Override public @NonNull T create(@NonNull Class modelClass) { //noinspection ConstantConditions return modelClass.cast(new ConversationSearchViewModel(noteToSelfTitle)); } } }