mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-23 19:26:17 +00:00
Fix note to self sorting by profile name in search bug.
This commit is contained in:
committed by
Greyson Parrelli
parent
e2b0567534
commit
ecc573f6b5
@@ -27,7 +27,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
|
||||
SignalDatabase.recipients.markHidden(hiddenRecipient)
|
||||
|
||||
val results = SignalDatabase.recipients.queryAllContacts("Hidden")!!
|
||||
val results = SignalDatabase.recipients.queryAllContacts("Hidden", RecipientTable.IncludeSelfMode.Exclude)!!
|
||||
|
||||
assertEquals(1, results.count)
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
|
||||
SignalDatabase.recipients.markHidden(hiddenRecipient)
|
||||
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getSignalContacts(false)?.use {
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use {
|
||||
val ids = mutableListOf<RecipientId>()
|
||||
while (it.moveToNext()) {
|
||||
ids.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID)))
|
||||
@@ -57,18 +57,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
|
||||
SignalDatabase.recipients.markHidden(hiddenRecipient)
|
||||
|
||||
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Hidden", false))!!
|
||||
|
||||
assertEquals(0, results.count)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenAHiddenRecipient_whenIQueryNonGroupContacts_thenIDoNotExpectHiddenToBeReturned() {
|
||||
val hiddenRecipient = harness.others[0]
|
||||
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
|
||||
SignalDatabase.recipients.markHidden(hiddenRecipient)
|
||||
|
||||
val results = SignalDatabase.recipients.queryNonGroupContacts("Hidden", false)!!
|
||||
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Hidden", RecipientTable.IncludeSelfMode.Exclude))!!
|
||||
|
||||
assertEquals(0, results.count)
|
||||
}
|
||||
@@ -79,7 +68,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(hiddenRecipient, ProfileName.fromParts("Hidden", "Person"))
|
||||
SignalDatabase.recipients.markHidden(hiddenRecipient)
|
||||
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getNonGroupContacts(false)?.use {
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getNonGroupContacts(RecipientTable.IncludeSelfMode.Exclude)?.use {
|
||||
val ids = mutableListOf<RecipientId>()
|
||||
while (it.moveToNext()) {
|
||||
ids.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID)))
|
||||
@@ -98,7 +87,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
|
||||
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
|
||||
|
||||
val results = SignalDatabase.recipients.queryAllContacts("Blocked")!!
|
||||
val results = SignalDatabase.recipients.queryAllContacts("Blocked", RecipientTable.IncludeSelfMode.Exclude)!!
|
||||
|
||||
assertEquals(0, results.count)
|
||||
}
|
||||
@@ -109,14 +98,14 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
|
||||
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
|
||||
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getSignalContacts(false)?.use {
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use {
|
||||
val ids = mutableListOf<RecipientId>()
|
||||
while (it.moveToNext()) {
|
||||
ids.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID)))
|
||||
}
|
||||
|
||||
ids
|
||||
}!!
|
||||
}
|
||||
|
||||
assertNotEquals(0, results.size)
|
||||
assertFalse(blockedRecipient in results)
|
||||
@@ -128,18 +117,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
|
||||
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
|
||||
|
||||
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Blocked", false))!!
|
||||
|
||||
assertEquals(0, results.count)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenABlockedRecipient_whenIQueryNonGroupContacts_thenIDoNotExpectBlockedToBeReturned() {
|
||||
val blockedRecipient = harness.others[0]
|
||||
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
|
||||
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
|
||||
|
||||
val results = SignalDatabase.recipients.queryNonGroupContacts("Blocked", false)!!
|
||||
val results = SignalDatabase.recipients.querySignalContacts(RecipientTable.ContactSearchQuery("Blocked", RecipientTable.IncludeSelfMode.Exclude))!!
|
||||
|
||||
assertEquals(0, results.count)
|
||||
}
|
||||
@@ -150,7 +128,7 @@ class RecipientTableTest {
|
||||
SignalDatabase.recipients.setProfileName(blockedRecipient, ProfileName.fromParts("Blocked", "Person"))
|
||||
SignalDatabase.recipients.setBlocked(blockedRecipient, true)
|
||||
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getNonGroupContacts(false)?.use {
|
||||
val results: MutableList<RecipientId> = SignalDatabase.recipients.getNonGroupContacts(RecipientTable.IncludeSelfMode.Exclude)?.use {
|
||||
val ids = mutableListOf<RecipientId>()
|
||||
while (it.moveToNext()) {
|
||||
ids.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID)))
|
||||
|
||||
@@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator;
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchSortOrder;
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState;
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -951,7 +952,7 @@ public final class ContactSelectionListFragment extends LoggingFragment {
|
||||
|
||||
boolean hideHeader = newCallCallback != null || (newConversationCallback != null && !hasQuery);
|
||||
builder.addSection(new ContactSearchConfiguration.Section.Individuals(
|
||||
includeSelf,
|
||||
includeSelf ? new RecipientTable.IncludeSelfMode.IncludeWithRemap(getString(R.string.note_to_self)) : RecipientTable.IncludeSelfMode.Exclude.INSTANCE,
|
||||
transportType,
|
||||
!hideHeader,
|
||||
null,
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
@@ -64,7 +65,7 @@ class GiftFlowRecipientSelectionFragment : Fragment(R.layout.gift_flow_recipient
|
||||
|
||||
addSection(
|
||||
ContactSearchConfiguration.Section.Individuals(
|
||||
includeSelf = false,
|
||||
includeSelfMode = RecipientTable.IncludeSelfMode.Exclude,
|
||||
transportType = ContactSearchConfiguration.TransportType.PUSH,
|
||||
includeHeader = true
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.usernamelinks.main
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
@@ -34,7 +33,6 @@ class QrImageSelectionActivity : AppCompatActivity(), MediaGalleryFragment.Callb
|
||||
setContentView(R.layout.username_qr_image_selection_activity)
|
||||
}
|
||||
|
||||
@SuppressLint("LogTagInlined")
|
||||
override fun onMediaSelected(media: Media) {
|
||||
setResult(RESULT_OK, Intent().setData(media.uri))
|
||||
finish()
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.signal.libsignal.protocol.util.Pair;
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchSortOrder;
|
||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -35,7 +31,6 @@ public class ContactRepository {
|
||||
|
||||
private final RecipientTable recipientTable;
|
||||
private final String noteToSelfTitle;
|
||||
private final Context context;
|
||||
|
||||
public static final String ID_COLUMN = "id";
|
||||
public static final String NAME_COLUMN = "name";
|
||||
@@ -46,11 +41,7 @@ public class ContactRepository {
|
||||
static final String ABOUT_COLUMN = "about";
|
||||
|
||||
static final int NORMAL_TYPE = 0;
|
||||
static final int PUSH_TYPE = 1 << 0;
|
||||
static final int NEW_PHONE_TYPE = 1 << 2;
|
||||
static final int NEW_USERNAME_TYPE = 1 << 3;
|
||||
static final int RECENT_TYPE = 1 << 4;
|
||||
static final int DIVIDER_TYPE = 1 << 5;
|
||||
static final int PUSH_TYPE = 1;
|
||||
|
||||
/** Maps the recipient results to the legacy contact column names */
|
||||
private static final List<Pair<String, ValueMapper>> SEARCH_CURSOR_MAPPERS = new ArrayList<Pair<String, ValueMapper>>() {{
|
||||
@@ -101,34 +92,21 @@ public class ContactRepository {
|
||||
}));
|
||||
}};
|
||||
|
||||
public ContactRepository(@NonNull Context context, @NonNull String noteToSelfTitle) {
|
||||
this.recipientTable = SignalDatabase.recipients();
|
||||
public ContactRepository(@NonNull String noteToSelfTitle) {
|
||||
this.noteToSelfTitle = noteToSelfTitle;
|
||||
this.context = context.getApplicationContext();
|
||||
this.recipientTable = SignalDatabase.recipients();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public @NonNull Cursor querySignalContacts(@NonNull String query) {
|
||||
return querySignalContacts(new RecipientTable.ContactSearchQuery(query, true, ContactSearchSortOrder.NATURAL));
|
||||
return querySignalContacts(new RecipientTable.ContactSearchQuery(query, new RecipientTable.IncludeSelfMode.IncludeWithRemap(noteToSelfTitle), ContactSearchSortOrder.NATURAL));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public @NonNull Cursor querySignalContacts(@NonNull RecipientTable.ContactSearchQuery contactSearchQuery) {
|
||||
Cursor cursor = TextUtils.isEmpty(contactSearchQuery.getQuery()) ? recipientTable.getSignalContacts(contactSearchQuery.getIncludeSelf())
|
||||
Cursor cursor = TextUtils.isEmpty(contactSearchQuery.getQuery()) ? recipientTable.getSignalContacts(contactSearchQuery.getIncludeSelfMode())
|
||||
: recipientTable.querySignalContacts(contactSearchQuery);
|
||||
|
||||
cursor = handleNoteToSelfQuery(contactSearchQuery.getQuery(), contactSearchQuery.getIncludeSelf(), cursor);
|
||||
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public @NonNull Cursor queryNonGroupContacts(@NonNull String query, boolean includeSelf) {
|
||||
Cursor cursor = TextUtils.isEmpty(query) ? recipientTable.getNonGroupContacts(includeSelf)
|
||||
: recipientTable.queryNonGroupContacts(query, includeSelf);
|
||||
|
||||
cursor = handleNoteToSelfQuery(query, includeSelf, cursor);
|
||||
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
@@ -140,31 +118,6 @@ public class ContactRepository {
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
private @NonNull Cursor handleNoteToSelfQuery(@NonNull String query, boolean includeSelf, Cursor cursor) {
|
||||
if (includeSelf && noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) {
|
||||
Recipient self = Recipient.self();
|
||||
boolean nameMatch = self.getDisplayName(context).toLowerCase().contains(query.toLowerCase());
|
||||
boolean numberMatch = self.getE164().isPresent() && self.requireE164().contains(query);
|
||||
boolean shouldAdd = !nameMatch && !numberMatch;
|
||||
|
||||
if (shouldAdd) {
|
||||
MatrixCursor selfCursor = new MatrixCursor(RecipientTable.SEARCH_PROJECTION_NAMES);
|
||||
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, self.getE164().orElse(""), self.getEmail().orElse(null), null, -1, RecipientTable.RegisteredState.REGISTERED.getId(), self.getAbout(), self.getAboutEmoji(), null, true, noteToSelfTitle, noteToSelfTitle });
|
||||
|
||||
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
||||
}
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public Cursor queryNonSignalContacts(@NonNull String query) {
|
||||
Cursor cursor = TextUtils.isEmpty(query) ? recipientTable.getNonSignalContacts()
|
||||
: recipientTable.queryNonSignalContacts(query);
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This lets us mock the legacy cursor interface while using the new cursor, even though the data
|
||||
* doesn't quite match up exactly.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.contacts.paged
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.HeaderAction
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
|
||||
/**
|
||||
* A strongly typed descriptor of how a given list of contacts should be formatted
|
||||
@@ -76,7 +77,7 @@ class ContactSearchConfiguration private constructor(
|
||||
* Model: [ContactSearchAdapter.RecipientModel]
|
||||
*/
|
||||
data class Individuals(
|
||||
val includeSelf: Boolean,
|
||||
val includeSelfMode: RecipientTable.IncludeSelfMode,
|
||||
val transportType: TransportType,
|
||||
override val includeHeader: Boolean,
|
||||
override val expandConfig: ExpandConfig? = null,
|
||||
|
||||
@@ -213,7 +213,7 @@ class ContactSearchPagedDataSource(
|
||||
}
|
||||
|
||||
private fun getNonGroupSearchIterator(section: ContactSearchConfiguration.Section.Individuals, query: String?): ContactSearchIterator<Cursor> {
|
||||
val searchQuery = RecipientTable.ContactSearchQuery(query ?: "", section.includeSelf, section.pushSearchResultsSortOrder)
|
||||
val searchQuery = RecipientTable.ContactSearchQuery(query ?: "", section.includeSelfMode, section.pushSearchResultsSortOrder)
|
||||
return CursorSearchIterator(wrapRecipientCursor(contactSearchPagedDataSourceRepository.querySignalContacts(searchQuery)))
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ class ContactSearchPagedDataSource(
|
||||
private fun getNonGroupHeaderLetterMap(section: ContactSearchConfiguration.Section.Individuals, query: String?): Map<RecipientId, String> {
|
||||
return contactSearchPagedDataSourceRepository.querySignalContactLetterHeaders(
|
||||
query = query,
|
||||
includeSelf = section.includeSelf,
|
||||
includeSelfMode = section.includeSelfMode,
|
||||
includePush = when (section.transportType) {
|
||||
ContactSearchConfiguration.TransportType.PUSH, ContactSearchConfiguration.TransportType.ALL -> true
|
||||
else -> false
|
||||
|
||||
@@ -26,7 +26,7 @@ open class ContactSearchPagedDataSourceRepository(
|
||||
context: Context
|
||||
) {
|
||||
|
||||
private val contactRepository = ContactRepository(context, context.getString(R.string.note_to_self))
|
||||
private val contactRepository = ContactRepository(context.getString(R.string.note_to_self))
|
||||
private val context = context.applicationContext
|
||||
|
||||
open fun getLatestStorySends(activeStoryCutoffDuration: Long): List<StorySend> {
|
||||
@@ -38,16 +38,8 @@ open class ContactSearchPagedDataSourceRepository(
|
||||
return contactRepository.querySignalContacts(contactsSearchQuery)
|
||||
}
|
||||
|
||||
open fun querySignalContactLetterHeaders(query: String?, includeSelf: Boolean, includePush: Boolean, includeSms: Boolean): Map<RecipientId, String> {
|
||||
return SignalDatabase.recipients.querySignalContactLetterHeaders(query ?: "", includeSelf, includePush, includeSms)
|
||||
}
|
||||
|
||||
open fun queryNonSignalContacts(query: String?): Cursor? {
|
||||
return contactRepository.queryNonSignalContacts(query ?: "")
|
||||
}
|
||||
|
||||
open fun queryNonGroupContacts(query: String?, includeSelf: Boolean): Cursor? {
|
||||
return contactRepository.queryNonGroupContacts(query ?: "", includeSelf)
|
||||
open fun querySignalContactLetterHeaders(query: String?, includeSelfMode: RecipientTable.IncludeSelfMode, includePush: Boolean, includeSms: Boolean): Map<RecipientId, String> {
|
||||
return SignalDatabase.recipients.querySignalContactLetterHeaders(query ?: "", includeSelfMode, includePush, includeSms)
|
||||
}
|
||||
|
||||
open fun queryGroupMemberContacts(query: String?): Cursor? {
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchError
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomSheet
|
||||
@@ -469,7 +470,7 @@ class MultiselectForwardFragment :
|
||||
ContactSearchConfiguration.Section.Individuals(
|
||||
includeHeader = true,
|
||||
transportType = ContactSearchConfiguration.TransportType.PUSH,
|
||||
includeSelf = true
|
||||
includeSelfMode = RecipientTable.IncludeSelfMode.IncludeWithRemap(getString(R.string.note_to_self))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -414,13 +414,13 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
|
||||
return when (privacyMode) {
|
||||
DistributionListPrivacyMode.ALL -> {
|
||||
SignalDatabase.recipients
|
||||
.getSignalContacts(false)!!
|
||||
.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude)
|
||||
.readToList { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
|
||||
}
|
||||
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
|
||||
DistributionListPrivacyMode.ALL_EXCEPT -> {
|
||||
SignalDatabase.recipients
|
||||
.getSignalContacts(false)!!
|
||||
.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude)
|
||||
.readToList(
|
||||
predicate = { !rawMembers.contains(it) },
|
||||
mapper = { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
|
||||
@@ -453,7 +453,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
|
||||
readableDatabase.withinTransaction {
|
||||
privacyMode = getPrivacyMode(listId)
|
||||
rawMemberCount = getRawMemberCount(listId, privacyMode)
|
||||
totalContactCount = SignalDatabase.recipients.getSignalContactsCount(false)
|
||||
totalContactCount = SignalDatabase.recipients.getSignalContactsCount(RecipientTable.IncludeSelfMode.Exclude)
|
||||
}
|
||||
|
||||
val memberCount = when (privacyMode) {
|
||||
|
||||
@@ -341,7 +341,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
}
|
||||
|
||||
fun queryGroupsByMemberName(inputQuery: String): Cursor {
|
||||
val subquery = recipients.getAllContactsSubquery(inputQuery)
|
||||
val subquery = recipients.getAllContactsSubquery(inputQuery, RecipientTable.IncludeSelfMode.IncludeWithoutRemap)
|
||||
val statement = """
|
||||
SELECT
|
||||
DISTINCT $TABLE_NAME.*,
|
||||
|
||||
@@ -338,7 +338,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
|
||||
private val ID_PROJECTION = arrayOf(ID)
|
||||
|
||||
private val SEARCH_PROJECTION = arrayOf(
|
||||
private val SEARCH_PROJECTION_WITHOUT_SELF_REMAP = arrayOf(
|
||||
ID,
|
||||
SYSTEM_JOINED_NAME,
|
||||
E164,
|
||||
@@ -3335,23 +3335,63 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
}
|
||||
}
|
||||
|
||||
fun getSignalContacts(includeSelf: Boolean): Cursor? {
|
||||
return getSignalContacts(includeSelf, "$SORT_NAME, $SYSTEM_JOINED_NAME, $SEARCH_PROFILE_NAME, $USERNAME, $E164")
|
||||
fun getSignalContacts(includeSelfMode: IncludeSelfMode): Cursor {
|
||||
return getSignalContacts(includeSelfMode, "$SORT_NAME, $SYSTEM_JOINED_NAME, $SEARCH_PROFILE_NAME, $USERNAME, $E164")
|
||||
}
|
||||
|
||||
fun getSignalContactsCount(includeSelf: Boolean): Int {
|
||||
return getSignalContacts(includeSelf)?.count ?: 0
|
||||
fun getSignalContactsCount(includeSelfMode: IncludeSelfMode): Int {
|
||||
return getSignalContacts(includeSelfMode).count
|
||||
}
|
||||
|
||||
private fun getSignalContacts(includeSelf: Boolean, orderBy: String? = null): Cursor? {
|
||||
private fun searchProjection(includeSelfMode: IncludeSelfMode): Array<String> {
|
||||
return when (includeSelfMode) {
|
||||
is IncludeSelfMode.IncludeWithRemap -> {
|
||||
val selfId = Recipient.self().id.toLong()
|
||||
arrayOf(
|
||||
ID,
|
||||
"""CASE WHEN ${TABLE_NAME}.$ID = $selfId THEN '${includeSelfMode.noteToSelfTitle}' ELSE $SYSTEM_JOINED_NAME END AS $SYSTEM_JOINED_NAME""",
|
||||
E164,
|
||||
EMAIL,
|
||||
SYSTEM_PHONE_LABEL,
|
||||
SYSTEM_PHONE_TYPE,
|
||||
REGISTERED,
|
||||
ABOUT,
|
||||
ABOUT_EMOJI,
|
||||
EXTRAS,
|
||||
GROUPS_IN_COMMON,
|
||||
"""CASE WHEN ${TABLE_NAME}.$ID = $selfId THEN '${includeSelfMode.noteToSelfTitle}' ELSE COALESCE(NULLIF($PROFILE_JOINED_NAME, ''), NULLIF($PROFILE_GIVEN_NAME, '')) END AS $SEARCH_PROFILE_NAME""",
|
||||
"""
|
||||
CASE WHEN ${TABLE_NAME}.$ID = $selfId THEN '${includeSelfMode.noteToSelfTitle.lowercase()}' ELSE
|
||||
LOWER(
|
||||
COALESCE(
|
||||
NULLIF($NICKNAME_JOINED_NAME, ''),
|
||||
NULLIF($NICKNAME_GIVEN_NAME, ''),
|
||||
NULLIF($SYSTEM_JOINED_NAME, ''),
|
||||
NULLIF($SYSTEM_GIVEN_NAME, ''),
|
||||
NULLIF($PROFILE_JOINED_NAME, ''),
|
||||
NULLIF($PROFILE_GIVEN_NAME, ''),
|
||||
NULLIF($USERNAME, '')
|
||||
)
|
||||
) END AS $SORT_NAME
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
IncludeSelfMode.IncludeWithoutRemap,
|
||||
IncludeSelfMode.Exclude -> SEARCH_PROJECTION_WITHOUT_SELF_REMAP
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSignalContacts(includeSelfMode: IncludeSelfMode, orderBy: String? = null): Cursor {
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withRegistered(true)
|
||||
.withGroups(false)
|
||||
.excludeId(if (includeSelf) null else Recipient.self().id)
|
||||
.excludeId(if (includeSelfMode.includeSelf) null else Recipient.self().id)
|
||||
.build()
|
||||
val selection = searchSelection.where
|
||||
val args = searchSelection.args
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
|
||||
return readableDatabase.query(TABLE_NAME, searchProjection(includeSelfMode), selection, args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun querySignalContacts(contactSearchQuery: ContactSearchQuery): Cursor? {
|
||||
@@ -3360,7 +3400,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withRegistered(true)
|
||||
.withGroups(false)
|
||||
.excludeId(if (contactSearchQuery.includeSelf) null else Recipient.self().id)
|
||||
.excludeId(if (contactSearchQuery.includeSelfMode.includeSelf) null else Recipient.self().id)
|
||||
.withSearchQuery(query)
|
||||
.build()
|
||||
val selection = searchSelection.where
|
||||
@@ -3376,7 +3416,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
|
||||
return if (contactSearchQuery.contactSearchSortOrder == ContactSearchSortOrder.RECENCY) {
|
||||
val ambiguous = listOf(ID)
|
||||
val projection = SEARCH_PROJECTION.map {
|
||||
val projection = searchProjection(contactSearchQuery.includeSelfMode).map {
|
||||
if (it in ambiguous) "$TABLE_NAME.$it" else it
|
||||
} + "${ThreadTable.TABLE_NAME}.${ThreadTable.DATE}"
|
||||
|
||||
@@ -3392,16 +3432,16 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
args
|
||||
)
|
||||
} else {
|
||||
readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
readableDatabase.query(TABLE_NAME, searchProjection(contactSearchQuery.includeSelfMode), selection, args, null, null, orderBy)
|
||||
}
|
||||
}
|
||||
|
||||
fun querySignalContactLetterHeaders(inputQuery: String, includeSelf: Boolean, includePush: Boolean, includeSms: Boolean): Map<RecipientId, String> {
|
||||
fun querySignalContactLetterHeaders(inputQuery: String, includeSelfMode: IncludeSelfMode, includePush: Boolean, includeSms: Boolean): Map<RecipientId, String> {
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withRegistered(includePush)
|
||||
.withNonRegistered(includeSms)
|
||||
.withGroups(false)
|
||||
.excludeId(if (includeSelf) null else Recipient.self().id)
|
||||
.excludeId(if (includeSelfMode.includeSelf) null else Recipient.self().id)
|
||||
.withSearchQuery(inputQuery)
|
||||
.build()
|
||||
|
||||
@@ -3411,7 +3451,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
_id,
|
||||
UPPER(SUBSTR($SORT_NAME, 0, 2)) AS letter_header
|
||||
FROM (
|
||||
SELECT ${SEARCH_PROJECTION.joinToString(", ")}
|
||||
SELECT ${searchProjection(includeSelfMode).joinToString(", ")}
|
||||
FROM recipient
|
||||
WHERE ${searchSelection.where}
|
||||
ORDER BY $SORT_NAME, $SYSTEM_JOINED_NAME, $SEARCH_PROFILE_NAME, $E164
|
||||
@@ -3435,55 +3475,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
}
|
||||
}
|
||||
|
||||
fun getNonSignalContacts(): Cursor? {
|
||||
val searchSelection = ContactSearchSelection.Builder().withNonRegistered(true)
|
||||
.withGroups(false)
|
||||
.build()
|
||||
val selection = searchSelection.where
|
||||
val args = searchSelection.args
|
||||
val orderBy = "$SYSTEM_JOINED_NAME, $E164"
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun queryNonSignalContacts(inputQuery: String): Cursor? {
|
||||
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withNonRegistered(true)
|
||||
.withGroups(false)
|
||||
.withSearchQuery(query)
|
||||
.build()
|
||||
val selection = searchSelection.where
|
||||
val args = searchSelection.args
|
||||
val orderBy = "$SYSTEM_JOINED_NAME, $E164"
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun getNonGroupContacts(includeSelf: Boolean): Cursor? {
|
||||
fun getNonGroupContacts(includeSelfMode: IncludeSelfMode): Cursor? {
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withRegistered(true)
|
||||
.withNonRegistered(true)
|
||||
.withGroups(false)
|
||||
.excludeId(if (includeSelf) null else Recipient.self().id)
|
||||
.excludeId(if (includeSelfMode.includeSelf) null else Recipient.self().id)
|
||||
.build()
|
||||
val orderBy = orderByPreferringAlphaOverNumeric(SORT_NAME) + ", " + E164
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, searchSelection.where, searchSelection.args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun queryNonGroupContacts(inputQuery: String, includeSelf: Boolean): Cursor? {
|
||||
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||
|
||||
val searchSelection = ContactSearchSelection.Builder()
|
||||
.withRegistered(true)
|
||||
.withNonRegistered(true)
|
||||
.withGroups(false)
|
||||
.excludeId(if (includeSelf) null else Recipient.self().id)
|
||||
.withSearchQuery(query)
|
||||
.build()
|
||||
val selection = searchSelection.where
|
||||
val args = searchSelection.args
|
||||
val orderBy = orderByPreferringAlphaOverNumeric(SORT_NAME) + ", " + E164
|
||||
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
return readableDatabase.query(TABLE_NAME, searchProjection(includeSelfMode), searchSelection.where, searchSelection.args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun getGroupMemberContacts(): Cursor? {
|
||||
@@ -3493,7 +3493,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
.build()
|
||||
|
||||
val orderBy = orderByPreferringAlphaOverNumeric(SORT_NAME) + ", " + E164
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, searchSelection.where, searchSelection.args, null, null, orderBy)
|
||||
return readableDatabase.query(TABLE_NAME, searchProjection(IncludeSelfMode.Exclude), searchSelection.where, searchSelection.args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun queryGroupMemberContacts(inputQuery: String): Cursor? {
|
||||
@@ -3508,10 +3508,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
val args = searchSelection.args
|
||||
val orderBy = orderByPreferringAlphaOverNumeric(SORT_NAME) + ", " + E164
|
||||
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy)
|
||||
return readableDatabase.query(TABLE_NAME, searchProjection(IncludeSelfMode.Exclude), selection, args, null, null, orderBy)
|
||||
}
|
||||
|
||||
fun queryAllContacts(inputQuery: String): Cursor? {
|
||||
fun queryAllContacts(inputQuery: String, includeSelfMode: IncludeSelfMode): Cursor? {
|
||||
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||
val selection =
|
||||
"""
|
||||
@@ -3524,18 +3524,18 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
)
|
||||
"""
|
||||
val args = SqlUtil.buildArgs(0, query, query, query, query)
|
||||
return readableDatabase.query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, null)
|
||||
return readableDatabase.query(TABLE_NAME, searchProjection(includeSelfMode), selection, args, null, null, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query used for performing the all contacts search so that it can be injected as a subquery.
|
||||
*/
|
||||
fun getAllContactsSubquery(inputQuery: String): SqlUtil.Query {
|
||||
fun getAllContactsSubquery(inputQuery: String, includeSelfMode: IncludeSelfMode): SqlUtil.Query {
|
||||
val query = SqlUtil.buildCaseInsensitiveGlobPattern(inputQuery)
|
||||
|
||||
//language=sql
|
||||
val subquery = """SELECT $ID FROM (
|
||||
SELECT ${SEARCH_PROJECTION.joinToString(",")} FROM $TABLE_NAME
|
||||
SELECT ${searchProjection(includeSelfMode).joinToString(",")} FROM $TABLE_NAME
|
||||
WHERE $BLOCKED = ? AND $HIDDEN = ? AND
|
||||
(
|
||||
$SORT_NAME GLOB ? OR
|
||||
@@ -3556,7 +3556,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
|
||||
//language=sql
|
||||
val subquery = """
|
||||
SELECT ${SEARCH_PROJECTION.joinToString(", ")} FROM $TABLE_NAME
|
||||
SELECT ${searchProjection(IncludeSelfMode.Exclude).joinToString(", ")} FROM $TABLE_NAME
|
||||
WHERE $BLOCKED = ? AND $HIDDEN = ? AND $REGISTERED != ? AND NOT EXISTS (SELECT 1 FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.TABLE_NAME}.${ThreadTable.ACTIVE} = 1 AND ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = $TABLE_NAME.$ID LIMIT 1)
|
||||
AND (
|
||||
$SORT_NAME GLOB ? OR
|
||||
@@ -4473,10 +4473,19 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
|
||||
data class ContactSearchQuery(
|
||||
val query: String,
|
||||
val includeSelf: Boolean,
|
||||
val includeSelfMode: IncludeSelfMode,
|
||||
val contactSearchSortOrder: ContactSearchSortOrder = ContactSearchSortOrder.NATURAL
|
||||
)
|
||||
|
||||
sealed interface IncludeSelfMode {
|
||||
val includeSelf: Boolean
|
||||
get() = this is IncludeWithRemap || this == IncludeWithoutRemap
|
||||
|
||||
data object Exclude : IncludeSelfMode
|
||||
data object IncludeWithoutRemap : IncludeSelfMode
|
||||
data class IncludeWithRemap(val noteToSelfTitle: String) : IncludeSelfMode
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal class ContactSearchSelection private constructor(val where: String, val args: Array<String>) {
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class CameraContactsRepository {
|
||||
this.threadTable = SignalDatabase.threads();
|
||||
this.groupDatabase = SignalDatabase.groups();
|
||||
this.recipientTable = SignalDatabase.recipients();
|
||||
this.contactRepository = new ContactRepository(context, context.getString(R.string.note_to_self));
|
||||
this.contactRepository = new ContactRepository(context.getString(R.string.note_to_self));
|
||||
this.serialExecutor = SignalExecutors.SERIAL;
|
||||
this.parallelExecutor = SignalExecutors.BOUNDED;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public final class LiveRecipientCache {
|
||||
stopwatch.split("thread");
|
||||
|
||||
if (SignalStore.registration().isRegistrationComplete() && SignalStore.account().getAci() != null) {
|
||||
try (Cursor cursor = SignalDatabase.recipients().getNonGroupContacts(false)) {
|
||||
try (Cursor cursor = SignalDatabase.recipients().getNonGroupContacts(RecipientTable.IncludeSelfMode.Exclude.INSTANCE)) {
|
||||
int count = 0;
|
||||
while (cursor != null && cursor.moveToNext() && count < CONTACT_CACHE_WARM_MAX) {
|
||||
RecipientId id = RecipientId.from(CursorUtil.requireLong(cursor, RecipientTable.ID));
|
||||
|
||||
@@ -79,7 +79,7 @@ public class SearchRepository {
|
||||
this.recipientTable = SignalDatabase.recipients();
|
||||
this.mentionTable = SignalDatabase.mentions();
|
||||
this.messageTable = SignalDatabase.messages();
|
||||
this.contactRepository = new ContactRepository(context, noteToSelfTitle);
|
||||
this.contactRepository = new ContactRepository(noteToSelfTitle);
|
||||
this.serialExecutor = new SerialExecutor(SignalExecutors.BOUNDED);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class SearchRepository {
|
||||
}
|
||||
|
||||
Set<RecipientId> filteredContacts = new LinkedHashSet<>();
|
||||
try (Cursor cursor = SignalDatabase.recipients().queryAllContacts(query)) {
|
||||
try (Cursor cursor = SignalDatabase.recipients().queryAllContacts(query, RecipientTable.IncludeSelfMode.IncludeWithoutRemap.INSTANCE)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
filteredContacts.add(RecipientId.from(CursorUtil.requireString(cursor, RecipientTable.ID)));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchAdapter
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.databinding.ViewAllSignalConnectionsFragmentBinding
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
|
||||
@@ -43,7 +44,7 @@ class ViewAllSignalConnectionsFragment : Fragment(R.layout.view_all_signal_conne
|
||||
addSection(
|
||||
ContactSearchConfiguration.Section.Individuals(
|
||||
includeHeader = false,
|
||||
includeSelf = false,
|
||||
includeSelfMode = RecipientTable.IncludeSelfMode.Exclude,
|
||||
includeLetterHeaders = true,
|
||||
transportType = ContactSearchConfiguration.TransportType.PUSH
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyData
|
||||
@@ -56,7 +57,7 @@ class MyStorySettingsRepository {
|
||||
|
||||
fun getAllSignalConnectionsCount(): Single<Int> {
|
||||
return Single.fromCallable {
|
||||
SignalDatabase.recipients.getSignalContactsCount(false)
|
||||
SignalDatabase.recipients.getSignalContactsCount(RecipientTable.IncludeSelfMode.Exclude)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class BaseStoryRecipientSelectionRepository {
|
||||
|
||||
fun getAllSignalContacts(): Single<Set<RecipientId>> {
|
||||
return Single.fromCallable {
|
||||
SignalDatabase.recipients.getSignalContacts(false)?.use {
|
||||
SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use {
|
||||
val recipientSet = mutableSetOf<RecipientId>()
|
||||
while (it.moveToNext()) {
|
||||
recipientSet.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID)))
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.thoughtcrime.securesms.MockCursor
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipientCache
|
||||
@@ -194,7 +195,7 @@ class ContactSearchPagedDataSourceTest {
|
||||
addSection(
|
||||
ContactSearchConfiguration.Section.Individuals(
|
||||
includeHeader = true,
|
||||
includeSelf = false,
|
||||
includeSelfMode = RecipientTable.IncludeSelfMode.Exclude,
|
||||
transportType = ContactSearchConfiguration.TransportType.ALL,
|
||||
expandConfig = ContactSearchConfiguration.ExpandConfig(isExpanded = false)
|
||||
)
|
||||
@@ -202,7 +203,6 @@ class ContactSearchPagedDataSourceTest {
|
||||
}
|
||||
|
||||
every { repository.getRecents(recents) } returns cursor
|
||||
every { repository.queryNonGroupContacts(isNull(), any()) } returns cursor
|
||||
every { repository.querySignalContacts(any()) } returns cursor
|
||||
every { cursor.count } returns 10
|
||||
|
||||
|
||||
Reference in New Issue
Block a user