mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
163 lines
5.1 KiB
Java
163 lines
5.1 KiB
Java
package org.thoughtcrime.securesms.search;
|
|
|
|
import android.content.Context;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.lifecycle.LiveData;
|
|
import androidx.lifecycle.MutableLiveData;
|
|
import androidx.lifecycle.ViewModel;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
import com.b44t.messenger.DcChatlist;
|
|
import com.b44t.messenger.DcContext;
|
|
import com.b44t.messenger.DcLot;
|
|
import org.thoughtcrime.securesms.connect.DcHelper;
|
|
import org.thoughtcrime.securesms.search.model.SearchResult;
|
|
import org.thoughtcrime.securesms.util.Util;
|
|
|
|
class SearchViewModel extends ViewModel {
|
|
private static final String TAG = "SearchViewModel";
|
|
private final ObservingLiveData searchResult;
|
|
private String lastQuery;
|
|
private final Context appContext;
|
|
private final DcContext dcContext;
|
|
private boolean forwarding = false;
|
|
private final Object bgSearchLock = new Object();
|
|
private boolean inBgSearch;
|
|
private boolean needsAnotherBgSearch;
|
|
|
|
SearchViewModel(@NonNull Context context) {
|
|
this.appContext = context.getApplicationContext();
|
|
this.dcContext = DcHelper.getContext(appContext);
|
|
this.searchResult = new ObservingLiveData();
|
|
}
|
|
|
|
LiveData<SearchResult> getSearchResult() {
|
|
return searchResult;
|
|
}
|
|
|
|
public void setForwardingMode(boolean forwarding) {
|
|
this.forwarding = forwarding;
|
|
}
|
|
|
|
void updateQuery(String query) {
|
|
lastQuery = query;
|
|
updateQuery();
|
|
}
|
|
|
|
public void updateQuery() {
|
|
synchronized (bgSearchLock) {
|
|
if (inBgSearch) {
|
|
Log.i(TAG, "... search call debounced");
|
|
needsAnotherBgSearch = true;
|
|
return;
|
|
}
|
|
inBgSearch = true;
|
|
}
|
|
Util.runOnBackground(
|
|
() -> {
|
|
while (true) {
|
|
Log.i(TAG, "... executing debounced search call");
|
|
queryAndCallback(lastQuery, searchResult::postValue);
|
|
synchronized (bgSearchLock) {
|
|
if (!needsAnotherBgSearch) {
|
|
inBgSearch = false;
|
|
return;
|
|
}
|
|
needsAnotherBgSearch = false;
|
|
}
|
|
Util.sleep(100);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void queryAndCallback(@NonNull String query, @NonNull SearchViewModel.Callback callback) {
|
|
int overallCnt = 0;
|
|
|
|
if (TextUtils.isEmpty(query)) {
|
|
callback.onResult(SearchResult.EMPTY);
|
|
return;
|
|
}
|
|
|
|
QrInviteData qrInviteData = null;
|
|
if (!forwarding && query.contains(":")) {
|
|
DcLot qrParsed = dcContext.checkQr(query);
|
|
qrInviteData = QrInviteData.from(appContext, dcContext, qrParsed, query);
|
|
}
|
|
|
|
// #1 search for chats
|
|
long startMs = System.currentTimeMillis();
|
|
DcChatlist conversations =
|
|
dcContext.getChatlist(forwarding ? DcContext.DC_GCL_FOR_FORWARDING : 0, query, 0);
|
|
overallCnt += conversations.getCnt();
|
|
Log.i(TAG, "⏰ getChatlist(" + query + "): " + (System.currentTimeMillis() - startMs) + "ms");
|
|
|
|
// #2 search for contacts
|
|
if (!query.equals(lastQuery) && overallCnt > 0) {
|
|
Log.i(TAG, "... skipping getContacts() and searchMsgs(), more recent search pending");
|
|
callback.onResult(
|
|
new SearchResult(query, new int[0], conversations, new int[0], qrInviteData));
|
|
return;
|
|
}
|
|
|
|
startMs = System.currentTimeMillis();
|
|
int[] contacts = dcContext.getContacts(DcContext.DC_GCL_ADD_SELF, query);
|
|
overallCnt += contacts.length;
|
|
Log.i(TAG, "⏰ getContacts(" + query + "): " + (System.currentTimeMillis() - startMs) + "ms");
|
|
|
|
// #3 search for messages
|
|
if (forwarding) {
|
|
Log.i(TAG, "... searchMsgs() disabled by caller");
|
|
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
|
return;
|
|
}
|
|
|
|
if (query.length() <= 1) {
|
|
Log.i(TAG, "... skipping searchMsgs(), string too short");
|
|
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
|
return;
|
|
}
|
|
|
|
if (!query.equals(lastQuery) && overallCnt > 0) {
|
|
Log.i(TAG, "... skipping searchMsgs(), more recent search pending");
|
|
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
|
return;
|
|
}
|
|
|
|
startMs = System.currentTimeMillis();
|
|
int[] messages = dcContext.searchMsgs(0, query);
|
|
Log.i(TAG, "⏰ searchMsgs(" + query + "): " + (System.currentTimeMillis() - startMs) + "ms");
|
|
|
|
callback.onResult(new SearchResult(query, contacts, conversations, messages, qrInviteData));
|
|
}
|
|
|
|
@NonNull
|
|
String getLastQuery() {
|
|
return lastQuery == null ? "" : lastQuery;
|
|
}
|
|
|
|
@Override
|
|
protected void onCleared() {}
|
|
|
|
private static class ObservingLiveData extends MutableLiveData<SearchResult> {}
|
|
|
|
public static class Factory extends ViewModelProvider.NewInstanceFactory {
|
|
|
|
private final Context context;
|
|
|
|
public Factory(@NonNull Context context) {
|
|
this.context = context;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
|
return modelClass.cast(new SearchViewModel(context));
|
|
}
|
|
}
|
|
|
|
public interface Callback {
|
|
void onResult(@NonNull SearchResult result);
|
|
}
|
|
}
|