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.os.Parcel;
import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.ProfileName;
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.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@ -65,6 +74,9 @@ public class ContactAccessor {
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();
public static synchronized ContactAccessor getInstance() {
@ -85,55 +97,42 @@ public class ContactAccessor {
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) {
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) {
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;
return context.getContentResolver().query(uri, projection, where, args, orderBy);
}
public String getNameFromContact(Context context, Uri uri) {
@ -160,13 +159,13 @@ public class ContactAccessor {
private ContactData getContactData(Context context, String displayName, long id) {
ContactData contactData = new ContactData(id, displayName);
Cursor numberCursor = null;
try {
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null,
Phone.CONTACT_ID + " = ?",
new String[] {contactData.id + ""}, null);
try (Cursor numberCursor = context.getContentResolver().query(Phone.CONTENT_URI,
null,
Phone.CONTACT_ID + " = ?",
new String[] {contactData.id + ""},
null))
{
while (numberCursor != null && numberCursor.moveToNext()) {
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
@ -175,9 +174,6 @@ public class ContactAccessor {
contactData.numbers.add(new NumberData(typeLabel, number));
}
} finally {
if (numberCursor != null)
numberCursor.close();
}
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<>(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);
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.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.text.TextUtils;
@ -43,6 +42,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
@ -63,6 +63,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@ -320,27 +321,63 @@ public class DirectoryHelper {
contactsDatabase.removeDeletedRawContacts(account.getAccount());
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()) {
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
String lookupKey = getLookupKey(cursor);
String mimeType = getMimeType(cursor);
ContactHolder contactHolder = new ContactHolder(lookupKey);
if (isValidContactNumber(number)) {
String formattedNumber = PhoneNumberFormatter.get(context).format(number);
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());
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)) {
String formattedNumber = PhoneNumberFormatter.get(context).format(number);
String realNumber = Util.getFirstNonEmpty(rewrites.get(formattedNumber), formattedNumber);
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 {
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) {
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) {
AccountManager accountManager = AccountManager.get(context);
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.RequestGroupV2InfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileName;
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 MESSAGE_EXPIRATION_TIME = "message_expiration_time";
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";
public static final String SYSTEM_PHONE_TYPE = "system_phone_type";
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,
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,
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,
NOTIFICATION_CHANNEL,
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[] 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};
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};
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_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)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList().toArray(new String[0]);
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[] {
"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, " +
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
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_PHONE_LABEL + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " +
@ -1264,7 +1267,8 @@ public class RecipientDatabase extends Database {
int registeredState = CursorUtil.requireInt(cursor, REGISTERED);
String profileKeyString = CursorUtil.requireString(cursor, PROFILE_KEY);
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 systemPhoneLabel = CursorUtil.requireString(cursor, SYSTEM_PHONE_LABEL);
String systemContactUri = CursorUtil.requireString(cursor, SYSTEM_CONTACT_URI);
@ -1350,7 +1354,7 @@ public class RecipientDatabase extends Database {
RegisteredState.fromId(registeredState),
profileKey,
profileKeyCredential,
systemDisplayName,
ProfileName.fromParts(systemGivenName, systemFamilyName),
systemContactPhoto,
systemPhoneLabel,
systemContactUri,
@ -1743,7 +1747,7 @@ public class RecipientDatabase extends Database {
public @NonNull List<RecipientId> getSimilarRecipientIds(@NonNull Recipient recipient) {
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[] arguments = SqlUtil.buildArgs(recipient.getProfileName().toString());
@ -2246,7 +2250,7 @@ public class RecipientDatabase extends Database {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
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()) {
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
}
@ -2260,10 +2264,10 @@ public class RecipientDatabase extends Database {
Map<RecipientId, MaterialColor> updates = new HashMap<>();
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()) {
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)));
ContentValues contentValues = new ContentValues(1);
@ -2284,7 +2288,7 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? 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)";
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() };
}
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);
}
@ -2306,7 +2310,7 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? 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 " +
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()) };
}
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);
}
@ -2330,10 +2334,10 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? 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)";
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);
}
@ -2344,15 +2348,15 @@ public class RecipientDatabase extends Database {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? 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 + " GLOB ? OR " +
EMAIL + " GLOB ? OR " +
SYSTEM_DISPLAY_NAME + " GLOB ?" +
PHONE + " GLOB ? OR " +
EMAIL + " GLOB ? OR " +
SYSTEM_JOINED_NAME + " GLOB ?" +
")";
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);
}
@ -2429,7 +2433,7 @@ public class RecipientDatabase extends Database {
String selection = REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL 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() };
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(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages());
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_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
@ -2840,14 +2846,16 @@ public class RecipientDatabase extends Database {
}
public void setSystemContactInfo(@NonNull RecipientId id,
@Nullable String displayName,
@NonNull ProfileName systemProfileName,
@Nullable String photoUri,
@Nullable String systemPhoneLabel,
int systemPhoneType,
@Nullable String systemContactUri)
{
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)) {
markDirty(id, DirtyState.UPDATE);
@ -2859,11 +2867,12 @@ public class RecipientDatabase extends Database {
refreshQualifyingValues.put(SYSTEM_PHONE_TYPE, systemPhoneType);
refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri);
String joinedName = systemProfileName.toString();
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) {
pendingContactInfoMap.put(id, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri));
pendingContactInfoMap.put(id, new PendingContactInfo(systemProfileName, photoUri, systemPhoneLabel, systemContactUri));
}
ContentValues otherValues = new ContentValues();
@ -2898,7 +2907,9 @@ public class RecipientDatabase extends Database {
ContentValues values = new ContentValues(5);
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_PHONE_LABEL, (String) null);
values.put(SYSTEM_CONTACT_URI, (String) null);
@ -2939,7 +2950,7 @@ public class RecipientDatabase extends Database {
private final RegisteredState registered;
private final byte[] profileKey;
private final ProfileKeyCredential profileKeyCredential;
private final String systemDisplayName;
private final ProfileName systemProfileName;
private final String systemContactPhoto;
private final String systemPhoneLabel;
private final String systemContactUri;
@ -2981,7 +2992,7 @@ public class RecipientDatabase extends Database {
@NonNull RegisteredState registered,
@Nullable byte[] profileKey,
@Nullable ProfileKeyCredential profileKeyCredential,
@Nullable String systemDisplayName,
@NonNull ProfileName systemProfileName,
@Nullable String systemContactPhoto,
@Nullable String systemPhoneLabel,
@Nullable String systemContactUri,
@ -3021,7 +3032,7 @@ public class RecipientDatabase extends Database {
this.registered = registered;
this.profileKey = profileKey;
this.profileKeyCredential = profileKeyCredential;
this.systemDisplayName = systemDisplayName;
this.systemProfileName = systemProfileName;
this.systemContactPhoto = systemContactPhoto;
this.systemPhoneLabel = systemPhoneLabel;
this.systemContactUri = systemContactUri;
@ -3125,8 +3136,8 @@ public class RecipientDatabase extends Database {
return profileKeyCredential;
}
public @Nullable String getSystemDisplayName() {
return systemDisplayName;
public @NonNull ProfileName getSystemProfileName() {
return systemProfileName;
}
public @Nullable String getSystemContactPhotoUri() {
@ -3313,13 +3324,13 @@ public class RecipientDatabase extends Database {
private static class PendingContactInfo {
private final String displayName;
private final String photoUri;
private final String phoneLabel;
private final String contactUri;
private final ProfileName profileName;
private final String photoUri;
private final String phoneLabel;
private final String contactUri;
private PendingContactInfo(String displayName, String photoUri, String phoneLabel, String contactUri) {
this.displayName = displayName;
private PendingContactInfo(@NonNull ProfileName systemProfileName, String photoUri, String phoneLabel, String contactUri) {
this.profileName = systemProfileName;
this.photoUri = photoUri;
this.phoneLabel = phoneLabel;
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 WALLPAPER = 88;
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 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");
}
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();
} finally {
db.endTransaction();

View file

@ -66,7 +66,7 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository
public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) {
SimpleTask.run(() -> {
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();
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();
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()),
Optional.fromNullable(recipient.getColor().serialize()),
verifiedMessage,
@ -191,7 +191,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
for (Recipient recipient : recipients) {
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
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<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey());
boolean blocked = recipient.isBlocked();

View file

@ -70,6 +70,7 @@ public class ApplicationMigrations {
static final int USER_NOTIFICATION = 25;
static final int DAY_BY_DAY_STICKERS = 26;
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());
}
if (lastSeenVersion < Version.SYSTEM_NAME_SPLIT) {
jobs.put(Version.SYSTEM_NAME_SPLIT, new DirectoryRefreshMigrationJob());
}
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.
*/

View file

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

View file

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

View file

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