diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java index ed32230529..486e46c9ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java @@ -300,6 +300,11 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter characterIterator = new CharacterIterable(name).iterator(); if (!TextUtils.isEmpty(name) && characterIterator.hasNext()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java index 38efc7b36a..fcdf376b21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java @@ -22,18 +22,26 @@ import android.database.MatrixCursor; import androidx.annotation.NonNull; +import org.signal.core.util.CursorUtil; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.phonenumbers.NumberUtil; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.UsernameUtil; +import org.whispersystems.signalservice.internal.util.Util; import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; /** * CursorLoader that initializes a ContactsDatabase instance @@ -213,13 +221,36 @@ public class ContactsCursorLoader extends AbstractContactsCursorLoader { } private Cursor getGroupsCursor() { - MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor(); + MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor(); + Map groups = new LinkedHashMap<>(); + try (GroupDatabase.Reader reader = SignalDatabase.groups().queryGroupsByTitle(getFilter(), flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) { GroupDatabase.GroupRecord groupRecord; while ((groupRecord = reader.getNext()) != null) { - groupContacts.addRow(ContactsCursorRows.forGroup(groupRecord)); + groups.put(groupRecord.getRecipientId(), groupRecord); } } + + if (getFilter() != null && !Util.isEmpty(getFilter())) { + Set filteredContacts = new HashSet<>(); + try (Cursor cursor = SignalDatabase.recipients().queryAllContacts(getFilter())) { + while (cursor != null && cursor.moveToNext()) { + filteredContacts.add(RecipientId.from(CursorUtil.requireString(cursor, RecipientDatabase.ID))); + } + } + + try (GroupDatabase.Reader reader = SignalDatabase.groups().queryGroupsByMembership(filteredContacts, flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) { + GroupDatabase.GroupRecord groupRecord; + while ((groupRecord = reader.getNext()) != null) { + groups.put(groupRecord.getRecipientId(), groupRecord); + } + } + } + + for (GroupDatabase.GroupRecord groupRecord : groups.values()) { + groupContacts.addRow(ContactsCursorRows.forGroup(groupRecord)); + } + return groupContacts; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 94a27543c6..48c3599c7d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -294,6 +294,40 @@ public class GroupDatabase extends Database { return new Reader(cursor); } + public Reader queryGroupsByMembership(@NonNull Set recipientIds, boolean includeInactive, boolean excludeV1, boolean excludeMms) { + if (recipientIds.isEmpty()) { + return new Reader(null); + } + + List recipientLikeClauses = recipientIds.stream() + .map(RecipientId::toLong) + .map(id -> "(" + MEMBERS + " LIKE " + id + " || ',%' OR " + MEMBERS + " LIKE '%,' || " + id + " || ',%' OR " + MEMBERS + " LIKE '%,' || " + id + ")") + .collect(Collectors.toList()); + + String query; + String[] queryArgs; + + String membershipQuery = "(" + Util.join(recipientLikeClauses, " OR ") + ")"; + + if (includeInactive) { + query = membershipQuery + " AND (" + ACTIVE + " = ? OR " + RECIPIENT_ID + " IN (SELECT " + ThreadDatabase.RECIPIENT_ID + " FROM " + ThreadDatabase.TABLE_NAME + "))"; + queryArgs = SqlUtil.buildArgs(1); + } else { + query = membershipQuery + " AND " + ACTIVE + " = ?"; + queryArgs = SqlUtil.buildArgs(1); + } + + if (excludeV1) { + query += " AND " + EXPECTED_V2_ID + " IS NULL"; + } + + if (excludeMms) { + query += " AND " + MMS + " = 0"; + } + + return new Reader(getReadableDatabase().query(TABLE_NAME, null, query, queryArgs, null, null, null)); + } + public Reader queryGroupsByRecency(@NonNull GroupQuery groupQuery) { SqlUtil.Query query = getGroupQueryWhereStatement(groupQuery.searchQuery, groupQuery.includeInactive, !groupQuery.includeV1, !groupQuery.includeMms); String sql = "SELECT * FROM " + TABLE_NAME + diff --git a/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java b/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java index 404c800408..be6de4953a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java @@ -10,6 +10,7 @@ import androidx.annotation.Nullable; import com.annimon.stream.Stream; +import org.signal.core.util.CursorUtil; import org.signal.core.util.concurrent.LatestPrioritizedSerialExecutor; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; @@ -29,7 +30,6 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.CursorUtil; import org.thoughtcrime.securesms.util.FtsUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.SerialExecutor; @@ -161,11 +161,13 @@ public class SearchRepository { Set recipientIds = new LinkedHashSet<>(); + Set filteredContacts = new LinkedHashSet<>(); try (Cursor cursor = SignalDatabase.recipients().queryAllContacts(query)) { while (cursor != null && cursor.moveToNext()) { - recipientIds.add(RecipientId.from(CursorUtil.requireString(cursor, RecipientDatabase.ID))); + filteredContacts.add(RecipientId.from(CursorUtil.requireString(cursor, RecipientDatabase.ID))); } } + recipientIds.addAll(filteredContacts); GroupDatabase.GroupRecord record; try (GroupDatabase.Reader reader = SignalDatabase.groups().queryGroupsByTitle(query, true, false, false)) { @@ -174,6 +176,12 @@ public class SearchRepository { } } + try (GroupDatabase.Reader reader = SignalDatabase.groups().queryGroupsByMembership(filteredContacts, true, false, false)) { + while ((record = reader.getNext()) != null) { + recipientIds.add(record.getRecipientId()); + } + } + if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) { recipientIds.add(Recipient.self().getId()); }