From 582028f2c298b95033a475fc42e350b9ce5a25f1 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 26 Aug 2019 18:09:01 -0400 Subject: [PATCH] Search contacts via the RecipientDatabase. --- res/layout/contact_selection_list_item.xml | 4 +- .../securesms/ContactSelectionActivity.java | 1 + .../securesms/CreateProfileActivity.java | 4 + .../securesms/InviteActivity.java | 1 - .../securesms/NewConversationActivity.java | 1 - .../securesms/RegistrationActivity.java | 7 +- .../thoughtcrime/securesms/ShareActivity.java | 2 - .../securesms/contacts/ContactAccessor.java | 2 +- .../securesms/contacts/ContactRepository.java | 180 ++++++++++++++ .../contacts/ContactSelectionListAdapter.java | 41 ++-- .../contacts/ContactSelectionListItem.java | 45 ++-- .../contacts/ContactsCursorLoader.java | 75 +++--- .../securesms/contacts/ContactsDatabase.java | 222 ------------------ .../ContactShareEditActivity.java | 2 +- .../ContactShareEditViewModel.java | 8 +- ...tory.java => SharedContactRepository.java} | 11 +- .../ConversationSearchViewModel.java | 3 +- .../securesms/database/RecipientDatabase.java | 94 +++++++- .../database/helpers/SQLCipherOpenHelper.java | 23 ++ .../securesms/jobs/JobManagerFactories.java | 2 + .../mediasend/CameraContactsRepository.java | 12 +- .../migrations/ApplicationMigrations.java | 10 +- .../RecipientSearchMigrationJob.java | 54 +++++ .../securesms/recipients/LiveRecipient.java | 6 +- .../securesms/recipients/Recipient.java | 14 ++ .../recipients/RecipientDetails.java | 9 +- .../securesms/search/SearchFragment.java | 3 +- .../securesms/search/SearchRepository.java | 31 +-- .../securesms/util/DirectoryHelper.java | 3 +- 29 files changed, 519 insertions(+), 351 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/contacts/ContactRepository.java rename src/org/thoughtcrime/securesms/contactshare/{ContactRepository.java => SharedContactRepository.java} (97%) create mode 100644 src/org/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob.java diff --git a/res/layout/contact_selection_list_item.xml b/res/layout/contact_selection_list_item.xml index bbab6b99ce..2169819ed0 100644 --- a/res/layout/contact_selection_list_item.xml +++ b/res/layout/contact_selection_list_item.xml @@ -29,7 +29,7 @@ android:layout_marginEnd="16dp" android:orientation="vertical"> - + tools:text="Ottttooooooooo Ocataaaaaaaavius" /> > SEARCH_CURSOR_MAPPERS = new ArrayList>() {{ + add(new Pair<>(ID_COLUMN, cursor -> cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID)))); + + add(new Pair<>(NAME_COLUMN, cursor -> { + String system = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_DISPLAY_NAME)); + String profile = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SIGNAL_PROFILE_NAME)); + + return !TextUtils.isEmpty(system) ? system : profile; + })); + + add(new Pair<>(NUMBER_COLUMN, cursor -> { + String phone = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.PHONE)); + String email = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.EMAIL)); + + return !TextUtils.isEmpty(phone) ? phone : email; + })); + + add(new Pair<>(NUMBER_TYPE_COLUMN, cursor -> cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_TYPE)))); + + add(new Pair<>(LABEL_COLUMN, cursor -> cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_LABEL)))); + + add(new Pair<>(CONTACT_TYPE_COLUMN, cursor -> { + int registered = cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.REGISTERED)); + return registered == RecipientDatabase.RegisteredState.REGISTERED.getId() ? PUSH_TYPE : NORMAL_TYPE; + })); + }}; + + public ContactRepository(@NonNull Context context) { + this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context); + this.noteToSelfTitle = context.getString(R.string.note_to_self); + } + + @WorkerThread + public Cursor querySignalContacts(@NonNull String query) { + Cursor cursor = TextUtils.isEmpty(query) ? recipientDatabase.getSignalContacts() + : recipientDatabase.querySignalContacts(query); + + + if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) { + Recipient self = Recipient.self(); + boolean nameMatch = self.getDisplayName().toLowerCase().contains(query.toLowerCase()); + boolean numberMatch = self.requireAddress().serialize() != null && self.requireAddress().serialize().contains(query); + boolean shouldAdd = !nameMatch && !numberMatch; + + if (shouldAdd) { + MatrixCursor selfCursor = new MatrixCursor(RecipientDatabase.SEARCH_PROJECTION); + selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.requireAddress().serialize(), null, null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle }); + + cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor }); + } + } + + return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS); + } + + @WorkerThread + public Cursor queryNonSignalContacts(@NonNull String query) { + Cursor cursor = TextUtils.isEmpty(query) ? recipientDatabase.getNonSignalContacts() + : recipientDatabase.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. + */ + private static class SearchCursorWrapper extends CursorWrapper { + + private final Cursor wrapped; + private final String[] columnNames; + private final List> mappers; + private final Map positions; + + SearchCursorWrapper(Cursor cursor, @NonNull List> mappers) { + super(cursor); + + this.wrapped = cursor; + this.mappers = mappers; + this.positions = new HashMap<>(); + this.columnNames = new String[mappers.size()]; + + for (int i = 0; i < mappers.size(); i++) { + Pair pair = mappers.get(i); + + positions.put(pair.first(), i); + columnNames[i] = pair.first(); + } + } + + @Override + public int getColumnCount() { + return mappers.size(); + } + + @Override + public String[] getColumnNames() { + return columnNames; + } + + @Override + public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { + Integer index = positions.get(columnName); + + if (index != null) { + return index; + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public String getString(int columnIndex) { + return String.valueOf(mappers.get(columnIndex).second().get(wrapped)); + } + + @Override + public int getInt(int columnIndex) { + return (int) mappers.get(columnIndex).second().get(wrapped); + } + + @Override + public long getLong(int columnIndex) { + return (long) mappers.get(columnIndex).second().get(wrapped); + } + } + + private interface ValueMapper { + T get(@NonNull Cursor cursor); + } +} diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java index 49751a3c54..23f338a423 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java @@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolde import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter; import org.thoughtcrime.securesms.util.Util; @@ -77,7 +78,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter getContactsCursors() { - ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(getContext()); - List cursorList = new ArrayList<>(2); + List cursorList = new ArrayList<>(2); if (!Permissions.hasAny(getContext(), Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { return cursorList; } if (pushEnabled(mode)) { - cursorList.add(contactsDatabase.queryTextSecureContacts(filter)); + cursorList.add(contactRepository.querySignalContacts(filter)); } if (pushEnabled(mode) && smsEnabled(mode)) { - cursorList.add(contactsDatabase.querySystemContacts(filter)); + cursorList.add(contactRepository.queryNonSignalContacts(filter)); } else if (smsEnabled(mode)) { - cursorList.add(filterNonPushContacts(contactsDatabase.querySystemContacts(filter))); + cursorList.add(filterNonPushContacts(contactRepository.queryNonSignalContacts(filter))); } return cursorList; } @@ -203,11 +211,12 @@ public class ContactsCursorLoader extends CursorLoader { try (GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(getContext()).getGroupsFilteredByTitle(filter)) { GroupDatabase.GroupRecord groupRecord; while ((groupRecord = reader.getNext()) != null) { - groupContacts.addRow(new Object[] { groupRecord.getTitle(), + groupContacts.addRow(new Object[] { groupRecord.getRecipientId().serialize(), + groupRecord.getTitle(), groupRecord.getEncodedId(), ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM, "", - ContactsDatabase.NORMAL_TYPE }); + ContactRepository.NORMAL_TYPE }); } } return groupContacts; @@ -215,11 +224,12 @@ public class ContactsCursorLoader extends CursorLoader { private Cursor getNewNumberCursor() { MatrixCursor newNumberCursor = new MatrixCursor(CONTACT_PROJECTION, 1); - newNumberCursor.addRow(new Object[] { getContext().getString(R.string.contact_selection_list__unknown_contact), + newNumberCursor.addRow(new Object[] { null, + getContext().getString(R.string.contact_selection_list__unknown_contact), filter, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM, "\u21e2", - ContactsDatabase.NEW_TYPE }); + ContactRepository.NEW_TYPE }); return newNumberCursor; } @@ -228,15 +238,16 @@ public class ContactsCursorLoader extends CursorLoader { final long startMillis = System.currentTimeMillis(); final MatrixCursor matrix = new MatrixCursor(CONTACT_PROJECTION); while (cursor.moveToNext()) { - final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); - final Recipient recipient = Recipient.external(getContext(), number); + final RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN))); + final Recipient recipient = Recipient.resolved(id); if (recipient.resolve().getRegistered() != RecipientDatabase.RegisteredState.REGISTERED) { - matrix.addRow(new Object[]{cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)), - number, - cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN)), - ContactsDatabase.NORMAL_TYPE}); + matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN)), + ContactRepository.NORMAL_TYPE}); } } Log.i(TAG, "filterNonPushContacts() -> " + (System.currentTimeMillis() - startMillis) + "ms"); diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java index 3a5f1d4a1e..26d1482a3f 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java @@ -17,31 +17,23 @@ package org.thoughtcrime.securesms.contacts; import android.accounts.Account; -import android.annotation.SuppressLint; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.Context; import android.content.OperationApplicationException; import android.database.Cursor; -import android.database.CursorWrapper; -import android.database.MatrixCursor; -import android.database.MergeCursor; import android.net.Uri; -import android.os.Build; import android.os.RemoteException; import android.provider.BaseColumns; import android.provider.ContactsContract; import android.provider.ContactsContract.RawContacts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import android.text.TextUtils; -import android.util.Pair; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -64,18 +56,6 @@ public class ContactsDatabase { private static final String CALL_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call"; private static final String SYNC = "__TS"; - static final String NAME_COLUMN = "name"; - static final String NUMBER_COLUMN = "number"; - static final String NUMBER_TYPE_COLUMN = "number_type"; - static final String LABEL_COLUMN = "label"; - static final String CONTACT_TYPE_COLUMN = "contact_type"; - - static final int NORMAL_TYPE = 0; - static final int PUSH_TYPE = 1; - static final int NEW_TYPE = 2; - static final int RECENT_TYPE = 3; - static final int DIVIDER_TYPE = 4; - private final Context context; public ContactsDatabase(Context context) { @@ -151,109 +131,6 @@ public class ContactsDatabase { } } - @SuppressLint("Recycle") - public @NonNull Cursor querySystemContacts(@Nullable String filter) { - Uri uri; - - if (!TextUtils.isEmpty(filter)) { - uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(filter)); - } else { - uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; - } - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - uri = uri.buildUpon().appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build(); - } - - String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, - ContactsContract.CommonDataKinds.Phone.NUMBER, - ContactsContract.CommonDataKinds.Phone.TYPE, - ContactsContract.CommonDataKinds.Phone.LABEL}; - - String sort = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; - - Map projectionMap = new HashMap() {{ - put(NAME_COLUMN, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); - put(NUMBER_COLUMN, ContactsContract.CommonDataKinds.Phone.NUMBER); - put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE); - put(LABEL_COLUMN, ContactsContract.CommonDataKinds.Phone.LABEL); - }}; - - String formattedNumber = "REPLACE(REPLACE(REPLACE(REPLACE(data1,' ',''),'-',''),'(',''),')','')"; - String excludeSelection = "(" + formattedNumber +" NOT IN " + - "(SELECT data1 FROM view_data WHERE "+formattedNumber+" = data1) " + - "OR "+formattedNumber+" = data1)" + - "AND " + formattedNumber + "NOT IN (SELECT "+formattedNumber+" FROM view_data where mimetype = '"+CONTACT_MIMETYPE+"')" ; - - String fallbackSelection = ContactsContract.Data.SYNC2 + " IS NULL OR " + ContactsContract.Data.SYNC2 + " != '" + SYNC + "'"; - - Cursor cursor; - - try { - cursor = context.getContentResolver().query(uri, projection, excludeSelection, null, sort); - } catch (Exception e) { - Log.w(TAG, e); - cursor = context.getContentResolver().query(uri, projection, fallbackSelection, null, sort); - } - - return new ProjectionMappingCursor(cursor, projectionMap, new Pair<>(CONTACT_TYPE_COLUMN, NORMAL_TYPE)); - } - - @SuppressLint("Recycle") - public @NonNull Cursor queryTextSecureContacts(String filter) { - String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME, - ContactsContract.Data.DATA1}; - - String sort = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; - - Map projectionMap = new HashMap(){{ - put(NAME_COLUMN, ContactsContract.Contacts.DISPLAY_NAME); - put(NUMBER_COLUMN, ContactsContract.Data.DATA1); - }}; - - Cursor cursor; - - if (TextUtils.isEmpty(filter)) { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - ContactsContract.Data.MIMETYPE + " = ?", - new String[] {CONTACT_MIMETYPE}, - sort); - } else { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - ContactsContract.Data.MIMETYPE + " = ? AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? OR " + ContactsContract.Data.DATA1 + " LIKE ?)", - new String[] {CONTACT_MIMETYPE, - "%" + filter + "%", "%" + filter + "%"}, - sort); - - if (context.getString(R.string.note_to_self).toLowerCase().contains(filter.toLowerCase())) { - Optional self = getSystemContactInfo(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); - boolean shouldAdd = true; - - if (self.isPresent()) { - boolean nameMatch = self.get().name != null && self.get().name.toLowerCase().contains(filter.toLowerCase()); - boolean numberMatch = self.get().number != null && self.get().number.contains(filter); - - shouldAdd = !nameMatch && !numberMatch; - } - - if (shouldAdd) { - MatrixCursor selfCursor = new MatrixCursor(projection); - selfCursor.addRow(new Object[]{ context.getString(R.string.note_to_self), TextSecurePreferences.getLocalNumber(context)}); - - cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor }); - } - } - } - - return new ProjectionMappingCursor(cursor, projectionMap, - new Pair<>(LABEL_COLUMN, "TextSecure"), - new Pair<>(NUMBER_TYPE_COLUMN, 0), - new Pair<>(CONTACT_TYPE_COLUMN, PUSH_TYPE)); - - } - public @Nullable Cursor getNameDetails(long contactId) { String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, @@ -573,105 +450,6 @@ public class ContactsDatabase { } } - private static class ProjectionMappingCursor extends CursorWrapper { - - private final Map projectionMap; - private final Pair[] extras; - - @SafeVarargs - ProjectionMappingCursor(Cursor cursor, - Map projectionMap, - Pair... extras) - { - super(cursor); - this.projectionMap = projectionMap; - this.extras = extras; - } - - @Override - public int getColumnCount() { - return super.getColumnCount() + extras.length; - } - - @Override - public int getColumnIndex(String columnName) { - for (int i=0;i= baseColumnCount) { - int offset = columnIndex - baseColumnCount; - return extras[offset].first; - } - - return getReverseProjection(super.getColumnName(columnIndex)); - } - - @Override - public String[] getColumnNames() { - String[] names = super.getColumnNames(); - String[] allNames = new String[names.length + extras.length]; - - for (int i=0;i= super.getColumnCount()) { - int offset = columnIndex - super.getColumnCount(); - return (Integer)extras[offset].second; - } - - return super.getInt(columnIndex); - } - - @Override - public String getString(int columnIndex) { - if (columnIndex >= super.getColumnCount()) { - int offset = columnIndex - super.getColumnCount(); - return (String)extras[offset].second; - } - - return super.getString(columnIndex); - } - - - private @Nullable String getReverseProjection(String columnName) { - for (Map.Entry entry : projectionMap.entrySet()) { - if (entry.getValue().equals(columnName)) { - return entry.getKey(); - } - } - - return null; - } - } - private static class SystemContactInfo { private final String name; private final String number; diff --git a/src/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java b/src/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java index 785aecff88..394074627c 100644 --- a/src/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java +++ b/src/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java @@ -75,7 +75,7 @@ public class ContactShareEditActivity extends PassphraseRequiredActionBarActivit ContactShareEditAdapter contactAdapter = new ContactShareEditAdapter(GlideApp.with(this), dynamicLanguage.getCurrentLocale(), this); contactList.setAdapter(contactAdapter); - ContactRepository contactRepository = new ContactRepository(this, + SharedContactRepository contactRepository = new SharedContactRepository(this, AsyncTask.THREAD_POOL_EXECUTOR, DatabaseFactory.getContactsDatabase(this)); diff --git a/src/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java b/src/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java index 860729cd45..d5e342177d 100644 --- a/src/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java +++ b/src/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java @@ -19,10 +19,10 @@ class ContactShareEditViewModel extends ViewModel { private final MutableLiveData> contacts; private final SingleLiveEvent events; - private final ContactRepository repo; + private final SharedContactRepository repo; ContactShareEditViewModel(@NonNull List contactUris, - @NonNull ContactRepository contactRepository) + @NonNull SharedContactRepository contactRepository) { contacts = new MutableLiveData<>(); events = new SingleLiveEvent<>(); @@ -98,9 +98,9 @@ class ContactShareEditViewModel extends ViewModel { static class Factory extends ViewModelProvider.NewInstanceFactory { private final List contactUris; - private final ContactRepository contactRepository; + private final SharedContactRepository contactRepository; - Factory(@NonNull List contactUris, @NonNull ContactRepository contactRepository) { + Factory(@NonNull List contactUris, @NonNull SharedContactRepository contactRepository) { this.contactUris = contactUris; this.contactRepository = contactRepository; } diff --git a/src/org/thoughtcrime/securesms/contactshare/ContactRepository.java b/src/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java similarity index 97% rename from src/org/thoughtcrime/securesms/contactshare/ContactRepository.java rename to src/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java index 4d49b32826..4289fa5a27 100644 --- a/src/org/thoughtcrime/securesms/contactshare/ContactRepository.java +++ b/src/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java @@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.contactshare.Contact.Email; import org.thoughtcrime.securesms.contactshare.Contact.Name; import org.thoughtcrime.securesms.contactshare.Contact.Phone; import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress; -import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; @@ -36,17 +35,17 @@ import ezvcard.VCard; import static org.thoughtcrime.securesms.contactshare.Contact.*; -public class ContactRepository { +public class SharedContactRepository { - private static final String TAG = ContactRepository.class.getSimpleName(); + private static final String TAG = SharedContactRepository.class.getSimpleName(); private final Context context; private final Executor executor; private final ContactsDatabase contactsDatabase; - ContactRepository(@NonNull Context context, - @NonNull Executor executor, - @NonNull ContactsDatabase contactsDatabase) + SharedContactRepository(@NonNull Context context, + @NonNull Executor executor, + @NonNull ContactsDatabase contactsDatabase) { this.context = context.getApplicationContext(); this.executor = executor; diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java b/src/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java index d83055071f..76bb06b28f 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java @@ -7,6 +7,7 @@ import android.content.Context; import androidx.annotation.NonNull; import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.contacts.ContactRepository; import org.thoughtcrime.securesms.database.CursorList; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.search.SearchRepository; @@ -37,8 +38,8 @@ public class ConversationSearchViewModel extends AndroidViewModel { debouncer = new Debouncer(500); searchRepository = new SearchRepository(context, DatabaseFactory.getSearchDatabase(context), - DatabaseFactory.getContactsDatabase(context), DatabaseFactory.getThreadDatabase(context), + new ContactRepository(application), ContactAccessor.getInstance(), SignalExecutors.SERIAL); } diff --git a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java index 574115e53d..fee1370c65 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -4,6 +4,8 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.text.TextUtils; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,8 +38,8 @@ public class RecipientDatabase extends Database { static final String TABLE_NAME = "recipient"; public static final String ID = "_id"; private static final String UUID = "uuid"; - static final String PHONE = "phone"; - static final String EMAIL = "email"; + public static final String PHONE = "phone"; + public static final String EMAIL = "email"; static final String GROUP_ID = "group_id"; private static final String BLOCKED = "blocked"; private static final String MESSAGE_RINGTONE = "message_ringtone"; @@ -50,22 +52,25 @@ public class RecipientDatabase extends Database { private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time"; - static final String REGISTERED = "registered"; - private static final String SYSTEM_DISPLAY_NAME = "system_display_name"; + public static final String REGISTERED = "registered"; + public static final String SYSTEM_DISPLAY_NAME = "system_display_name"; private static final String SYSTEM_PHOTO_URI = "system_photo_uri"; - private static final String SYSTEM_PHONE_LABEL = "system_phone_label"; + public static final String SYSTEM_PHONE_TYPE = "system_phone_type"; + public static final String SYSTEM_PHONE_LABEL = "system_phone_label"; private static final String SYSTEM_CONTACT_URI = "system_contact_uri"; private static final String PROFILE_KEY = "profile_key"; - private static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; + public static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"; private static final String PROFILE_SHARING = "profile_sharing"; private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode"; private static final String FORCE_SMS_SELECTION = "force_sms_selection"; + private static final String SORT_NAME = "sort_name"; + private static final String[] RECIPIENT_PROJECTION = new String[] { UUID, PHONE, EMAIL, GROUP_ID, BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED, - PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, + PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI, SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, UNIDENTIFIED_ACCESS_MODE, FORCE_SMS_SELECTION, @@ -73,6 +78,8 @@ public class RecipientDatabase extends Database { private static final String[] ID_PROJECTION = new String[] { ID }; + public static final String[] SEARCH_PROJECTION = new String[] { ID, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, "IFNULL(" + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ") AS " + SORT_NAME }; + private static Address addressFromCursor(Cursor cursor) { String phone = cursor.getString(cursor.getColumnIndexOrThrow(PHONE)); String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL)); @@ -159,6 +166,7 @@ public class RecipientDatabase extends Database { SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " + SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " + SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " + + SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " + SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " + PROFILE_KEY + " TEXT DEFAULT NULL, " + SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " + @@ -262,8 +270,7 @@ public class RecipientDatabase extends Database { if (cursor != null && cursor.moveToNext()) { return getRecipientSettings(cursor); } else { - // TODO: Maybe not make this an error? - throw new AssertionError("Couldn't find recipient!"); + throw new AssertionError("Couldn't find recipient! id: " + id.serialize()); } } } @@ -551,6 +558,66 @@ public class RecipientDatabase extends Database { Stream.of(updates.entrySet()).forEach(entry -> Recipient.live(entry.getKey()).refresh()); } } + public @Nullable Cursor getSignalContacts() { + String selection = BLOCKED + " = ? AND " + + REGISTERED + " = ? AND " + + GROUP_ID + " IS NULL AND " + + "(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " + + "(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + SIGNAL_PROFILE_NAME + " NOT NULL)"; + String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1" }; + String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ", " + PHONE; + + return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); + } + + public @Nullable Cursor querySignalContacts(@NonNull String query) { + query = TextUtils.isEmpty(query) ? "*" : query; + query = "%" + query + "%"; + + String selection = BLOCKED + " = ? AND " + + REGISTERED + " = ? AND " + + GROUP_ID + " IS NULL AND " + + "(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " + + "(" + + PHONE + " LIKE ? OR " + + SYSTEM_DISPLAY_NAME + " LIKE ? OR " + + SIGNAL_PROFILE_NAME + " LIKE ?" + + ")"; + String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query }; + String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ", " + PHONE; + + return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); + } + + public @Nullable Cursor getNonSignalContacts() { + String selection = BLOCKED + " = ? AND " + + REGISTERED + " != ? AND " + + GROUP_ID + " IS NULL AND " + + SYSTEM_DISPLAY_NAME + " NOT NULL AND " + + "(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)"; + String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) }; + String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE; + + return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); + } + + public @Nullable Cursor queryNonSignalContacts(@NonNull String query) { + query = TextUtils.isEmpty(query) ? "*" : query; + query = "%" + query + "%"; + + String selection = BLOCKED + " = ? AND " + + REGISTERED + " != ? AND " + + GROUP_ID + " IS NULL AND " + + "(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " + + "(" + + PHONE + " LIKE ? OR " + + SYSTEM_DISPLAY_NAME + " LIKE ?" + + ")"; + String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query }; + String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE; + + return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); + } private void update(@NonNull RecipientId id, ContentValues contentValues) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); @@ -567,11 +634,18 @@ public class RecipientDatabase extends Database { this.database = database; } - public void setSystemContactInfo(@NonNull RecipientId id, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) { + public void setSystemContactInfo(@NonNull RecipientId id, + @Nullable String displayName, + @Nullable String photoUri, + @Nullable String systemPhoneLabel, + int systemPhoneType, + @Nullable String systemContactUri) + { ContentValues contentValues = new ContentValues(1); contentValues.put(SYSTEM_DISPLAY_NAME, displayName); contentValues.put(SYSTEM_PHOTO_URI, photoUri); contentValues.put(SYSTEM_PHONE_LABEL, systemPhoneLabel); + contentValues.put(SYSTEM_PHONE_TYPE, systemPhoneType); contentValues.put(SYSTEM_CONTACT_URI, systemContactUri); update(id, contentValues); diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 3eec65a5f7..6e3aafe20e 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.GroupUtil; @@ -76,6 +77,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int REVEALABLE_MESSAGES = 22; private static final int VIEW_ONCE_ONLY = 23; private static final int RECIPIENT_IDS = 24; + private static final int RECIPIENT_SEARCH = 25; private static final int DATABASE_VERSION = 25; private static final String DATABASE_NAME = "signal.db"; @@ -490,6 +492,27 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { RecipientIdMigrationHelper.execute(db); } + if (oldVersion < RECIPIENT_SEARCH) { + db.execSQL("ALTER TABLE recipient ADD COLUMN system_phone_type INTEGER DEFAULT -1"); + + String localNumber = TextSecurePreferences.getLocalNumber(context); + if (!TextUtils.isEmpty(localNumber)) { + try (Cursor cursor = db.query("recipient", null, "phone = ?", new String[] { localNumber }, null, null, null)) { + if (cursor == null || !cursor.moveToFirst()) { + ContentValues values = new ContentValues(); + values.put("phone", localNumber); + values.put("registered", 1); + values.put("profile_sharing", 1); + values.put("signal_profile_name", TextSecurePreferences.getProfileName(context)); + db.insert("recipient", null, values); + } else { + db.execSQL("UPDATE recipient SET registered = ?, profile_sharing = ?, signal_profile_name = ? WHERE phone = ?", + new String[] { "1", "1", TextSecurePreferences.getProfileName(context), localNumber }); + } + } + } + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 3fd008f24f..fcc5836e1f 100644 --- a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration; import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob; import org.thoughtcrime.securesms.migrations.LegacyMigrationJob; import org.thoughtcrime.securesms.migrations.MigrationCompleteJob; +import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob; import java.util.Arrays; import java.util.HashMap; @@ -82,6 +83,7 @@ public final class JobManagerFactories { put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory()); put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory()); put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory()); + put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory()); // Dead jobs put("PushContentReceiveJob", new FailingJob.Factory()); diff --git a/src/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java b/src/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java index d4df95fb4e..64d376105f 100644 --- a/src/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java +++ b/src/org/thoughtcrime/securesms/mediasend/CameraContactsRepository.java @@ -7,8 +7,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; -import org.thoughtcrime.securesms.contacts.ContactsDatabase; -import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.contacts.ContactRepository; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase; @@ -32,15 +31,15 @@ class CameraContactsRepository { private final Context context; private final ThreadDatabase threadDatabase; private final GroupDatabase groupDatabase; - private final ContactsDatabase contactsDatabase; private final RecipientDatabase recipientDatabase; + private final ContactRepository contactRepository; CameraContactsRepository(@NonNull Context context) { this.context = context.getApplicationContext(); this.threadDatabase = DatabaseFactory.getThreadDatabase(context); this.groupDatabase = DatabaseFactory.getGroupDatabase(context); - this.contactsDatabase = DatabaseFactory.getContactsDatabase(context); this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context); + this.contactRepository = new ContactRepository(context); } void getCameraContacts(@NonNull Callback callback) { @@ -80,9 +79,10 @@ class CameraContactsRepository { private @NonNull List getContacts(@NonNull String query) { List recipients = new ArrayList<>(); - try (Cursor cursor = contactsDatabase.queryTextSecureContacts(query)) { + try (Cursor cursor = contactRepository.querySignalContacts(query)) { while (cursor.moveToNext()) { - Recipient recipient = Recipient.external(context, cursor.getString(1)); + RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN))); + Recipient recipient = Recipient.resolved(id); recipients.add(recipient); } } diff --git a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index f5a8b554dc..0485ed7fd2 100644 --- a/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/src/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -11,6 +11,7 @@ import com.annimon.stream.Stream; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.Util; @@ -38,8 +39,9 @@ public class ApplicationMigrations { private static final MutableLiveData UI_BLOCKING_MIGRATION_RUNNING = new MutableLiveData<>(); private static final class Version { - static final int LEGACY = 455; - static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION + static final int LEGACY = 455; + static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION + static final int RECIPIENT_SEARCH = 525; // TODO [greyson] USE PROPER APPLICATION VERSION } /** @@ -133,6 +135,10 @@ public class ApplicationMigrations { jobs.add(new DatabaseMigrationJob()); } + if (lastSeenVersion < Version.RECIPIENT_SEARCH) { + jobs.add(new RecipientSearchMigrationJob()); + } + return jobs; } } diff --git a/src/org/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob.java b/src/org/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob.java new file mode 100644 index 0000000000..a90e7ac8ab --- /dev/null +++ b/src/org/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob.java @@ -0,0 +1,54 @@ +package org.thoughtcrime.securesms.migrations; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.util.DirectoryHelper; + +import java.io.IOException; + +/** + * We added a column for keeping track of the phone number type ("mobile", "home", etc) to the + * recipient database, and therefore we need to do a directory sync to fill in that column. + */ +public class RecipientSearchMigrationJob extends MigrationJob { + + public static final String KEY = "RecipientSearchMigrationJob"; + + RecipientSearchMigrationJob() { + this(new Job.Parameters.Builder().addConstraint(NetworkConstraint.KEY).build()); + } + + private RecipientSearchMigrationJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + boolean isUiBlocking() { + return false; + } + + @Override + void performMigration() throws Exception { + DirectoryHelper.refreshDirectory(context, false); + } + + @Override + boolean shouldRetry(@NonNull Exception e) { + return e instanceof IOException; + } + + public static class Factory implements Job.Factory { + @Override + public @NonNull RecipientSearchMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RecipientSearchMigrationJob(parameters); + } + } +} diff --git a/src/org/thoughtcrime/securesms/recipients/LiveRecipient.java b/src/org/thoughtcrime/securesms/recipients/LiveRecipient.java index 113f90b7f9..172284f428 100644 --- a/src/org/thoughtcrime/securesms/recipients/LiveRecipient.java +++ b/src/org/thoughtcrime/securesms/recipients/LiveRecipient.java @@ -152,7 +152,7 @@ public final class LiveRecipient { private @NonNull RecipientDetails getIndividualRecipientDetails(RecipientSettings settings) { boolean systemContact = !TextUtils.isEmpty(settings.getSystemDisplayName()); boolean isLocalNumber = settings.getAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context)); - return new RecipientDetails(null, Optional.absent(), systemContact, isLocalNumber, settings, null); + return new RecipientDetails(context, null, Optional.absent(), systemContact, isLocalNumber, settings, null); } @WorkerThread @@ -172,10 +172,10 @@ public final class LiveRecipient { avatarId = Optional.of(groupRecord.get().getAvatarId()); } - return new RecipientDetails(title, avatarId, false, false, settings, members); + return new RecipientDetails(context, title, avatarId, false, false, settings, members); } - return new RecipientDetails(unnamedGroupName, Optional.absent(), false, false, settings, null); + return new RecipientDetails(context, unnamedGroupName, Optional.absent(), false, false, settings, null); } @Override diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index bb6a767f03..aee90cfa53 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -207,6 +207,20 @@ public class Recipient { return this.name; } + public @NonNull String getDisplayName() { + String name = getName(); + if (!TextUtils.isEmpty(name)) { + return name; + } + + String profileName = getProfileName(); + if (!TextUtils.isEmpty(profileName)) { + return profileName; + } + + return requireAddress().serialize(); + } + public @NonNull MaterialColor getColor() { if (isGroup()) return MaterialColor.GROUP; else if (color != null) return color; diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientDetails.java b/src/org/thoughtcrime/securesms/recipients/RecipientDetails.java index 3b83fd69ec..a9afda852c 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientDetails.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientDetails.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.recipients; +import android.content.Context; import android.net.Uri; import androidx.annotation.NonNull; @@ -11,6 +12,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -33,7 +35,7 @@ public class RecipientDetails { final VibrateState callVibrateState; final boolean blocked; final int expireMessages; - final List participants; + final List participants; final String profileName; final boolean seenInviteReminder; final Optional defaultSubscriptionId; @@ -47,7 +49,8 @@ public class RecipientDetails { final UnidentifiedAccessMode unidentifiedAccessMode; final boolean forceSmsSelection; - RecipientDetails(@Nullable String name, + RecipientDetails(@NonNull Context context, + @Nullable String name, @NonNull Optional groupAvatarId, boolean systemContact, boolean isLocalNumber, @@ -68,7 +71,7 @@ public class RecipientDetails { this.blocked = settings.isBlocked(); this.expireMessages = settings.getExpireMessages(); this.participants = participants == null ? new LinkedList<>() : participants; - this.profileName = settings.getProfileName(); + this.profileName = isLocalNumber ? TextSecurePreferences.getProfileName(context) : settings.getProfileName(); this.seenInviteReminder = settings.hasSeenInviteReminder(); this.defaultSubscriptionId = settings.getDefaultSubscriptionId(); this.registered = settings.getRegistered(); diff --git a/src/org/thoughtcrime/securesms/search/SearchFragment.java b/src/org/thoughtcrime/securesms/search/SearchFragment.java index d6f2e72dfe..5fd57053c5 100644 --- a/src/org/thoughtcrime/securesms/search/SearchFragment.java +++ b/src/org/thoughtcrime/securesms/search/SearchFragment.java @@ -16,6 +16,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import org.thoughtcrime.securesms.contacts.ContactRepository; import org.thoughtcrime.securesms.conversation.ConversationActivity; import org.thoughtcrime.securesms.ConversationListActivity; import org.thoughtcrime.securesms.R; @@ -67,8 +68,8 @@ public class SearchFragment extends Fragment implements SearchListAdapter.EventL SearchRepository searchRepository = new SearchRepository(getContext(), DatabaseFactory.getSearchDatabase(getContext()), - DatabaseFactory.getContactsDatabase(getContext()), DatabaseFactory.getThreadDatabase(getContext()), + new ContactRepository(requireContext()), ContactAccessor.getInstance(), SignalExecutors.SERIAL); viewModel = ViewModelProviders.of(this, new SearchViewModel.Factory(searchRepository)).get(SearchViewModel.class); diff --git a/src/org/thoughtcrime/securesms/search/SearchRepository.java b/src/org/thoughtcrime/securesms/search/SearchRepository.java index 69e0bf1ef5..8bebb038b7 100644 --- a/src/org/thoughtcrime/securesms/search/SearchRepository.java +++ b/src/org/thoughtcrime/securesms/search/SearchRepository.java @@ -12,6 +12,7 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.contacts.ContactRepository; import org.thoughtcrime.securesms.contacts.ContactsDatabase; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.CursorList; @@ -56,26 +57,26 @@ public class SearchRepository { } } - private final Context context; - private final SearchDatabase searchDatabase; - private final ContactsDatabase contactsDatabase; - private final ThreadDatabase threadDatabase; - private final ContactAccessor contactAccessor; - private final Executor executor; + private final Context context; + private final SearchDatabase searchDatabase; + private final ContactRepository contactRepository; + private final ThreadDatabase threadDatabase; + private final ContactAccessor contactAccessor; + private final Executor executor; public SearchRepository(@NonNull Context context, @NonNull SearchDatabase searchDatabase, - @NonNull ContactsDatabase contactsDatabase, @NonNull ThreadDatabase threadDatabase, + @NonNull ContactRepository contactRepository, @NonNull ContactAccessor contactAccessor, @NonNull Executor executor) { - this.context = context.getApplicationContext(); - this.searchDatabase = searchDatabase; - this.contactsDatabase = contactsDatabase; - this.threadDatabase = threadDatabase; - this.contactAccessor = contactAccessor; - this.executor = executor; + this.context = context.getApplicationContext(); + this.searchDatabase = searchDatabase; + this.threadDatabase = threadDatabase; + this.contactRepository = contactRepository; + this.contactAccessor = contactAccessor; + this.executor = executor; } public void query(@NonNull String query, @NonNull Callback callback) { @@ -125,8 +126,8 @@ public class SearchRepository { return CursorList.emptyList(); } - Cursor textSecureContacts = contactsDatabase.queryTextSecureContacts(query); - Cursor systemContacts = contactsDatabase.querySystemContacts(query); + Cursor textSecureContacts = contactRepository.querySignalContacts(query); + Cursor systemContacts = contactRepository.queryNonSignalContacts(query); MergeCursor contacts = new MergeCursor(new Cursor[]{ textSecureContacts, systemContacts }); return new CursorList<>(contacts, new RecipientModelBuilder(context)); diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 2be20e07c9..e856dc750a 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -205,11 +205,12 @@ public class DirectoryHelper { String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI)); String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL)); + int phoneType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE)); Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)), cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY))); - handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, contactUri.toString()); + handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, phoneType, contactUri.toString()); } } } finally {