Split system names into first / last.
This commit is contained in:
parent
dc9fceb8cf
commit
fd9c420dc8
15 changed files with 458 additions and 157 deletions
|
@ -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,
|
||||
try (Cursor numberCursor = context.getContentResolver().query(Phone.CONTENT_URI,
|
||||
null,
|
||||
Phone.CONTACT_ID + " = ?",
|
||||
new String[] {contactData.id + ""}, null);
|
||||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
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 (!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);
|
||||
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 {
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ?" +
|
||||
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 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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
@ -748,7 +771,7 @@ public class Recipient {
|
|||
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 if (!TextUtils.isEmpty(groupName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(groupName);
|
||||
else return fallbackPhotoProvider.getPhotoForRecipientWithoutName();
|
||||
}
|
||||
|
||||
|
@ -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 &&
|
||||
|
|
|
@ -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)));
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue