Fixes #1346 - properly this time!

This commit is contained in:
Al Lansley 2024-04-04 11:27:47 +11:00
parent d65705c845
commit 04fb296787
4 changed files with 32 additions and 71 deletions

View File

@ -12,6 +12,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.SpannableString import android.text.SpannableString
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -21,20 +22,25 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityHomeBinding import network.loki.messenger.databinding.ActivityHomeBinding
import network.loki.messenger.databinding.ViewMessageRequestBannerBinding import network.loki.messenger.databinding.ViewMessageRequestBannerBinding
import network.loki.messenger.libsession_util.ConfigBase import network.loki.messenger.libsession_util.ConfigBase
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.sending_receiving.MessageSender
@ -47,6 +53,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.utilities.toHexString import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.start.NewConversationFragment import org.thoughtcrime.securesms.conversation.start.NewConversationFragment
@ -82,8 +89,10 @@ import org.thoughtcrime.securesms.util.disableClipping
import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show import org.thoughtcrime.securesms.util.show
import org.thoughtcrime.securesms.util.themeState import org.thoughtcrime.securesms.util.themeState
import java.io.IOException import java.io.IOException
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint

View File

@ -2,7 +2,9 @@ package org.thoughtcrime.securesms.home.search
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -13,11 +15,14 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.SettableFuture import org.session.libsignal.utilities.SettableFuture
import org.thoughtcrime.securesms.search.SearchRepository import org.thoughtcrime.securesms.search.SearchRepository
import org.thoughtcrime.securesms.search.model.SearchResult import org.thoughtcrime.securesms.search.model.SearchResult
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -25,8 +30,7 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
private val executor = viewModelScope + SupervisorJob() private val executor = viewModelScope + SupervisorJob()
private val _result: MutableStateFlow<GlobalSearchResult> = private val _result: MutableStateFlow<GlobalSearchResult> = MutableStateFlow(GlobalSearchResult.EMPTY)
MutableStateFlow(GlobalSearchResult.EMPTY)
val result: StateFlow<GlobalSearchResult> = _result val result: StateFlow<GlobalSearchResult> = _result
@ -34,7 +38,6 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
private var settableFuture = SettableFuture<SearchResult>() private var settableFuture = SettableFuture<SearchResult>()
fun postQuery(charSequence: CharSequence?) { fun postQuery(charSequence: CharSequence?) {
charSequence ?: return charSequence ?: return
_queryText.value = charSequence _queryText.value = charSequence
@ -45,7 +48,7 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
_queryText _queryText
.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST) .buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
.mapLatest { query -> .mapLatest { query ->
// Minimum search term is 2 characters - for a single char we do nothing // Minimum search term is 2 characters
if (query.trim().length < 2) { if (query.trim().length < 2) {
SearchResult.EMPTY SearchResult.EMPTY
} else { } else {
@ -55,9 +58,7 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
delay(300) delay(300)
// If we already have a search running then stop it // If we already have a search running then stop it
if (!settableFuture.isDone) { if (!settableFuture.isDone) { settableFuture.cancel(true) }
Log.w("[ACL]", "Cancelling settable future..")
settableFuture.cancel(true); }
settableFuture = SettableFuture<SearchResult>() settableFuture = SettableFuture<SearchResult>()
searchRepository.query(query.toString(), settableFuture::set) searchRepository.query(query.toString(), settableFuture::set)

View File

@ -4,7 +4,6 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils; import android.database.DatabaseUtils;
import android.database.MergeCursor; import android.database.MergeCursor;
import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -36,28 +35,18 @@ import java.util.concurrent.Executor;
import kotlin.Pair; import kotlin.Pair;
/** // Class to manage data retrieval for search
* Manages data retrieval for search.
*/
public class SearchRepository { public class SearchRepository {
private static final String TAG = SearchRepository.class.getSimpleName(); private static final String TAG = SearchRepository.class.getSimpleName();
private static final Set<Character> BANNED_CHARACTERS = new HashSet<>(); private static final Set<Character> BANNED_CHARACTERS = new HashSet<>();
static { static {
// Several ranges of invalid ASCII characters // Construct a list containing several ranges of invalid ASCII characters
for (int i = 33; i <= 47; i++) { // See: https://www.ascii-code.com/
BANNED_CHARACTERS.add((char) i); for (int i = 33; i <= 47; i++) { BANNED_CHARACTERS.add((char) i); } // !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /
} for (int i = 58; i <= 64; i++) { BANNED_CHARACTERS.add((char) i); } // :, ;, <, =, >, ?, @
for (int i = 58; i <= 64; i++) { for (int i = 91; i <= 96; i++) { BANNED_CHARACTERS.add((char) i); } // [, \, ], ^, _, `
BANNED_CHARACTERS.add((char) i); for (int i = 123; i <= 126; i++) { BANNED_CHARACTERS.add((char) i); } // {, |, }, ~
}
for (int i = 91; i <= 96; i++) {
BANNED_CHARACTERS.add((char) i);
}
for (int i = 123; i <= 126; i++) {
BANNED_CHARACTERS.add((char) i);
}
} }
private final Context context; private final Context context;
@ -86,54 +75,25 @@ public class SearchRepository {
} }
public void query(@NonNull String query, @NonNull Callback<SearchResult> callback) { public void query(@NonNull String query, @NonNull Callback<SearchResult> callback) {
Log.w("[ACL]", "Hit SearchRepository.query - query string is: \"" + query + "\"");
String cleanQuery = sanitizeQuery(query).trim();
Log.w("[ACL]", "When sanitized and trimmed this is: \"" + cleanQuery + "\"");
// If the sanitized search is empty or is less than 2 chars then abort // If the sanitized search is empty or is less than 2 chars then abort
String cleanQuery = sanitizeQuery(query).trim();
if (cleanQuery.isEmpty() || cleanQuery.length() < 2) { if (cleanQuery.isEmpty() || cleanQuery.length() < 2) {
Log.w("[ACL]", "Trimmed query is empty or less than 2 chars so returning empty SearchResult");
callback.onResult(SearchResult.EMPTY); callback.onResult(SearchResult.EMPTY);
return; return;
} }
executor.execute(() -> { executor.execute(() -> {
Stopwatch timer = new Stopwatch("FtsQuery"); Stopwatch timer = new Stopwatch("FtsQuery");
// ACL
//String cleanQuery = sanitizeQuery(query).trim();
/*
if (cleanQuery.isEmpty())
{
Log.w("[ACL]", "Aborting empty search query.");
Log.d(TAG, "Aborting empty search query.");
timer.stop(TAG);
return;
}
else {
Log.w("[ACL]", "Clean query is non-empty and is: \"" + cleanQuery + "\"");
}
*/
timer.split("clean"); timer.split("clean");
Log.w("[ACL]", "About to query contacts.");
Pair<CursorList<Contact>, List<String>> contacts = queryContacts(cleanQuery); Pair<CursorList<Contact>, List<String>> contacts = queryContacts(cleanQuery);
timer.split("contacts"); timer.split("Contacts");
Log.w("[ACL]", "About to query conversations.");
CursorList<GroupRecord> conversations = queryConversations(cleanQuery, contacts.getSecond()); CursorList<GroupRecord> conversations = queryConversations(cleanQuery, contacts.getSecond());
timer.split("conversations"); timer.split("Conversations");
Log.w("[ACL]", "About to query messages.");
CursorList<MessageResult> messages = queryMessages(cleanQuery); CursorList<MessageResult> messages = queryMessages(cleanQuery);
timer.split("messages"); timer.split("Messages");
timer.stop(TAG); timer.stop(TAG);
@ -182,11 +142,10 @@ public class SearchRepository {
MergeCursor merged = new MergeCursor(new Cursor[]{addressThreads, individualRecipients}); MergeCursor merged = new MergeCursor(new Cursor[]{addressThreads, individualRecipients});
return new Pair<>(new CursorList<>(merged, new ContactModelBuilder(contactDatabase, threadDatabase)), contactStrings); return new Pair<>(new CursorList<>(merged, new ContactModelBuilder(contactDatabase, threadDatabase)), contactStrings);
} }
private CursorList<GroupRecord> queryConversations(@NonNull String query, List<String> matchingAddresses) { private CursorList<GroupRecord> queryConversations(@NonNull String query, List<String> matchingAddresses) {
List<String> numbers = contactAccessor.getNumbersForThreadSearchFilter(context, query); List<String> numbers = contactAccessor.getNumbersForThreadSearchFilter(context, query);
String localUserNumber = TextSecurePreferences.getLocalNumber(context); String localUserNumber = TextSecurePreferences.getLocalNumber(context);
if (localUserNumber != null) { if (localUserNumber != null) {
matchingAddresses.remove(localUserNumber); matchingAddresses.remove(localUserNumber);
@ -270,9 +229,7 @@ public class SearchRepository {
private final Context context; private final Context context;
RecipientModelBuilder(@NonNull Context context) { RecipientModelBuilder(@NonNull Context context) { this.context = context; }
this.context = context;
}
@Override @Override
public Recipient build(@NonNull Cursor cursor) { public Recipient build(@NonNull Cursor cursor) {
@ -315,9 +272,7 @@ public class SearchRepository {
private final Context context; private final Context context;
MessageModelBuilder(@NonNull Context context) { MessageModelBuilder(@NonNull Context context) { this.context = context; }
this.context = context;
}
@Override @Override
public MessageResult build(@NonNull Cursor cursor) { public MessageResult build(@NonNull Cursor cursor) {

View File

@ -39,13 +39,9 @@ public class Stopwatch {
out.append(splits.get(i).time - splits.get(i - 1).time); out.append(splits.get(i).time - splits.get(i - 1).time);
out.append("ms "); out.append("ms ");
} }
out.append("total: ").append(splits.get(splits.size() - 1).time - startTime).append("ms."); out.append("total: ").append(splits.get(splits.size() - 1).time - startTime).append("ms.");
} }
Log.d(tag, out.toString()); Log.d(tag, out.toString());
Log.w("[ACL]", out.toString());
} }
private static class Split { private static class Split {