Split system names into first / last.

This commit is contained in:
Alex Hart 2021-03-03 09:37:30 -04:00 committed by GitHub
parent dc9fceb8cf
commit fd9c420dc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 458 additions and 157 deletions

View file

@ -23,28 +23,37 @@ import android.database.MergeCursor;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup; import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils; import android.telephony.PhoneNumberUtils;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@ -65,6 +74,9 @@ public class ContactAccessor {
public static final String PUSH_COLUMN = "push"; public static final String PUSH_COLUMN = "push";
private static final String GIVEN_NAME = ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME;
private static final String FAMILY_NAME = ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME;
private static final ContactAccessor instance = new ContactAccessor(); private static final ContactAccessor instance = new ContactAccessor();
public static synchronized ContactAccessor getInstance() { public static synchronized ContactAccessor getInstance() {
@ -85,55 +97,42 @@ public class ContactAccessor {
return results; return results;
} }
/**
* Gets and returns a cursor of data for all contacts, containing both phone number data and
* structured name data.
*
* Cursor rows are ordered as follows:
*
* <ol>
* <li>Contact Lookup Key</li>
* <li>Mimetype</li>
* <li>id</li>
* </ol>
*
* The lookup key is a fixed value that allows you to verify two rows in the database actually
* belong to the same contact, since the contact uri can be unstable (if a sync fails, say.)
*
* We order by id explicitly here for the same contact sync failure error, which could result in
* multiple structured name rows for the same user. By ordering by id DESC, we ensure we get
* whatever the latest input data was.
*
* What this results in is a cursor that looks like:
*
* Alice phone 1
* Alice phone 2
* Alice structured name 2
* Alice structured name 1
* Bob phone 1
* ... etc.
*/
public Cursor getAllSystemContacts(Context context) { public Cursor getAllSystemContacts(Context context) {
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY, Phone.TYPE}, null, null, null); Uri uri = ContactsContract.Data.CONTENT_URI;
} String[] projection = SqlUtil.buildArgs(ContactsContract.Data.MIMETYPE, Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY, Phone.TYPE, GIVEN_NAME, FAMILY_NAME);
String where = ContactsContract.Data.MIMETYPE + " IN (?, ?)";
String[] args = SqlUtil.buildArgs(Phone.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
String orderBy = Phone.LOOKUP_KEY + " ASC, " + ContactsContract.Data.MIMETYPE + " DESC, " + ContactsContract.CommonDataKinds.Phone._ID + " DESC";
public boolean isSystemContact(Context context, String number) { return context.getContentResolver().query(uri, projection, where, args, orderBy);
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY,
PhoneLookup._ID, PhoneLookup.NUMBER};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
return true;
}
} finally {
if (cursor != null) cursor.close();
}
return false;
}
public Collection<ContactData> getContactsWithPush(Context context) {
final ContentResolver resolver = context.getContentResolver();
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
final List<String> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered())
.map(Recipient::resolved)
.filter(r -> r.getE164().isPresent())
.map(Recipient::requireE164)
.toList();
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
for (String registeredAddress : registeredAddresses) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress));
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
try {
if (lookupCursor != null && lookupCursor.moveToFirst()) {
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
contactData.numbers.add(new NumberData("TextSecure", registeredAddress));
lookupData.add(contactData);
}
} finally {
if (lookupCursor != null)
lookupCursor.close();
}
}
return lookupData;
} }
public String getNameFromContact(Context context, Uri uri) { public String getNameFromContact(Context context, Uri uri) {
@ -160,13 +159,13 @@ public class ContactAccessor {
private ContactData getContactData(Context context, String displayName, long id) { private ContactData getContactData(Context context, String displayName, long id) {
ContactData contactData = new ContactData(id, displayName); ContactData contactData = new ContactData(id, displayName);
Cursor numberCursor = null;
try { try (Cursor numberCursor = context.getContentResolver().query(Phone.CONTENT_URI,
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null, null,
Phone.CONTACT_ID + " = ?", Phone.CONTACT_ID + " = ?",
new String[] {contactData.id + ""}, null); new String[] {contactData.id + ""},
null))
{
while (numberCursor != null && numberCursor.moveToNext()) { while (numberCursor != null && numberCursor.moveToNext()) {
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE)); int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL)); String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
@ -175,9 +174,6 @@ public class ContactAccessor {
contactData.numbers.add(new NumberData(typeLabel, number)); contactData.numbers.add(new NumberData(typeLabel, number));
} }
} finally {
if (numberCursor != null)
numberCursor.close();
} }
return contactData; return contactData;

View file

@ -57,7 +57,7 @@ public class ContactRepository {
add(new Pair<>(ID_COLUMN, cursor -> CursorUtil.requireLong(cursor, RecipientDatabase.ID))); add(new Pair<>(ID_COLUMN, cursor -> CursorUtil.requireLong(cursor, RecipientDatabase.ID)));
add(new Pair<>(NAME_COLUMN, cursor -> { add(new Pair<>(NAME_COLUMN, cursor -> {
String system = CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_DISPLAY_NAME); String system = CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_JOINED_NAME);
String profile = CursorUtil.requireString(cursor, RecipientDatabase.SEARCH_PROFILE_NAME); String profile = CursorUtil.requireString(cursor, RecipientDatabase.SEARCH_PROFILE_NAME);
return Util.getFirstNonEmpty(system, profile); return Util.getFirstNonEmpty(system, profile);

View file

@ -0,0 +1,53 @@
package org.thoughtcrime.securesms.contacts.sync;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.profiles.ProfileName;
import java.util.LinkedList;
import java.util.List;
final class ContactHolder {
private final String lookupKey;
private final List<PhoneNumberRecord> phoneNumberRecords = new LinkedList<>();
private StructuredNameRecord structuredNameRecord;
ContactHolder(@NonNull String lookupKey) {
this.lookupKey = lookupKey;
}
@NonNull String getLookupKey() {
return lookupKey;
}
public void addPhoneNumberRecord(@NonNull PhoneNumberRecord phoneNumberRecord) {
phoneNumberRecords.add(phoneNumberRecord);
}
public void setStructuredNameRecord(@NonNull StructuredNameRecord structuredNameRecord) {
this.structuredNameRecord = structuredNameRecord;
}
void commit(@NonNull RecipientDatabase.BulkOperationsHandle handle) {
for (PhoneNumberRecord phoneNumberRecord : phoneNumberRecords) {
handle.setSystemContactInfo(phoneNumberRecord.getRecipientId(),
getProfileName(phoneNumberRecord.getDisplayName()),
phoneNumberRecord.getContactPhotoUri(),
phoneNumberRecord.getContactLabel(),
phoneNumberRecord.getPhoneType(),
phoneNumberRecord.getContactUri().toString());
}
}
private @NonNull ProfileName getProfileName(@NonNull String displayName) {
if (structuredNameRecord.hasGivenName()) {
return structuredNameRecord.asProfileName();
} else {
return ProfileName.asGiven(displayName);
}
}
}

View file

@ -7,7 +7,6 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.OperationApplicationException; import android.content.OperationApplicationException;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.text.TextUtils; import android.text.TextUtils;
@ -43,6 +42,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.registration.RegistrationUtil; import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage; import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.ProfileUtil; import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.Stopwatch;
@ -63,6 +63,7 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -320,27 +321,63 @@ public class DirectoryHelper {
contactsDatabase.removeDeletedRawContacts(account.getAccount()); contactsDatabase.removeDeletedRawContacts(account.getAccount());
contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing); contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing);
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate(); BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate();
try { ContactHolder old = null;
try (Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)); String lookupKey = getLookupKey(cursor);
String mimeType = getMimeType(cursor);
ContactHolder contactHolder = new ContactHolder(lookupKey);
if (!isPhoneMimeType(mimeType)) {
Log.w(TAG, "Ignoring unexpected mime type: " + mimeType);
}
while (getLookupKey(cursor).equals(lookupKey) && isPhoneMimeType(getMimeType(cursor))) {
String number = CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.NUMBER);
if (isValidContactNumber(number)) { if (isValidContactNumber(number)) {
String formattedNumber = PhoneNumberFormatter.get(context).format(number); String formattedNumber = PhoneNumberFormatter.get(context).format(number);
String realNumber = Util.getFirstNonEmpty(rewrites.get(formattedNumber), formattedNumber); String realNumber = Util.getFirstNonEmpty(rewrites.get(formattedNumber), formattedNumber);
RecipientId recipientId = Recipient.externalContact(context, realNumber).getId();
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, phoneType, contactUri.toString()); PhoneNumberRecord.Builder builder = new PhoneNumberRecord.Builder();
builder.withRecipientId(Recipient.externalContact(context, realNumber).getId());
builder.withDisplayName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
builder.withContactPhotoUri(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
builder.withContactLabel(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LABEL));
builder.withPhoneType(CursorUtil.requireInt(cursor, ContactsContract.CommonDataKinds.Phone.TYPE));
builder.withContactUri(ContactsContract.Contacts.getLookupUri(CursorUtil.requireLong(cursor, ContactsContract.CommonDataKinds.Phone._ID),
CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
contactHolder.addPhoneNumberRecord(builder.build());
} else {
Log.w(TAG, "Skipping phone entry with invalid number");
} }
cursor.moveToNext();
} }
if (getLookupKey(cursor).equals(lookupKey)) {
if (isStructuredNameMimeType(getMimeType(cursor))) {
StructuredNameRecord.Builder builder = new StructuredNameRecord.Builder();
builder.withGivenName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
builder.withFamilyName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
contactHolder.setStructuredNameRecord(builder.build());
} else {
Log.i(TAG, "Skipping invalid mimeType " + mimeType);
}
} else {
Log.i(TAG, "No structured name for user, rolling back cursor.");
cursor.moveToPrevious();
}
contactHolder.commit(handle);
}
} finally { } finally {
handle.finish(); handle.finish();
} }
@ -358,10 +395,26 @@ public class DirectoryHelper {
} }
} }
private static boolean isPhoneMimeType(@NonNull String mimeType) {
return ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
}
private static boolean isStructuredNameMimeType(@NonNull String mimeType) {
return ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(mimeType);
}
private static boolean isValidContactNumber(@Nullable String number) { private static boolean isValidContactNumber(@Nullable String number) {
return !TextUtils.isEmpty(number) && !UuidUtil.isUuid(number); return !TextUtils.isEmpty(number) && !UuidUtil.isUuid(number);
} }
private static @NonNull String getLookupKey(@NonNull Cursor cursor) {
return Objects.requireNonNull(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY));
}
private static @NonNull String getMimeType(@NonNull Cursor cursor) {
return CursorUtil.requireString(cursor, ContactsContract.Data.MIMETYPE);
}
private static @Nullable AccountHolder getOrCreateSystemAccount(Context context) { private static @Nullable AccountHolder getOrCreateSystemAccount(Context context) {
AccountManager accountManager = AccountManager.get(context); AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType(BuildConfig.APPLICATION_ID); Account[] accounts = accountManager.getAccountsByType(BuildConfig.APPLICATION_ID);

View file

@ -0,0 +1,99 @@
package org.thoughtcrime.securesms.contacts.sync;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.Objects;
/**
* Represents all the data we pull from a Phone data cursor row from the contacts database.
*/
final class PhoneNumberRecord {
private final RecipientId recipientId;
private final String displayName;
private final String contactPhotoUri;
private final String contactLabel;
private final int phoneType;
private final Uri contactUri;
private PhoneNumberRecord(@NonNull PhoneNumberRecord.Builder builder) {
recipientId = Objects.requireNonNull(builder.recipientId);
displayName = builder.displayName;
contactPhotoUri = builder.contactPhotoUri;
contactLabel = builder.contactLabel;
phoneType = builder.phoneType;
contactUri = builder.contactUri;
}
@NonNull RecipientId getRecipientId() {
return recipientId;
}
@Nullable String getDisplayName() {
return displayName;
}
@Nullable String getContactPhotoUri() {
return contactPhotoUri;
}
@Nullable String getContactLabel() {
return contactLabel;
}
int getPhoneType() {
return phoneType;
}
@Nullable Uri getContactUri() {
return contactUri;
}
final static class Builder {
private RecipientId recipientId;
private String displayName;
private String contactPhotoUri;
private String contactLabel;
private int phoneType;
private Uri contactUri;
@NonNull Builder withRecipientId(@NonNull RecipientId recipientId) {
this.recipientId = recipientId;
return this;
}
@NonNull Builder withDisplayName(@Nullable String displayName) {
this.displayName = displayName;
return this;
}
@NonNull Builder withContactUri(@Nullable Uri contactUri) {
this.contactUri = contactUri;
return this;
}
@NonNull Builder withContactLabel(@NonNull String contactLabel) {
this.contactLabel = contactLabel;
return this;
}
@NonNull Builder withContactPhotoUri(@NonNull String contactPhotoUri) {
this.contactPhotoUri = contactPhotoUri;
return this;
}
@NonNull Builder withPhoneType(int phoneType) {
this.phoneType = phoneType;
return this;
}
@NonNull PhoneNumberRecord build() {
return new PhoneNumberRecord(this);
}
}
}

View file

@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.contacts.sync;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.profiles.ProfileName;
/**
* Represents the data pulled from a StructuredName row of a Contacts data cursor.
*/
final class StructuredNameRecord {
private final String givenName;
private final String familyName;
StructuredNameRecord(@NonNull StructuredNameRecord.Builder builder) {
this.givenName = builder.givenName;
this.familyName = builder.familyName;
}
public boolean hasGivenName() {
return givenName != null;
}
public @NonNull ProfileName asProfileName() {
return ProfileName.fromParts(givenName, familyName);
}
final static class Builder {
private String givenName;
private String familyName;
@NonNull Builder withGivenName(@Nullable String givenName) {
this.givenName = givenName;
return this;
}
@NonNull Builder withFamilyName(@Nullable String familyName) {
this.familyName = familyName;
return this;
}
@NonNull StructuredNameRecord build() {
return new StructuredNameRecord(this);
}
}
}

View file

@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob; import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob; import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -113,7 +112,9 @@ public class RecipientDatabase extends Database {
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time"; private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
public static final String REGISTERED = "registered"; public static final String REGISTERED = "registered";
public static final String SYSTEM_DISPLAY_NAME = "system_display_name"; public static final String SYSTEM_JOINED_NAME = "system_display_name";
public static final String SYSTEM_FAMILY_NAME = "system_family_name";
public static final String SYSTEM_GIVEN_NAME = "system_given_name";
private static final String SYSTEM_PHOTO_URI = "system_photo_uri"; private static final String SYSTEM_PHOTO_URI = "system_photo_uri";
public static final String SYSTEM_PHONE_TYPE = "system_phone_type"; public static final String SYSTEM_PHONE_TYPE = "system_phone_type";
public static final String SYSTEM_PHONE_LABEL = "system_phone_label"; public static final String SYSTEM_PHONE_LABEL = "system_phone_label";
@ -157,7 +158,7 @@ public class RecipientDatabase extends Database {
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE, ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED, BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
PROFILE_KEY, PROFILE_KEY_CREDENTIAL, PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI, SYSTEM_GIVEN_NAME, SYSTEM_FAMILY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH, PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH,
NOTIFICATION_CHANNEL, NOTIFICATION_CHANNEL,
UNIDENTIFIED_ACCESS_MODE, UNIDENTIFIED_ACCESS_MODE,
@ -170,15 +171,15 @@ public class RecipientDatabase extends Database {
}; };
private static final String[] ID_PROJECTION = new String[]{ID}; private static final String[] ID_PROJECTION = new String[]{ID};
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME}; private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME}; public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName) .map(columnName -> TABLE_NAME + "." + columnName)
.toList().toArray(new String[0]); .toList().toArray(new String[0]);
static final String[] TYPED_RECIPIENT_PROJECTION_NO_ID = Arrays.copyOfRange(TYPED_RECIPIENT_PROJECTION, 1, TYPED_RECIPIENT_PROJECTION.length); static final String[] TYPED_RECIPIENT_PROJECTION_NO_ID = Arrays.copyOfRange(TYPED_RECIPIENT_PROJECTION, 1, TYPED_RECIPIENT_PROJECTION.length);
private static final String[] MENTION_SEARCH_PROJECTION = new String[]{ID, removeWhitespace("COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ", " + nullIfEmpty(PHONE) + ")") + " AS " + SORT_NAME}; private static final String[] MENTION_SEARCH_PROJECTION = new String[]{ID, removeWhitespace("COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ", " + nullIfEmpty(PHONE) + ")") + " AS " + SORT_NAME};
public static final String[] CREATE_INDEXS = new String[] { public static final String[] CREATE_INDEXS = new String[] {
"CREATE INDEX IF NOT EXISTS recipient_dirty_index ON " + TABLE_NAME + " (" + DIRTY + ");", "CREATE INDEX IF NOT EXISTS recipient_dirty_index ON " + TABLE_NAME + " (" + DIRTY + ");",
@ -339,7 +340,9 @@ public class RecipientDatabase extends Database {
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " + MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
REGISTERED + " INTEGER DEFAULT " + RegisteredState.UNKNOWN.getId() + ", " + REGISTERED + " INTEGER DEFAULT " + RegisteredState.UNKNOWN.getId() + ", " +
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " + SYSTEM_GIVEN_NAME + " TEXT DEFAULT NULL, " +
SYSTEM_FAMILY_NAME + " TEXT DEFAULT NULL, " +
SYSTEM_JOINED_NAME + " TEXT DEFAULT NULL, " +
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " + SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " + SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " + SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " +
@ -1264,7 +1267,8 @@ public class RecipientDatabase extends Database {
int registeredState = CursorUtil.requireInt(cursor, REGISTERED); int registeredState = CursorUtil.requireInt(cursor, REGISTERED);
String profileKeyString = CursorUtil.requireString(cursor, PROFILE_KEY); String profileKeyString = CursorUtil.requireString(cursor, PROFILE_KEY);
String profileKeyCredentialString = CursorUtil.requireString(cursor, PROFILE_KEY_CREDENTIAL); String profileKeyCredentialString = CursorUtil.requireString(cursor, PROFILE_KEY_CREDENTIAL);
String systemDisplayName = CursorUtil.requireString(cursor, SYSTEM_DISPLAY_NAME); String systemGivenName = CursorUtil.requireString(cursor, SYSTEM_GIVEN_NAME);
String systemFamilyName = CursorUtil.requireString(cursor, SYSTEM_FAMILY_NAME);
String systemContactPhoto = CursorUtil.requireString(cursor, SYSTEM_PHOTO_URI); String systemContactPhoto = CursorUtil.requireString(cursor, SYSTEM_PHOTO_URI);
String systemPhoneLabel = CursorUtil.requireString(cursor, SYSTEM_PHONE_LABEL); String systemPhoneLabel = CursorUtil.requireString(cursor, SYSTEM_PHONE_LABEL);
String systemContactUri = CursorUtil.requireString(cursor, SYSTEM_CONTACT_URI); String systemContactUri = CursorUtil.requireString(cursor, SYSTEM_CONTACT_URI);
@ -1350,7 +1354,7 @@ public class RecipientDatabase extends Database {
RegisteredState.fromId(registeredState), RegisteredState.fromId(registeredState),
profileKey, profileKey,
profileKeyCredential, profileKeyCredential,
systemDisplayName, ProfileName.fromParts(systemGivenName, systemFamilyName),
systemContactPhoto, systemContactPhoto,
systemPhoneLabel, systemPhoneLabel,
systemContactUri, systemContactUri,
@ -1743,7 +1747,7 @@ public class RecipientDatabase extends Database {
public @NonNull List<RecipientId> getSimilarRecipientIds(@NonNull Recipient recipient) { public @NonNull List<RecipientId> getSimilarRecipientIds(@NonNull Recipient recipient) {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] projection = SqlUtil.buildArgs(ID, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ") AS checked_name"); String[] projection = SqlUtil.buildArgs(ID, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ") AS checked_name");
String where = "checked_name = ?"; String where = "checked_name = ?";
String[] arguments = SqlUtil.buildArgs(recipient.getProfileName().toString()); String[] arguments = SqlUtil.buildArgs(recipient.getProfileName().toString());
@ -2246,7 +2250,7 @@ public class RecipientDatabase extends Database {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<RecipientId> results = new LinkedList<>(); List<RecipientId> results = new LinkedList<>();
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))); results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
} }
@ -2260,10 +2264,10 @@ public class RecipientDatabase extends Database {
Map<RecipientId, MaterialColor> updates = new HashMap<>(); Map<RecipientId, MaterialColor> updates = new HashMap<>();
db.beginTransaction(); db.beginTransaction();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_JOINED_NAME}, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)), MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_JOINED_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(COLOR))); cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
@ -2284,7 +2288,7 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " + String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? AND " + REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL AND " + GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " + "(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)"; "(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
String[] args; String[] args;
@ -2295,7 +2299,7 @@ public class RecipientDatabase extends Database {
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", Recipient.self().getId().serialize() }; args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", Recipient.self().getId().serialize() };
} }
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE; String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
} }
@ -2306,7 +2310,7 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " + String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? AND " + REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL AND " + GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " + "(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" + "(" +
PHONE + " GLOB ? OR " + PHONE + " GLOB ? OR " +
SORT_NAME + " GLOB ? OR " + SORT_NAME + " GLOB ? OR " +
@ -2321,7 +2325,7 @@ public class RecipientDatabase extends Database {
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query, String.valueOf(Recipient.self().getId().toLong()) }; args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query, String.valueOf(Recipient.self().getId().toLong()) };
} }
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE; String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
} }
@ -2330,10 +2334,10 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " + String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? AND " + REGISTERED + " != ? AND " +
GROUP_ID + " IS NULL AND " + GROUP_ID + " IS NULL AND " +
SYSTEM_DISPLAY_NAME + " NOT NULL AND " + SYSTEM_CONTACT_URI + " NOT NULL AND " +
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)"; "(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) }; String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) };
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE; String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
} }
@ -2344,15 +2348,15 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " + String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? AND " + REGISTERED + " != ? AND " +
GROUP_ID + " IS NULL AND " + GROUP_ID + " IS NULL AND " +
SYSTEM_DISPLAY_NAME + " NOT NULL AND " + SYSTEM_CONTACT_URI + " NOT NULL AND " +
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " + "(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " +
"(" + "(" +
PHONE + " GLOB ? OR " + PHONE + " GLOB ? OR " +
EMAIL + " GLOB ? OR " + EMAIL + " GLOB ? OR " +
SYSTEM_DISPLAY_NAME + " GLOB ?" + SYSTEM_JOINED_NAME + " GLOB ?" +
")"; ")";
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query, query }; String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query, query };
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE; String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy); return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
} }
@ -2429,7 +2433,7 @@ public class RecipientDatabase extends Database {
String selection = REGISTERED + " = ? AND " + String selection = REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL AND " + GROUP_ID + " IS NULL AND " +
ID + " != ? AND " + ID + " != ? AND " +
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + ID + " IN (" + subquery + "))"; "(" + SYSTEM_CONTACT_URI + " NOT NULL OR " + ID + " IN (" + subquery + "))";
String[] args = new String[] { String.valueOf(RegisteredState.REGISTERED.getId()), Recipient.self().getId().serialize() }; String[] args = new String[] { String.valueOf(RegisteredState.REGISTERED.getId()), Recipient.self().getId().serialize() };
List<Recipient> recipients = new ArrayList<>(); List<Recipient> recipients = new ArrayList<>();
@ -2729,7 +2733,9 @@ public class RecipientDatabase extends Database {
uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1)); uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1));
uuidValues.put(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages()); uuidValues.put(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages());
uuidValues.put(REGISTERED, RegisteredState.REGISTERED.getId()); uuidValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
uuidValues.put(SYSTEM_DISPLAY_NAME, e164Settings.getSystemDisplayName()); uuidValues.put(SYSTEM_GIVEN_NAME, e164Settings.getSystemProfileName().getGivenName());
uuidValues.put(SYSTEM_FAMILY_NAME, e164Settings.getSystemProfileName().getFamilyName());
uuidValues.put(SYSTEM_JOINED_NAME, e164Settings.getSystemProfileName().toString());
uuidValues.put(SYSTEM_PHOTO_URI, e164Settings.getSystemContactPhotoUri()); uuidValues.put(SYSTEM_PHOTO_URI, e164Settings.getSystemContactPhotoUri());
uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel()); uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri()); uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
@ -2840,14 +2846,16 @@ public class RecipientDatabase extends Database {
} }
public void setSystemContactInfo(@NonNull RecipientId id, public void setSystemContactInfo(@NonNull RecipientId id,
@Nullable String displayName, @NonNull ProfileName systemProfileName,
@Nullable String photoUri, @Nullable String photoUri,
@Nullable String systemPhoneLabel, @Nullable String systemPhoneLabel,
int systemPhoneType, int systemPhoneType,
@Nullable String systemContactUri) @Nullable String systemContactUri)
{ {
ContentValues dirtyQualifyingValues = new ContentValues(); ContentValues dirtyQualifyingValues = new ContentValues();
dirtyQualifyingValues.put(SYSTEM_DISPLAY_NAME, displayName); dirtyQualifyingValues.put(SYSTEM_GIVEN_NAME, systemProfileName.getGivenName());
dirtyQualifyingValues.put(SYSTEM_FAMILY_NAME, systemProfileName.getFamilyName());
dirtyQualifyingValues.put(SYSTEM_JOINED_NAME, systemProfileName.toString());
if (update(id, dirtyQualifyingValues)) { if (update(id, dirtyQualifyingValues)) {
markDirty(id, DirtyState.UPDATE); markDirty(id, DirtyState.UPDATE);
@ -2859,11 +2867,12 @@ public class RecipientDatabase extends Database {
refreshQualifyingValues.put(SYSTEM_PHONE_TYPE, systemPhoneType); refreshQualifyingValues.put(SYSTEM_PHONE_TYPE, systemPhoneType);
refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri); refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri);
String joinedName = systemProfileName.toString();
boolean updatedValues = update(id, refreshQualifyingValues); boolean updatedValues = update(id, refreshQualifyingValues);
boolean updatedColor = displayName != null && setColorIfNotSetInternal(id, ContactColors.generateFor(displayName)); boolean updatedColor = !TextUtils.isEmpty(joinedName) && setColorIfNotSetInternal(id, ContactColors.generateFor(joinedName));
if (updatedValues || updatedColor) { if (updatedValues || updatedColor) {
pendingContactInfoMap.put(id, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri)); pendingContactInfoMap.put(id, new PendingContactInfo(systemProfileName, photoUri, systemPhoneLabel, systemContactUri));
} }
ContentValues otherValues = new ContentValues(); ContentValues otherValues = new ContentValues();
@ -2898,7 +2907,9 @@ public class RecipientDatabase extends Database {
ContentValues values = new ContentValues(5); ContentValues values = new ContentValues(5);
values.put(SYSTEM_INFO_PENDING, 0); values.put(SYSTEM_INFO_PENDING, 0);
values.put(SYSTEM_DISPLAY_NAME, (String) null); values.put(SYSTEM_GIVEN_NAME, (String) null);
values.put(SYSTEM_FAMILY_NAME, (String) null);
values.put(SYSTEM_JOINED_NAME, (String) null);
values.put(SYSTEM_PHOTO_URI, (String) null); values.put(SYSTEM_PHOTO_URI, (String) null);
values.put(SYSTEM_PHONE_LABEL, (String) null); values.put(SYSTEM_PHONE_LABEL, (String) null);
values.put(SYSTEM_CONTACT_URI, (String) null); values.put(SYSTEM_CONTACT_URI, (String) null);
@ -2939,7 +2950,7 @@ public class RecipientDatabase extends Database {
private final RegisteredState registered; private final RegisteredState registered;
private final byte[] profileKey; private final byte[] profileKey;
private final ProfileKeyCredential profileKeyCredential; private final ProfileKeyCredential profileKeyCredential;
private final String systemDisplayName; private final ProfileName systemProfileName;
private final String systemContactPhoto; private final String systemContactPhoto;
private final String systemPhoneLabel; private final String systemPhoneLabel;
private final String systemContactUri; private final String systemContactUri;
@ -2981,7 +2992,7 @@ public class RecipientDatabase extends Database {
@NonNull RegisteredState registered, @NonNull RegisteredState registered,
@Nullable byte[] profileKey, @Nullable byte[] profileKey,
@Nullable ProfileKeyCredential profileKeyCredential, @Nullable ProfileKeyCredential profileKeyCredential,
@Nullable String systemDisplayName, @NonNull ProfileName systemProfileName,
@Nullable String systemContactPhoto, @Nullable String systemContactPhoto,
@Nullable String systemPhoneLabel, @Nullable String systemPhoneLabel,
@Nullable String systemContactUri, @Nullable String systemContactUri,
@ -3021,7 +3032,7 @@ public class RecipientDatabase extends Database {
this.registered = registered; this.registered = registered;
this.profileKey = profileKey; this.profileKey = profileKey;
this.profileKeyCredential = profileKeyCredential; this.profileKeyCredential = profileKeyCredential;
this.systemDisplayName = systemDisplayName; this.systemProfileName = systemProfileName;
this.systemContactPhoto = systemContactPhoto; this.systemContactPhoto = systemContactPhoto;
this.systemPhoneLabel = systemPhoneLabel; this.systemPhoneLabel = systemPhoneLabel;
this.systemContactUri = systemContactUri; this.systemContactUri = systemContactUri;
@ -3125,8 +3136,8 @@ public class RecipientDatabase extends Database {
return profileKeyCredential; return profileKeyCredential;
} }
public @Nullable String getSystemDisplayName() { public @NonNull ProfileName getSystemProfileName() {
return systemDisplayName; return systemProfileName;
} }
public @Nullable String getSystemContactPhotoUri() { public @Nullable String getSystemContactPhotoUri() {
@ -3313,13 +3324,13 @@ public class RecipientDatabase extends Database {
private static class PendingContactInfo { private static class PendingContactInfo {
private final String displayName; private final ProfileName profileName;
private final String photoUri; private final String photoUri;
private final String phoneLabel; private final String phoneLabel;
private final String contactUri; private final String contactUri;
private PendingContactInfo(String displayName, String photoUri, String phoneLabel, String contactUri) { private PendingContactInfo(@NonNull ProfileName systemProfileName, String photoUri, String phoneLabel, String contactUri) {
this.displayName = displayName; this.profileName = systemProfileName;
this.photoUri = photoUri; this.photoUri = photoUri;
this.phoneLabel = phoneLabel; this.phoneLabel = phoneLabel;
this.contactUri = contactUri; this.contactUri = contactUri;

View file

@ -171,8 +171,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int LAST_RESET_SESSION_TIME = 87; private static final int LAST_RESET_SESSION_TIME = 87;
private static final int WALLPAPER = 88; private static final int WALLPAPER = 88;
private static final int ABOUT = 89; private static final int ABOUT = 89;
private static final int SPLIT_SYSTEM_NAMES = 90;
private static final int DATABASE_VERSION = 89; private static final int DATABASE_VERSION = 90;
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
@ -1258,6 +1259,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
db.execSQL("ALTER TABLE recipient ADD COLUMN about_emoji TEXT DEFAULT NULL"); db.execSQL("ALTER TABLE recipient ADD COLUMN about_emoji TEXT DEFAULT NULL");
} }
if (oldVersion < SPLIT_SYSTEM_NAMES) {
db.execSQL("ALTER TABLE recipient ADD COLUMN system_family_name TEXT DEFAULT NULL");
db.execSQL("ALTER TABLE recipient ADD COLUMN system_given_name TEXT DEFAULT NULL");
db.execSQL("UPDATE recipient SET system_given_name = system_display_name");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View file

@ -66,7 +66,7 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository
public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) { public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) {
SimpleTask.run(() -> { SimpleTask.run(() -> {
Recipient self = Recipient.self().resolve(); Recipient self = Recipient.self().resolve();
String name = Optional.fromNullable(self.getName(context)).or(""); String name = Optional.fromNullable(self.getDisplayName(context)).or("");
MaterialColor fallbackColor = self.getColor(); MaterialColor fallbackColor = self.getColor();
if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) { if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) {

View file

@ -136,7 +136,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients(); Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients();
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient), out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient),
Optional.fromNullable(recipient.getName(context)), Optional.fromNullable(recipient.isGroup() || recipient.isSystemContact() ? recipient.getDisplayName(context) : null),
getAvatar(recipient.getId(), recipient.getContactUri()), getAvatar(recipient.getId(), recipient.getContactUri()),
Optional.fromNullable(recipient.getColor().serialize()), Optional.fromNullable(recipient.getColor().serialize()),
verifiedMessage, verifiedMessage,
@ -191,7 +191,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
for (Recipient recipient : recipients) { for (Recipient recipient : recipients) {
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId()); Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity); Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
Optional<String> name = Optional.fromNullable(recipient.getName(context)); Optional<String> name = Optional.fromNullable(recipient.isSystemContact() ? recipient.getDisplayName(context) : recipient.getGroupName(context));
Optional<String> color = Optional.of(recipient.getColor().serialize()); Optional<String> color = Optional.of(recipient.getColor().serialize());
Optional<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey()); Optional<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey());
boolean blocked = recipient.isBlocked(); boolean blocked = recipient.isBlocked();

View file

@ -70,6 +70,7 @@ public class ApplicationMigrations {
static final int USER_NOTIFICATION = 25; static final int USER_NOTIFICATION = 25;
static final int DAY_BY_DAY_STICKERS = 26; static final int DAY_BY_DAY_STICKERS = 26;
static final int BLOB_LOCATION = 27; static final int BLOB_LOCATION = 27;
static final int SYSTEM_NAME_SPLIT = 28;
} }
/** /**
@ -296,6 +297,10 @@ public class ApplicationMigrations {
jobs.put(Version.BLOB_LOCATION, new BlobStorageLocationMigrationJob()); jobs.put(Version.BLOB_LOCATION, new BlobStorageLocationMigrationJob());
} }
if (lastSeenVersion < Version.SYSTEM_NAME_SPLIT) {
jobs.put(Version.SYSTEM_NAME_SPLIT, new DirectoryRefreshMigrationJob());
}
return jobs; return jobs;
} }

View file

@ -89,6 +89,13 @@ public final class ProfileName implements Parcelable {
} }
} }
/**
* Creates a profile name that only contains a given name.
*/
public static @NonNull ProfileName asGiven(@Nullable String givenName) {
return fromParts(givenName, null);
}
/** /**
* Creates a profile name, trimming chars until it fits the limits. * Creates a profile name, trimming chars until it fits the limits.
*/ */

View file

@ -74,7 +74,7 @@ class EditGroupProfileRepository implements EditProfileRepository {
String title = groupRecord.getTitle(); String title = groupRecord.getTitle();
return title == null ? "" : title; return title == null ? "" : title;
}) })
.or(() -> recipient.getName(context)); .or(() -> recipient.getGroupName(context));
}, nameConsumer::accept); }, nameConsumer::accept);
} }

View file

@ -89,11 +89,11 @@ public class Recipient {
private final RegisteredState registered; private final RegisteredState registered;
private final byte[] profileKey; private final byte[] profileKey;
private final ProfileKeyCredential profileKeyCredential; private final ProfileKeyCredential profileKeyCredential;
private final String name; private final String groupName;
private final Uri systemContactPhoto; private final Uri systemContactPhoto;
private final String customLabel; private final String customLabel;
private final Uri contactUri; private final Uri contactUri;
private final ProfileName profileName; private final ProfileName signalProfileName;
private final String profileAvatar; private final String profileAvatar;
private final boolean hasProfileImage; private final boolean hasProfileImage;
private final boolean profileSharing; private final boolean profileSharing;
@ -109,6 +109,7 @@ public class Recipient {
private final ChatWallpaper wallpaper; private final ChatWallpaper wallpaper;
private final String about; private final String about;
private final String aboutEmoji; private final String aboutEmoji;
private final ProfileName systemProfileName;
/** /**
@ -326,11 +327,11 @@ public class Recipient {
this.registered = RegisteredState.UNKNOWN; this.registered = RegisteredState.UNKNOWN;
this.profileKey = null; this.profileKey = null;
this.profileKeyCredential = null; this.profileKeyCredential = null;
this.name = null; this.groupName = null;
this.systemContactPhoto = null; this.systemContactPhoto = null;
this.customLabel = null; this.customLabel = null;
this.contactUri = null; this.contactUri = null;
this.profileName = ProfileName.EMPTY; this.signalProfileName = ProfileName.EMPTY;
this.profileAvatar = null; this.profileAvatar = null;
this.hasProfileImage = false; this.hasProfileImage = false;
this.profileSharing = false; this.profileSharing = false;
@ -345,6 +346,7 @@ public class Recipient {
this.wallpaper = null; this.wallpaper = null;
this.about = null; this.about = null;
this.aboutEmoji = null; this.aboutEmoji = null;
this.systemProfileName = ProfileName.EMPTY;
} }
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) { public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
@ -371,11 +373,11 @@ public class Recipient {
this.registered = details.registered; this.registered = details.registered;
this.profileKey = details.profileKey; this.profileKey = details.profileKey;
this.profileKeyCredential = details.profileKeyCredential; this.profileKeyCredential = details.profileKeyCredential;
this.name = details.name; this.groupName = details.groupName;
this.systemContactPhoto = details.systemContactPhoto; this.systemContactPhoto = details.systemContactPhoto;
this.customLabel = details.customLabel; this.customLabel = details.customLabel;
this.contactUri = details.contactUri; this.contactUri = details.contactUri;
this.profileName = details.profileName; this.signalProfileName = details.profileName;
this.profileAvatar = details.profileAvatar; this.profileAvatar = details.profileAvatar;
this.hasProfileImage = details.hasProfileImage; this.hasProfileImage = details.hasProfileImage;
this.profileSharing = details.profileSharing; this.profileSharing = details.profileSharing;
@ -390,6 +392,7 @@ public class Recipient {
this.wallpaper = details.wallpaper; this.wallpaper = details.wallpaper;
this.about = details.about; this.about = details.about;
this.aboutEmoji = details.aboutEmoji; this.aboutEmoji = details.aboutEmoji;
this.systemProfileName = details.systemProfileName;
} }
public @NonNull RecipientId getId() { public @NonNull RecipientId getId() {
@ -404,8 +407,8 @@ public class Recipient {
return contactUri; return contactUri;
} }
public @Nullable String getName(@NonNull Context context) { public @Nullable String getGroupName(@NonNull Context context) {
if (this.name == null && groupId != null && groupId.isMms()) { if (this.groupName == null && groupId != null && groupId.isMms()) {
List<String> names = new LinkedList<>(); List<String> names = new LinkedList<>();
for (Recipient recipient : participants) { for (Recipient recipient : participants) {
@ -413,27 +416,32 @@ public class Recipient {
} }
return Util.join(names, ", "); return Util.join(names, ", ");
} else if (name == null && groupId != null && groupId.isPush()) { } else if (groupName == null && groupId != null && groupId.isPush()) {
return context.getString(R.string.RecipientProvider_unnamed_group); return context.getString(R.string.RecipientProvider_unnamed_group);
} else { } else {
return this.name; return this.groupName;
} }
} }
public boolean hasName() { public boolean hasName() {
return name != null; return groupName != null;
} }
/** /**
* False iff it {@link #getDisplayName} would fall back to e164, email or unknown. * False iff it {@link #getDisplayName} would fall back to e164, email or unknown.
*/ */
public boolean hasAUserSetDisplayName(@NonNull Context context) { public boolean hasAUserSetDisplayName(@NonNull Context context) {
return !TextUtils.isEmpty(getName(context)) || return !TextUtils.isEmpty(getGroupName(context)) ||
!TextUtils.isEmpty(getSystemProfileName().toString()) ||
!TextUtils.isEmpty(getProfileName().toString()); !TextUtils.isEmpty(getProfileName().toString());
} }
public @NonNull String getDisplayName(@NonNull Context context) { public @NonNull String getDisplayName(@NonNull Context context) {
String name = getName(context); String name = getGroupName(context);
if (Util.isEmpty(name)) {
name = getSystemProfileName().toString();
}
if (Util.isEmpty(name)) { if (Util.isEmpty(name)) {
name = getProfileName().toString(); name = getProfileName().toString();
@ -455,7 +463,11 @@ public class Recipient {
} }
public @NonNull String getDisplayNameOrUsername(@NonNull Context context) { public @NonNull String getDisplayNameOrUsername(@NonNull Context context) {
String name = getName(context); String name = getGroupName(context);
if (Util.isEmpty(name)) {
name = getSystemProfileName().toString();
}
if (Util.isEmpty(name)) { if (Util.isEmpty(name)) {
name = StringUtil.isolateBidi(getProfileName().toString()); name = StringUtil.isolateBidi(getProfileName().toString());
@ -481,11 +493,16 @@ public class Recipient {
} }
public @NonNull String getMentionDisplayName(@NonNull Context context) { public @NonNull String getMentionDisplayName(@NonNull Context context) {
String name = isSelf ? getProfileName().toString() : getName(context); String name = isSelf ? getProfileName().toString() : getGroupName(context);
name = StringUtil.isolateBidi(name); name = StringUtil.isolateBidi(name);
if (Util.isEmpty(name)) { if (Util.isEmpty(name)) {
name = isSelf ? getName(context) : getProfileName().toString(); name = isSelf ? getGroupName(context) : getSystemProfileName().toString();
name = StringUtil.isolateBidi(name);
}
if (Util.isEmpty(name)) {
name = isSelf ? getGroupName(context) : getProfileName().toString();
name = StringUtil.isolateBidi(name); name = StringUtil.isolateBidi(name);
} }
@ -505,7 +522,8 @@ public class Recipient {
} }
public @NonNull String getShortDisplayName(@NonNull Context context) { public @NonNull String getShortDisplayName(@NonNull Context context) {
String name = Util.getFirstNonEmpty(getName(context), String name = Util.getFirstNonEmpty(getGroupName(context),
getSystemProfileName().getGivenName(),
getProfileName().getGivenName(), getProfileName().getGivenName(),
getDisplayName(context)); getDisplayName(context));
@ -513,7 +531,8 @@ public class Recipient {
} }
public @NonNull String getShortDisplayNameIncludingUsername(@NonNull Context context) { public @NonNull String getShortDisplayNameIncludingUsername(@NonNull Context context) {
String name = Util.getFirstNonEmpty(getName(context), String name = Util.getFirstNonEmpty(getGroupName(context),
getSystemProfileName().getGivenName(),
getProfileName().getGivenName(), getProfileName().getGivenName(),
getDisplayName(context), getDisplayName(context),
getUsername().orNull()); getUsername().orNull());
@ -526,7 +545,7 @@ public class Recipient {
return MaterialColor.GROUP; return MaterialColor.GROUP;
} else if (color != null) { } else if (color != null) {
return color; return color;
} else if (name != null || profileSharing) { } else if (groupName != null || profileSharing) {
Log.w(TAG, "Had no color for " + id + "! Saving a new one."); Log.w(TAG, "Had no color for " + id + "! Saving a new one.");
Context context = ApplicationDependencies.getApplication(); Context context = ApplicationDependencies.getApplication();
@ -672,7 +691,11 @@ public class Recipient {
} }
public @NonNull ProfileName getProfileName() { public @NonNull ProfileName getProfileName() {
return profileName; return signalProfileName;
}
private @NonNull ProfileName getSystemProfileName() {
return systemProfileName;
} }
public @Nullable String getProfileAvatar() { public @Nullable String getProfileAvatar() {
@ -748,7 +771,7 @@ public class Recipient {
else if (isResolving()) return fallbackPhotoProvider.getPhotoForResolvingRecipient(); else if (isResolving()) return fallbackPhotoProvider.getPhotoForResolvingRecipient();
else if (isGroupInternal()) return fallbackPhotoProvider.getPhotoForGroup(); else if (isGroupInternal()) return fallbackPhotoProvider.getPhotoForGroup();
else if (isGroup()) return fallbackPhotoProvider.getPhotoForGroup(); else if (isGroup()) return fallbackPhotoProvider.getPhotoForGroup();
else if (!TextUtils.isEmpty(name)) return fallbackPhotoProvider.getPhotoForRecipientWithName(name); else if (!TextUtils.isEmpty(groupName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(groupName);
else return fallbackPhotoProvider.getPhotoForRecipientWithoutName(); else return fallbackPhotoProvider.getPhotoForRecipientWithoutName();
} }
@ -1003,11 +1026,12 @@ public class Recipient {
registered == other.registered && registered == other.registered &&
Arrays.equals(profileKey, other.profileKey) && Arrays.equals(profileKey, other.profileKey) &&
Objects.equals(profileKeyCredential, other.profileKeyCredential) && Objects.equals(profileKeyCredential, other.profileKeyCredential) &&
Objects.equals(name, other.name) && Objects.equals(groupName, other.groupName) &&
Objects.equals(systemContactPhoto, other.systemContactPhoto) && Objects.equals(systemContactPhoto, other.systemContactPhoto) &&
Objects.equals(customLabel, other.customLabel) && Objects.equals(customLabel, other.customLabel) &&
Objects.equals(contactUri, other.contactUri) && Objects.equals(contactUri, other.contactUri) &&
Objects.equals(profileName, other.profileName) && Objects.equals(signalProfileName, other.signalProfileName) &&
Objects.equals(systemProfileName, other.systemProfileName) &&
Objects.equals(profileAvatar, other.profileAvatar) && Objects.equals(profileAvatar, other.profileAvatar) &&
Objects.equals(notificationChannel, other.notificationChannel) && Objects.equals(notificationChannel, other.notificationChannel) &&
unidentifiedAccessMode == other.unidentifiedAccessMode && unidentifiedAccessMode == other.unidentifiedAccessMode &&

View file

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.recipients;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -33,7 +32,7 @@ public class RecipientDetails {
final String e164; final String e164;
final String email; final String email;
final GroupId groupId; final GroupId groupId;
final String name; final String groupName;
final String customLabel; final String customLabel;
final Uri systemContactPhoto; final Uri systemContactPhoto;
final Uri contactUri; final Uri contactUri;
@ -69,8 +68,9 @@ public class RecipientDetails {
final ChatWallpaper wallpaper; final ChatWallpaper wallpaper;
final String about; final String about;
final String aboutEmoji; final String aboutEmoji;
final ProfileName systemProfileName;
public RecipientDetails(@Nullable String name, public RecipientDetails(@Nullable String groupName,
@NonNull Optional<Long> groupAvatarId, @NonNull Optional<Long> groupAvatarId,
boolean systemContact, boolean systemContact,
boolean isSelf, boolean isSelf,
@ -117,9 +117,8 @@ public class RecipientDetails {
this.wallpaper = settings.getWallpaper(); this.wallpaper = settings.getWallpaper();
this.about = settings.getAbout(); this.about = settings.getAbout();
this.aboutEmoji = settings.getAboutEmoji(); this.aboutEmoji = settings.getAboutEmoji();
this.systemProfileName = settings.getSystemProfileName();
if (name == null) this.name = settings.getSystemDisplayName(); this.groupName = groupName;
else this.name = name;
} }
/** /**
@ -159,7 +158,7 @@ public class RecipientDetails {
this.notificationChannel = null; this.notificationChannel = null;
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN; this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
this.forceSmsSelection = false; this.forceSmsSelection = false;
this.name = null; this.groupName = null;
this.groupsV2Capability = Recipient.Capability.UNKNOWN; this.groupsV2Capability = Recipient.Capability.UNKNOWN;
this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN; this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN;
this.storageId = null; this.storageId = null;
@ -167,10 +166,11 @@ public class RecipientDetails {
this.wallpaper = null; this.wallpaper = null;
this.about = null; this.about = null;
this.aboutEmoji = null; this.aboutEmoji = null;
this.systemProfileName = ProfileName.EMPTY;
} }
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) { public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
boolean systemContact = !TextUtils.isEmpty(settings.getSystemDisplayName()); boolean systemContact = !settings.getSystemProfileName().isEmpty();
boolean isSelf = (settings.getE164() != null && settings.getE164().equals(TextSecurePreferences.getLocalNumber(context))) || boolean isSelf = (settings.getE164() != null && settings.getE164().equals(TextSecurePreferences.getLocalNumber(context))) ||
(settings.getUuid() != null && settings.getUuid().equals(TextSecurePreferences.getLocalUuid(context))); (settings.getUuid() != null && settings.getUuid().equals(TextSecurePreferences.getLocalUuid(context)));