From ecc573f6b5d787d18d5f1cdaff99ed03c602359a Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 6 Feb 2025 12:56:09 -0500 Subject: [PATCH] Fix note to self sorting by profile name in search bug. --- .../securesms/database/RecipientTableTest.kt | 40 ++--- .../ContactSelectionListFragment.java | 3 +- .../GiftFlowRecipientSelectionFragment.kt | 3 +- .../main/QrImageSelectionActivity.kt | 2 - .../securesms/contacts/ContactRepository.java | 59 +------- .../paged/ContactSearchConfiguration.kt | 3 +- .../paged/ContactSearchPagedDataSource.kt | 4 +- .../ContactSearchPagedDataSourceRepository.kt | 14 +- .../forward/MultiselectForwardFragment.kt | 3 +- .../database/DistributionListTables.kt | 6 +- .../securesms/database/GroupTable.kt | 2 +- .../securesms/database/RecipientTable.kt | 139 ++++++++++-------- .../mediasend/CameraContactsRepository.java | 2 +- .../recipients/LiveRecipientCache.java | 2 +- .../securesms/search/SearchRepository.java | 4 +- .../ViewAllSignalConnectionsFragment.kt | 3 +- .../settings/my/MyStorySettingsRepository.kt | 3 +- .../BaseStoryRecipientSelectionRepository.kt | 2 +- .../paged/ContactSearchPagedDataSourceTest.kt | 4 +- 19 files changed, 117 insertions(+), 181 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest.kt index 64d480e6d4..11c8c6b8cb 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest.kt @@ -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 = SignalDatabase.recipients.getSignalContacts(false)?.use { + val results: MutableList = SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use { val ids = mutableListOf() 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 = SignalDatabase.recipients.getNonGroupContacts(false)?.use { + val results: MutableList = SignalDatabase.recipients.getNonGroupContacts(RecipientTable.IncludeSelfMode.Exclude)?.use { val ids = mutableListOf() 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 = SignalDatabase.recipients.getSignalContacts(false)?.use { + val results: MutableList = SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use { val ids = mutableListOf() 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 = SignalDatabase.recipients.getNonGroupContacts(false)?.use { + val results: MutableList = SignalDatabase.recipients.getNonGroupContacts(RecipientTable.IncludeSelfMode.Exclude)?.use { val ids = mutableListOf() while (it.moveToNext()) { ids.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID))) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java index 46ef1de7bc..e632478cda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java @@ -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, diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRecipientSelectionFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRecipientSelectionFragment.kt index e2c9733f04..d1c362ec83 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRecipientSelectionFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRecipientSelectionFragment.kt @@ -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 ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/QrImageSelectionActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/QrImageSelectionActivity.kt index 4ae8ed625b..d954f18991 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/QrImageSelectionActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/QrImageSelectionActivity.kt @@ -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() diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactRepository.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactRepository.java index 957f4e732d..348f08c899 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactRepository.java @@ -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> SEARCH_CURSOR_MAPPERS = new ArrayList>() {{ @@ -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. diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt index e17a2575bc..4ee3033c12 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration.kt @@ -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, diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt index 4983cc8cda..2d64a365a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSource.kt @@ -213,7 +213,7 @@ class ContactSearchPagedDataSource( } private fun getNonGroupSearchIterator(section: ContactSearchConfiguration.Section.Individuals, query: String?): ContactSearchIterator { - 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 { return contactSearchPagedDataSourceRepository.querySignalContactLetterHeaders( query = query, - includeSelf = section.includeSelf, + includeSelfMode = section.includeSelfMode, includePush = when (section.transportType) { ContactSearchConfiguration.TransportType.PUSH, ContactSearchConfiguration.TransportType.ALL -> true else -> false diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt index b0541632d2..9cef5e8991 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceRepository.kt @@ -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 { @@ -38,16 +38,8 @@ open class ContactSearchPagedDataSourceRepository( return contactRepository.querySignalContacts(contactsSearchQuery) } - open fun querySignalContactLetterHeaders(query: String?, includeSelf: Boolean, includePush: Boolean, includeSms: Boolean): Map { - 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 { + return SignalDatabase.recipients.querySignalContactLetterHeaders(query ?: "", includeSelfMode, includePush, includeSms) } open fun queryGroupMemberContacts(query: String?): Cursor? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt index 02cc5d2e9a..5e207b2a96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt @@ -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)) ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt index d4453285bc..2d8694002c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt @@ -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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 19682b1999..5b52b7fccd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -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.*, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 4c6e0b7ae2..92369f9525 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -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 { + 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 { + fun querySignalContactLetterHeaders(inputQuery: String, includeSelfMode: IncludeSelfMode, includePush: Boolean, includeSms: Boolean): Map { 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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java index 4beb5028a8..1e06ab1b40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java @@ -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; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java index 724bb63bf2..c16da54168 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java @@ -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)); 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 e588525883..26493090f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java @@ -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 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))); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt index 10d3834543..866228d3aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/connections/ViewAllSignalConnectionsFragment.kt @@ -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 ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt index 4aaf3bdafa..86c8cc1112 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsRepository.kt @@ -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 { return Single.fromCallable { - SignalDatabase.recipients.getSignalContactsCount(false) + SignalDatabase.recipients.getSignalContactsCount(RecipientTable.IncludeSelfMode.Exclude) }.subscribeOn(Schedulers.io()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/select/BaseStoryRecipientSelectionRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/select/BaseStoryRecipientSelectionRepository.kt index a7bd7792b8..9d86b2eb60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/select/BaseStoryRecipientSelectionRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/select/BaseStoryRecipientSelectionRepository.kt @@ -39,7 +39,7 @@ class BaseStoryRecipientSelectionRepository { fun getAllSignalContacts(): Single> { return Single.fromCallable { - SignalDatabase.recipients.getSignalContacts(false)?.use { + SignalDatabase.recipients.getSignalContacts(RecipientTable.IncludeSelfMode.Exclude).use { val recipientSet = mutableSetOf() while (it.moveToNext()) { recipientSet.add(RecipientId.from(CursorUtil.requireLong(it, RecipientTable.ID))) diff --git a/app/src/test/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceTest.kt b/app/src/test/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceTest.kt index e39fcbf405..b8b95c6955 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchPagedDataSourceTest.kt @@ -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