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.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||||
import android.provider.ContactsContract.Contacts;
|
import android.provider.ContactsContract.Contacts;
|
||||||
import android.provider.ContactsContract.PhoneLookup;
|
import android.provider.ContactsContract.PhoneLookup;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
|
@ -65,6 +74,9 @@ public class ContactAccessor {
|
||||||
|
|
||||||
public static final String PUSH_COLUMN = "push";
|
public static final String PUSH_COLUMN = "push";
|
||||||
|
|
||||||
|
private static final String GIVEN_NAME = ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME;
|
||||||
|
private static final String FAMILY_NAME = ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME;
|
||||||
|
|
||||||
private static final ContactAccessor instance = new ContactAccessor();
|
private static final ContactAccessor instance = new ContactAccessor();
|
||||||
|
|
||||||
public static synchronized ContactAccessor getInstance() {
|
public static synchronized ContactAccessor getInstance() {
|
||||||
|
@ -85,55 +97,42 @@ public class ContactAccessor {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets and returns a cursor of data for all contacts, containing both phone number data and
|
||||||
|
* structured name data.
|
||||||
|
*
|
||||||
|
* Cursor rows are ordered as follows:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Contact Lookup Key</li>
|
||||||
|
* <li>Mimetype</li>
|
||||||
|
* <li>id</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* The lookup key is a fixed value that allows you to verify two rows in the database actually
|
||||||
|
* belong to the same contact, since the contact uri can be unstable (if a sync fails, say.)
|
||||||
|
*
|
||||||
|
* We order by id explicitly here for the same contact sync failure error, which could result in
|
||||||
|
* multiple structured name rows for the same user. By ordering by id DESC, we ensure we get
|
||||||
|
* whatever the latest input data was.
|
||||||
|
*
|
||||||
|
* What this results in is a cursor that looks like:
|
||||||
|
*
|
||||||
|
* Alice phone 1
|
||||||
|
* Alice phone 2
|
||||||
|
* Alice structured name 2
|
||||||
|
* Alice structured name 1
|
||||||
|
* Bob phone 1
|
||||||
|
* ... etc.
|
||||||
|
*/
|
||||||
public Cursor getAllSystemContacts(Context context) {
|
public Cursor getAllSystemContacts(Context context) {
|
||||||
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY, Phone.TYPE}, null, null, null);
|
Uri uri = ContactsContract.Data.CONTENT_URI;
|
||||||
}
|
String[] projection = SqlUtil.buildArgs(ContactsContract.Data.MIMETYPE, Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY, Phone.TYPE, GIVEN_NAME, FAMILY_NAME);
|
||||||
|
String where = ContactsContract.Data.MIMETYPE + " IN (?, ?)";
|
||||||
|
String[] args = SqlUtil.buildArgs(Phone.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
|
||||||
|
String orderBy = Phone.LOOKUP_KEY + " ASC, " + ContactsContract.Data.MIMETYPE + " DESC, " + ContactsContract.CommonDataKinds.Phone._ID + " DESC";
|
||||||
|
|
||||||
public boolean isSystemContact(Context context, String number) {
|
return context.getContentResolver().query(uri, projection, where, args, orderBy);
|
||||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
|
|
||||||
String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY,
|
|
||||||
PhoneLookup._ID, PhoneLookup.NUMBER};
|
|
||||||
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) cursor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ContactData> getContactsWithPush(Context context) {
|
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
|
||||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
|
||||||
|
|
||||||
final List<String> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered())
|
|
||||||
.map(Recipient::resolved)
|
|
||||||
.filter(r -> r.getE164().isPresent())
|
|
||||||
.map(Recipient::requireE164)
|
|
||||||
.toList();
|
|
||||||
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
|
|
||||||
|
|
||||||
for (String registeredAddress : registeredAddresses) {
|
|
||||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress));
|
|
||||||
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
|
||||||
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
|
|
||||||
contactData.numbers.add(new NumberData("TextSecure", registeredAddress));
|
|
||||||
lookupData.add(contactData);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (lookupCursor != null)
|
|
||||||
lookupCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lookupData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNameFromContact(Context context, Uri uri) {
|
public String getNameFromContact(Context context, Uri uri) {
|
||||||
|
@ -160,13 +159,13 @@ public class ContactAccessor {
|
||||||
|
|
||||||
private ContactData getContactData(Context context, String displayName, long id) {
|
private ContactData getContactData(Context context, String displayName, long id) {
|
||||||
ContactData contactData = new ContactData(id, displayName);
|
ContactData contactData = new ContactData(id, displayName);
|
||||||
Cursor numberCursor = null;
|
|
||||||
|
|
||||||
try {
|
try (Cursor numberCursor = context.getContentResolver().query(Phone.CONTENT_URI,
|
||||||
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null,
|
null,
|
||||||
Phone.CONTACT_ID + " = ?",
|
Phone.CONTACT_ID + " = ?",
|
||||||
new String[] {contactData.id + ""}, null);
|
new String[] {contactData.id + ""},
|
||||||
|
null))
|
||||||
|
{
|
||||||
while (numberCursor != null && numberCursor.moveToNext()) {
|
while (numberCursor != null && numberCursor.moveToNext()) {
|
||||||
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
|
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
|
||||||
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
|
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
|
||||||
|
@ -175,9 +174,6 @@ public class ContactAccessor {
|
||||||
|
|
||||||
contactData.numbers.add(new NumberData(typeLabel, number));
|
contactData.numbers.add(new NumberData(typeLabel, number));
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
if (numberCursor != null)
|
|
||||||
numberCursor.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return contactData;
|
return contactData;
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class ContactRepository {
|
||||||
add(new Pair<>(ID_COLUMN, cursor -> CursorUtil.requireLong(cursor, RecipientDatabase.ID)));
|
add(new Pair<>(ID_COLUMN, cursor -> CursorUtil.requireLong(cursor, RecipientDatabase.ID)));
|
||||||
|
|
||||||
add(new Pair<>(NAME_COLUMN, cursor -> {
|
add(new Pair<>(NAME_COLUMN, cursor -> {
|
||||||
String system = CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_DISPLAY_NAME);
|
String system = CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_JOINED_NAME);
|
||||||
String profile = CursorUtil.requireString(cursor, RecipientDatabase.SEARCH_PROFILE_NAME);
|
String profile = CursorUtil.requireString(cursor, RecipientDatabase.SEARCH_PROFILE_NAME);
|
||||||
|
|
||||||
return Util.getFirstNonEmpty(system, profile);
|
return Util.getFirstNonEmpty(system, profile);
|
||||||
|
|
|
@ -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.Context;
|
||||||
import android.content.OperationApplicationException;
|
import android.content.OperationApplicationException;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -43,6 +42,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationUtil;
|
import org.thoughtcrime.securesms.registration.RegistrationUtil;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
|
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||||
|
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||||
import org.thoughtcrime.securesms.util.ProfileUtil;
|
import org.thoughtcrime.securesms.util.ProfileUtil;
|
||||||
import org.thoughtcrime.securesms.util.SetUtil;
|
import org.thoughtcrime.securesms.util.SetUtil;
|
||||||
import org.thoughtcrime.securesms.util.Stopwatch;
|
import org.thoughtcrime.securesms.util.Stopwatch;
|
||||||
|
@ -63,6 +63,7 @@ import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -320,27 +321,63 @@ public class DirectoryHelper {
|
||||||
contactsDatabase.removeDeletedRawContacts(account.getAccount());
|
contactsDatabase.removeDeletedRawContacts(account.getAccount());
|
||||||
contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing);
|
contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing);
|
||||||
|
|
||||||
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
|
|
||||||
BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate();
|
BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate();
|
||||||
|
|
||||||
try {
|
ContactHolder old = null;
|
||||||
|
try (Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
|
String lookupKey = getLookupKey(cursor);
|
||||||
|
String mimeType = getMimeType(cursor);
|
||||||
|
ContactHolder contactHolder = new ContactHolder(lookupKey);
|
||||||
|
|
||||||
|
if (!isPhoneMimeType(mimeType)) {
|
||||||
|
Log.w(TAG, "Ignoring unexpected mime type: " + mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (getLookupKey(cursor).equals(lookupKey) && isPhoneMimeType(getMimeType(cursor))) {
|
||||||
|
String number = CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||||
|
|
||||||
if (isValidContactNumber(number)) {
|
if (isValidContactNumber(number)) {
|
||||||
String formattedNumber = PhoneNumberFormatter.get(context).format(number);
|
String formattedNumber = PhoneNumberFormatter.get(context).format(number);
|
||||||
String realNumber = Util.getFirstNonEmpty(rewrites.get(formattedNumber), formattedNumber);
|
String realNumber = Util.getFirstNonEmpty(rewrites.get(formattedNumber), formattedNumber);
|
||||||
RecipientId recipientId = Recipient.externalContact(context, realNumber).getId();
|
|
||||||
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
|
||||||
String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
|
|
||||||
String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL));
|
|
||||||
int phoneType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE));
|
|
||||||
Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)),
|
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
|
|
||||||
|
|
||||||
handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, phoneType, contactUri.toString());
|
PhoneNumberRecord.Builder builder = new PhoneNumberRecord.Builder();
|
||||||
|
|
||||||
|
builder.withRecipientId(Recipient.externalContact(context, realNumber).getId());
|
||||||
|
builder.withDisplayName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
||||||
|
builder.withContactPhotoUri(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
|
||||||
|
builder.withContactLabel(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LABEL));
|
||||||
|
builder.withPhoneType(CursorUtil.requireInt(cursor, ContactsContract.CommonDataKinds.Phone.TYPE));
|
||||||
|
builder.withContactUri(ContactsContract.Contacts.getLookupUri(CursorUtil.requireLong(cursor, ContactsContract.CommonDataKinds.Phone._ID),
|
||||||
|
CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
|
||||||
|
|
||||||
|
contactHolder.addPhoneNumberRecord(builder.build());
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Skipping phone entry with invalid number");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor.moveToNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getLookupKey(cursor).equals(lookupKey)) {
|
||||||
|
if (isStructuredNameMimeType(getMimeType(cursor))) {
|
||||||
|
StructuredNameRecord.Builder builder = new StructuredNameRecord.Builder();
|
||||||
|
|
||||||
|
builder.withGivenName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
|
||||||
|
builder.withFamilyName(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
|
||||||
|
|
||||||
|
contactHolder.setStructuredNameRecord(builder.build());
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Skipping invalid mimeType " + mimeType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "No structured name for user, rolling back cursor.");
|
||||||
|
cursor.moveToPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
contactHolder.commit(handle);
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
handle.finish();
|
handle.finish();
|
||||||
}
|
}
|
||||||
|
@ -358,10 +395,26 @@ public class DirectoryHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isPhoneMimeType(@NonNull String mimeType) {
|
||||||
|
return ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isStructuredNameMimeType(@NonNull String mimeType) {
|
||||||
|
return ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isValidContactNumber(@Nullable String number) {
|
private static boolean isValidContactNumber(@Nullable String number) {
|
||||||
return !TextUtils.isEmpty(number) && !UuidUtil.isUuid(number);
|
return !TextUtils.isEmpty(number) && !UuidUtil.isUuid(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @NonNull String getLookupKey(@NonNull Cursor cursor) {
|
||||||
|
return Objects.requireNonNull(CursorUtil.requireString(cursor, ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull String getMimeType(@NonNull Cursor cursor) {
|
||||||
|
return CursorUtil.requireString(cursor, ContactsContract.Data.MIMETYPE);
|
||||||
|
}
|
||||||
|
|
||||||
private static @Nullable AccountHolder getOrCreateSystemAccount(Context context) {
|
private static @Nullable AccountHolder getOrCreateSystemAccount(Context context) {
|
||||||
AccountManager accountManager = AccountManager.get(context);
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
Account[] accounts = accountManager.getAccountsByType(BuildConfig.APPLICATION_ID);
|
Account[] accounts = accountManager.getAccountsByType(BuildConfig.APPLICATION_ID);
|
||||||
|
|
|
@ -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.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -113,7 +112,9 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
||||||
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
|
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
|
||||||
public static final String REGISTERED = "registered";
|
public static final String REGISTERED = "registered";
|
||||||
public static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
public static final String SYSTEM_JOINED_NAME = "system_display_name";
|
||||||
|
public static final String SYSTEM_FAMILY_NAME = "system_family_name";
|
||||||
|
public static final String SYSTEM_GIVEN_NAME = "system_given_name";
|
||||||
private static final String SYSTEM_PHOTO_URI = "system_photo_uri";
|
private static final String SYSTEM_PHOTO_URI = "system_photo_uri";
|
||||||
public static final String SYSTEM_PHONE_TYPE = "system_phone_type";
|
public static final String SYSTEM_PHONE_TYPE = "system_phone_type";
|
||||||
public static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
public static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||||
|
@ -157,7 +158,7 @@ public class RecipientDatabase extends Database {
|
||||||
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
|
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
|
||||||
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
|
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
|
||||||
PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
|
PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
|
||||||
SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
|
SYSTEM_GIVEN_NAME, SYSTEM_FAMILY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
|
||||||
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH,
|
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH,
|
||||||
NOTIFICATION_CHANNEL,
|
NOTIFICATION_CHANNEL,
|
||||||
UNIDENTIFIED_ACCESS_MODE,
|
UNIDENTIFIED_ACCESS_MODE,
|
||||||
|
@ -170,15 +171,15 @@ public class RecipientDatabase extends Database {
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] ID_PROJECTION = new String[]{ID};
|
private static final String[] ID_PROJECTION = new String[]{ID};
|
||||||
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
||||||
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
|
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
|
||||||
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||||
.map(columnName -> TABLE_NAME + "." + columnName)
|
.map(columnName -> TABLE_NAME + "." + columnName)
|
||||||
.toList().toArray(new String[0]);
|
.toList().toArray(new String[0]);
|
||||||
|
|
||||||
static final String[] TYPED_RECIPIENT_PROJECTION_NO_ID = Arrays.copyOfRange(TYPED_RECIPIENT_PROJECTION, 1, TYPED_RECIPIENT_PROJECTION.length);
|
static final String[] TYPED_RECIPIENT_PROJECTION_NO_ID = Arrays.copyOfRange(TYPED_RECIPIENT_PROJECTION, 1, TYPED_RECIPIENT_PROJECTION.length);
|
||||||
|
|
||||||
private static final String[] MENTION_SEARCH_PROJECTION = new String[]{ID, removeWhitespace("COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ", " + nullIfEmpty(PHONE) + ")") + " AS " + SORT_NAME};
|
private static final String[] MENTION_SEARCH_PROJECTION = new String[]{ID, removeWhitespace("COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ", " + nullIfEmpty(PHONE) + ")") + " AS " + SORT_NAME};
|
||||||
|
|
||||||
public static final String[] CREATE_INDEXS = new String[] {
|
public static final String[] CREATE_INDEXS = new String[] {
|
||||||
"CREATE INDEX IF NOT EXISTS recipient_dirty_index ON " + TABLE_NAME + " (" + DIRTY + ");",
|
"CREATE INDEX IF NOT EXISTS recipient_dirty_index ON " + TABLE_NAME + " (" + DIRTY + ");",
|
||||||
|
@ -339,7 +340,9 @@ public class RecipientDatabase extends Database {
|
||||||
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
|
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
|
||||||
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
|
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
|
||||||
REGISTERED + " INTEGER DEFAULT " + RegisteredState.UNKNOWN.getId() + ", " +
|
REGISTERED + " INTEGER DEFAULT " + RegisteredState.UNKNOWN.getId() + ", " +
|
||||||
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
|
SYSTEM_GIVEN_NAME + " TEXT DEFAULT NULL, " +
|
||||||
|
SYSTEM_FAMILY_NAME + " TEXT DEFAULT NULL, " +
|
||||||
|
SYSTEM_JOINED_NAME + " TEXT DEFAULT NULL, " +
|
||||||
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
|
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
|
||||||
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
|
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
|
||||||
SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " +
|
SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " +
|
||||||
|
@ -1264,7 +1267,8 @@ public class RecipientDatabase extends Database {
|
||||||
int registeredState = CursorUtil.requireInt(cursor, REGISTERED);
|
int registeredState = CursorUtil.requireInt(cursor, REGISTERED);
|
||||||
String profileKeyString = CursorUtil.requireString(cursor, PROFILE_KEY);
|
String profileKeyString = CursorUtil.requireString(cursor, PROFILE_KEY);
|
||||||
String profileKeyCredentialString = CursorUtil.requireString(cursor, PROFILE_KEY_CREDENTIAL);
|
String profileKeyCredentialString = CursorUtil.requireString(cursor, PROFILE_KEY_CREDENTIAL);
|
||||||
String systemDisplayName = CursorUtil.requireString(cursor, SYSTEM_DISPLAY_NAME);
|
String systemGivenName = CursorUtil.requireString(cursor, SYSTEM_GIVEN_NAME);
|
||||||
|
String systemFamilyName = CursorUtil.requireString(cursor, SYSTEM_FAMILY_NAME);
|
||||||
String systemContactPhoto = CursorUtil.requireString(cursor, SYSTEM_PHOTO_URI);
|
String systemContactPhoto = CursorUtil.requireString(cursor, SYSTEM_PHOTO_URI);
|
||||||
String systemPhoneLabel = CursorUtil.requireString(cursor, SYSTEM_PHONE_LABEL);
|
String systemPhoneLabel = CursorUtil.requireString(cursor, SYSTEM_PHONE_LABEL);
|
||||||
String systemContactUri = CursorUtil.requireString(cursor, SYSTEM_CONTACT_URI);
|
String systemContactUri = CursorUtil.requireString(cursor, SYSTEM_CONTACT_URI);
|
||||||
|
@ -1350,7 +1354,7 @@ public class RecipientDatabase extends Database {
|
||||||
RegisteredState.fromId(registeredState),
|
RegisteredState.fromId(registeredState),
|
||||||
profileKey,
|
profileKey,
|
||||||
profileKeyCredential,
|
profileKeyCredential,
|
||||||
systemDisplayName,
|
ProfileName.fromParts(systemGivenName, systemFamilyName),
|
||||||
systemContactPhoto,
|
systemContactPhoto,
|
||||||
systemPhoneLabel,
|
systemPhoneLabel,
|
||||||
systemContactUri,
|
systemContactUri,
|
||||||
|
@ -1743,7 +1747,7 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
public @NonNull List<RecipientId> getSimilarRecipientIds(@NonNull Recipient recipient) {
|
public @NonNull List<RecipientId> getSimilarRecipientIds(@NonNull Recipient recipient) {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
String[] projection = SqlUtil.buildArgs(ID, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ") AS checked_name");
|
String[] projection = SqlUtil.buildArgs(ID, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ") AS checked_name");
|
||||||
String where = "checked_name = ?";
|
String where = "checked_name = ?";
|
||||||
|
|
||||||
String[] arguments = SqlUtil.buildArgs(recipient.getProfileName().toString());
|
String[] arguments = SqlUtil.buildArgs(recipient.getProfileName().toString());
|
||||||
|
@ -2246,7 +2250,7 @@ public class RecipientDatabase extends Database {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
List<RecipientId> results = new LinkedList<>();
|
List<RecipientId> results = new LinkedList<>();
|
||||||
|
|
||||||
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
|
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
|
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
|
||||||
}
|
}
|
||||||
|
@ -2260,10 +2264,10 @@ public class RecipientDatabase extends Database {
|
||||||
Map<RecipientId, MaterialColor> updates = new HashMap<>();
|
Map<RecipientId, MaterialColor> updates = new HashMap<>();
|
||||||
|
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_JOINED_NAME}, SYSTEM_JOINED_NAME + " IS NOT NULL AND " + SYSTEM_JOINED_NAME + " != \"\"", null, null, null, null)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)),
|
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_JOINED_NAME)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
|
cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
@ -2284,7 +2288,7 @@ public class RecipientDatabase extends Database {
|
||||||
String selection = BLOCKED + " = ? AND " +
|
String selection = BLOCKED + " = ? AND " +
|
||||||
REGISTERED + " = ? AND " +
|
REGISTERED + " = ? AND " +
|
||||||
GROUP_ID + " IS NULL AND " +
|
GROUP_ID + " IS NULL AND " +
|
||||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
||||||
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
|
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
|
||||||
String[] args;
|
String[] args;
|
||||||
|
|
||||||
|
@ -2295,7 +2299,7 @@ public class RecipientDatabase extends Database {
|
||||||
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", Recipient.self().getId().serialize() };
|
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", Recipient.self().getId().serialize() };
|
||||||
}
|
}
|
||||||
|
|
||||||
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
|
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
|
||||||
|
|
||||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
@ -2306,7 +2310,7 @@ public class RecipientDatabase extends Database {
|
||||||
String selection = BLOCKED + " = ? AND " +
|
String selection = BLOCKED + " = ? AND " +
|
||||||
REGISTERED + " = ? AND " +
|
REGISTERED + " = ? AND " +
|
||||||
GROUP_ID + " IS NULL AND " +
|
GROUP_ID + " IS NULL AND " +
|
||||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
||||||
"(" +
|
"(" +
|
||||||
PHONE + " GLOB ? OR " +
|
PHONE + " GLOB ? OR " +
|
||||||
SORT_NAME + " GLOB ? OR " +
|
SORT_NAME + " GLOB ? OR " +
|
||||||
|
@ -2321,7 +2325,7 @@ public class RecipientDatabase extends Database {
|
||||||
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query, String.valueOf(Recipient.self().getId().toLong()) };
|
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query, String.valueOf(Recipient.self().getId().toLong()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE;
|
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE;
|
||||||
|
|
||||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
@ -2330,10 +2334,10 @@ public class RecipientDatabase extends Database {
|
||||||
String selection = BLOCKED + " = ? AND " +
|
String selection = BLOCKED + " = ? AND " +
|
||||||
REGISTERED + " != ? AND " +
|
REGISTERED + " != ? AND " +
|
||||||
GROUP_ID + " IS NULL AND " +
|
GROUP_ID + " IS NULL AND " +
|
||||||
SYSTEM_DISPLAY_NAME + " NOT NULL AND " +
|
SYSTEM_CONTACT_URI + " NOT NULL AND " +
|
||||||
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
|
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
|
||||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) };
|
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) };
|
||||||
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE;
|
String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
|
||||||
|
|
||||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
@ -2344,15 +2348,15 @@ public class RecipientDatabase extends Database {
|
||||||
String selection = BLOCKED + " = ? AND " +
|
String selection = BLOCKED + " = ? AND " +
|
||||||
REGISTERED + " != ? AND " +
|
REGISTERED + " != ? AND " +
|
||||||
GROUP_ID + " IS NULL AND " +
|
GROUP_ID + " IS NULL AND " +
|
||||||
SYSTEM_DISPLAY_NAME + " NOT NULL AND " +
|
SYSTEM_CONTACT_URI + " NOT NULL AND " +
|
||||||
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " +
|
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " +
|
||||||
"(" +
|
"(" +
|
||||||
PHONE + " GLOB ? OR " +
|
PHONE + " GLOB ? OR " +
|
||||||
EMAIL + " GLOB ? OR " +
|
EMAIL + " GLOB ? OR " +
|
||||||
SYSTEM_DISPLAY_NAME + " GLOB ?" +
|
SYSTEM_JOINED_NAME + " GLOB ?" +
|
||||||
")";
|
")";
|
||||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query, query };
|
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query, query };
|
||||||
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE;
|
String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
|
||||||
|
|
||||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
@ -2429,7 +2433,7 @@ public class RecipientDatabase extends Database {
|
||||||
String selection = REGISTERED + " = ? AND " +
|
String selection = REGISTERED + " = ? AND " +
|
||||||
GROUP_ID + " IS NULL AND " +
|
GROUP_ID + " IS NULL AND " +
|
||||||
ID + " != ? AND " +
|
ID + " != ? AND " +
|
||||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + ID + " IN (" + subquery + "))";
|
"(" + SYSTEM_CONTACT_URI + " NOT NULL OR " + ID + " IN (" + subquery + "))";
|
||||||
String[] args = new String[] { String.valueOf(RegisteredState.REGISTERED.getId()), Recipient.self().getId().serialize() };
|
String[] args = new String[] { String.valueOf(RegisteredState.REGISTERED.getId()), Recipient.self().getId().serialize() };
|
||||||
|
|
||||||
List<Recipient> recipients = new ArrayList<>();
|
List<Recipient> recipients = new ArrayList<>();
|
||||||
|
@ -2729,7 +2733,9 @@ public class RecipientDatabase extends Database {
|
||||||
uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1));
|
uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1));
|
||||||
uuidValues.put(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages());
|
uuidValues.put(MESSAGE_EXPIRATION_TIME, uuidSettings.getExpireMessages() > 0 ? uuidSettings.getExpireMessages() : e164Settings.getExpireMessages());
|
||||||
uuidValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
|
uuidValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
|
||||||
uuidValues.put(SYSTEM_DISPLAY_NAME, e164Settings.getSystemDisplayName());
|
uuidValues.put(SYSTEM_GIVEN_NAME, e164Settings.getSystemProfileName().getGivenName());
|
||||||
|
uuidValues.put(SYSTEM_FAMILY_NAME, e164Settings.getSystemProfileName().getFamilyName());
|
||||||
|
uuidValues.put(SYSTEM_JOINED_NAME, e164Settings.getSystemProfileName().toString());
|
||||||
uuidValues.put(SYSTEM_PHOTO_URI, e164Settings.getSystemContactPhotoUri());
|
uuidValues.put(SYSTEM_PHOTO_URI, e164Settings.getSystemContactPhotoUri());
|
||||||
uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
|
uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
|
||||||
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
|
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
|
||||||
|
@ -2840,14 +2846,16 @@ public class RecipientDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystemContactInfo(@NonNull RecipientId id,
|
public void setSystemContactInfo(@NonNull RecipientId id,
|
||||||
@Nullable String displayName,
|
@NonNull ProfileName systemProfileName,
|
||||||
@Nullable String photoUri,
|
@Nullable String photoUri,
|
||||||
@Nullable String systemPhoneLabel,
|
@Nullable String systemPhoneLabel,
|
||||||
int systemPhoneType,
|
int systemPhoneType,
|
||||||
@Nullable String systemContactUri)
|
@Nullable String systemContactUri)
|
||||||
{
|
{
|
||||||
ContentValues dirtyQualifyingValues = new ContentValues();
|
ContentValues dirtyQualifyingValues = new ContentValues();
|
||||||
dirtyQualifyingValues.put(SYSTEM_DISPLAY_NAME, displayName);
|
dirtyQualifyingValues.put(SYSTEM_GIVEN_NAME, systemProfileName.getGivenName());
|
||||||
|
dirtyQualifyingValues.put(SYSTEM_FAMILY_NAME, systemProfileName.getFamilyName());
|
||||||
|
dirtyQualifyingValues.put(SYSTEM_JOINED_NAME, systemProfileName.toString());
|
||||||
|
|
||||||
if (update(id, dirtyQualifyingValues)) {
|
if (update(id, dirtyQualifyingValues)) {
|
||||||
markDirty(id, DirtyState.UPDATE);
|
markDirty(id, DirtyState.UPDATE);
|
||||||
|
@ -2859,11 +2867,12 @@ public class RecipientDatabase extends Database {
|
||||||
refreshQualifyingValues.put(SYSTEM_PHONE_TYPE, systemPhoneType);
|
refreshQualifyingValues.put(SYSTEM_PHONE_TYPE, systemPhoneType);
|
||||||
refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri);
|
refreshQualifyingValues.put(SYSTEM_CONTACT_URI, systemContactUri);
|
||||||
|
|
||||||
|
String joinedName = systemProfileName.toString();
|
||||||
boolean updatedValues = update(id, refreshQualifyingValues);
|
boolean updatedValues = update(id, refreshQualifyingValues);
|
||||||
boolean updatedColor = displayName != null && setColorIfNotSetInternal(id, ContactColors.generateFor(displayName));
|
boolean updatedColor = !TextUtils.isEmpty(joinedName) && setColorIfNotSetInternal(id, ContactColors.generateFor(joinedName));
|
||||||
|
|
||||||
if (updatedValues || updatedColor) {
|
if (updatedValues || updatedColor) {
|
||||||
pendingContactInfoMap.put(id, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri));
|
pendingContactInfoMap.put(id, new PendingContactInfo(systemProfileName, photoUri, systemPhoneLabel, systemContactUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentValues otherValues = new ContentValues();
|
ContentValues otherValues = new ContentValues();
|
||||||
|
@ -2898,7 +2907,9 @@ public class RecipientDatabase extends Database {
|
||||||
ContentValues values = new ContentValues(5);
|
ContentValues values = new ContentValues(5);
|
||||||
|
|
||||||
values.put(SYSTEM_INFO_PENDING, 0);
|
values.put(SYSTEM_INFO_PENDING, 0);
|
||||||
values.put(SYSTEM_DISPLAY_NAME, (String) null);
|
values.put(SYSTEM_GIVEN_NAME, (String) null);
|
||||||
|
values.put(SYSTEM_FAMILY_NAME, (String) null);
|
||||||
|
values.put(SYSTEM_JOINED_NAME, (String) null);
|
||||||
values.put(SYSTEM_PHOTO_URI, (String) null);
|
values.put(SYSTEM_PHOTO_URI, (String) null);
|
||||||
values.put(SYSTEM_PHONE_LABEL, (String) null);
|
values.put(SYSTEM_PHONE_LABEL, (String) null);
|
||||||
values.put(SYSTEM_CONTACT_URI, (String) null);
|
values.put(SYSTEM_CONTACT_URI, (String) null);
|
||||||
|
@ -2939,7 +2950,7 @@ public class RecipientDatabase extends Database {
|
||||||
private final RegisteredState registered;
|
private final RegisteredState registered;
|
||||||
private final byte[] profileKey;
|
private final byte[] profileKey;
|
||||||
private final ProfileKeyCredential profileKeyCredential;
|
private final ProfileKeyCredential profileKeyCredential;
|
||||||
private final String systemDisplayName;
|
private final ProfileName systemProfileName;
|
||||||
private final String systemContactPhoto;
|
private final String systemContactPhoto;
|
||||||
private final String systemPhoneLabel;
|
private final String systemPhoneLabel;
|
||||||
private final String systemContactUri;
|
private final String systemContactUri;
|
||||||
|
@ -2981,7 +2992,7 @@ public class RecipientDatabase extends Database {
|
||||||
@NonNull RegisteredState registered,
|
@NonNull RegisteredState registered,
|
||||||
@Nullable byte[] profileKey,
|
@Nullable byte[] profileKey,
|
||||||
@Nullable ProfileKeyCredential profileKeyCredential,
|
@Nullable ProfileKeyCredential profileKeyCredential,
|
||||||
@Nullable String systemDisplayName,
|
@NonNull ProfileName systemProfileName,
|
||||||
@Nullable String systemContactPhoto,
|
@Nullable String systemContactPhoto,
|
||||||
@Nullable String systemPhoneLabel,
|
@Nullable String systemPhoneLabel,
|
||||||
@Nullable String systemContactUri,
|
@Nullable String systemContactUri,
|
||||||
|
@ -3021,7 +3032,7 @@ public class RecipientDatabase extends Database {
|
||||||
this.registered = registered;
|
this.registered = registered;
|
||||||
this.profileKey = profileKey;
|
this.profileKey = profileKey;
|
||||||
this.profileKeyCredential = profileKeyCredential;
|
this.profileKeyCredential = profileKeyCredential;
|
||||||
this.systemDisplayName = systemDisplayName;
|
this.systemProfileName = systemProfileName;
|
||||||
this.systemContactPhoto = systemContactPhoto;
|
this.systemContactPhoto = systemContactPhoto;
|
||||||
this.systemPhoneLabel = systemPhoneLabel;
|
this.systemPhoneLabel = systemPhoneLabel;
|
||||||
this.systemContactUri = systemContactUri;
|
this.systemContactUri = systemContactUri;
|
||||||
|
@ -3125,8 +3136,8 @@ public class RecipientDatabase extends Database {
|
||||||
return profileKeyCredential;
|
return profileKeyCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getSystemDisplayName() {
|
public @NonNull ProfileName getSystemProfileName() {
|
||||||
return systemDisplayName;
|
return systemProfileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getSystemContactPhotoUri() {
|
public @Nullable String getSystemContactPhotoUri() {
|
||||||
|
@ -3313,13 +3324,13 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
private static class PendingContactInfo {
|
private static class PendingContactInfo {
|
||||||
|
|
||||||
private final String displayName;
|
private final ProfileName profileName;
|
||||||
private final String photoUri;
|
private final String photoUri;
|
||||||
private final String phoneLabel;
|
private final String phoneLabel;
|
||||||
private final String contactUri;
|
private final String contactUri;
|
||||||
|
|
||||||
private PendingContactInfo(String displayName, String photoUri, String phoneLabel, String contactUri) {
|
private PendingContactInfo(@NonNull ProfileName systemProfileName, String photoUri, String phoneLabel, String contactUri) {
|
||||||
this.displayName = displayName;
|
this.profileName = systemProfileName;
|
||||||
this.photoUri = photoUri;
|
this.photoUri = photoUri;
|
||||||
this.phoneLabel = phoneLabel;
|
this.phoneLabel = phoneLabel;
|
||||||
this.contactUri = contactUri;
|
this.contactUri = contactUri;
|
||||||
|
|
|
@ -171,8 +171,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
private static final int LAST_RESET_SESSION_TIME = 87;
|
private static final int LAST_RESET_SESSION_TIME = 87;
|
||||||
private static final int WALLPAPER = 88;
|
private static final int WALLPAPER = 88;
|
||||||
private static final int ABOUT = 89;
|
private static final int ABOUT = 89;
|
||||||
|
private static final int SPLIT_SYSTEM_NAMES = 90;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 89;
|
private static final int DATABASE_VERSION = 90;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -1258,6 +1259,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
db.execSQL("ALTER TABLE recipient ADD COLUMN about_emoji TEXT DEFAULT NULL");
|
db.execSQL("ALTER TABLE recipient ADD COLUMN about_emoji TEXT DEFAULT NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < SPLIT_SYSTEM_NAMES) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN system_family_name TEXT DEFAULT NULL");
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN system_given_name TEXT DEFAULT NULL");
|
||||||
|
db.execSQL("UPDATE recipient SET system_given_name = system_display_name");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository
|
||||||
public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) {
|
public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) {
|
||||||
SimpleTask.run(() -> {
|
SimpleTask.run(() -> {
|
||||||
Recipient self = Recipient.self().resolve();
|
Recipient self = Recipient.self().resolve();
|
||||||
String name = Optional.fromNullable(self.getName(context)).or("");
|
String name = Optional.fromNullable(self.getDisplayName(context)).or("");
|
||||||
MaterialColor fallbackColor = self.getColor();
|
MaterialColor fallbackColor = self.getColor();
|
||||||
|
|
||||||
if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) {
|
if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) {
|
||||||
|
|
|
@ -136,7 +136,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
|
||||||
Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients();
|
Set<RecipientId> archived = DatabaseFactory.getThreadDatabase(context).getArchivedRecipients();
|
||||||
|
|
||||||
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient),
|
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient),
|
||||||
Optional.fromNullable(recipient.getName(context)),
|
Optional.fromNullable(recipient.isGroup() || recipient.isSystemContact() ? recipient.getDisplayName(context) : null),
|
||||||
getAvatar(recipient.getId(), recipient.getContactUri()),
|
getAvatar(recipient.getId(), recipient.getContactUri()),
|
||||||
Optional.fromNullable(recipient.getColor().serialize()),
|
Optional.fromNullable(recipient.getColor().serialize()),
|
||||||
verifiedMessage,
|
verifiedMessage,
|
||||||
|
@ -191,7 +191,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients) {
|
||||||
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
|
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
|
||||||
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
|
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
|
||||||
Optional<String> name = Optional.fromNullable(recipient.getName(context));
|
Optional<String> name = Optional.fromNullable(recipient.isSystemContact() ? recipient.getDisplayName(context) : recipient.getGroupName(context));
|
||||||
Optional<String> color = Optional.of(recipient.getColor().serialize());
|
Optional<String> color = Optional.of(recipient.getColor().serialize());
|
||||||
Optional<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey());
|
Optional<ProfileKey> profileKey = ProfileKeyUtil.profileKeyOptional(recipient.getProfileKey());
|
||||||
boolean blocked = recipient.isBlocked();
|
boolean blocked = recipient.isBlocked();
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class ApplicationMigrations {
|
||||||
static final int USER_NOTIFICATION = 25;
|
static final int USER_NOTIFICATION = 25;
|
||||||
static final int DAY_BY_DAY_STICKERS = 26;
|
static final int DAY_BY_DAY_STICKERS = 26;
|
||||||
static final int BLOB_LOCATION = 27;
|
static final int BLOB_LOCATION = 27;
|
||||||
|
static final int SYSTEM_NAME_SPLIT = 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -296,6 +297,10 @@ public class ApplicationMigrations {
|
||||||
jobs.put(Version.BLOB_LOCATION, new BlobStorageLocationMigrationJob());
|
jobs.put(Version.BLOB_LOCATION, new BlobStorageLocationMigrationJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSeenVersion < Version.SYSTEM_NAME_SPLIT) {
|
||||||
|
jobs.put(Version.SYSTEM_NAME_SPLIT, new DirectoryRefreshMigrationJob());
|
||||||
|
}
|
||||||
|
|
||||||
return jobs;
|
return jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,13 @@ public final class ProfileName implements Parcelable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a profile name that only contains a given name.
|
||||||
|
*/
|
||||||
|
public static @NonNull ProfileName asGiven(@Nullable String givenName) {
|
||||||
|
return fromParts(givenName, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a profile name, trimming chars until it fits the limits.
|
* Creates a profile name, trimming chars until it fits the limits.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -74,7 +74,7 @@ class EditGroupProfileRepository implements EditProfileRepository {
|
||||||
String title = groupRecord.getTitle();
|
String title = groupRecord.getTitle();
|
||||||
return title == null ? "" : title;
|
return title == null ? "" : title;
|
||||||
})
|
})
|
||||||
.or(() -> recipient.getName(context));
|
.or(() -> recipient.getGroupName(context));
|
||||||
}, nameConsumer::accept);
|
}, nameConsumer::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,11 +89,11 @@ public class Recipient {
|
||||||
private final RegisteredState registered;
|
private final RegisteredState registered;
|
||||||
private final byte[] profileKey;
|
private final byte[] profileKey;
|
||||||
private final ProfileKeyCredential profileKeyCredential;
|
private final ProfileKeyCredential profileKeyCredential;
|
||||||
private final String name;
|
private final String groupName;
|
||||||
private final Uri systemContactPhoto;
|
private final Uri systemContactPhoto;
|
||||||
private final String customLabel;
|
private final String customLabel;
|
||||||
private final Uri contactUri;
|
private final Uri contactUri;
|
||||||
private final ProfileName profileName;
|
private final ProfileName signalProfileName;
|
||||||
private final String profileAvatar;
|
private final String profileAvatar;
|
||||||
private final boolean hasProfileImage;
|
private final boolean hasProfileImage;
|
||||||
private final boolean profileSharing;
|
private final boolean profileSharing;
|
||||||
|
@ -109,6 +109,7 @@ public class Recipient {
|
||||||
private final ChatWallpaper wallpaper;
|
private final ChatWallpaper wallpaper;
|
||||||
private final String about;
|
private final String about;
|
||||||
private final String aboutEmoji;
|
private final String aboutEmoji;
|
||||||
|
private final ProfileName systemProfileName;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,11 +327,11 @@ public class Recipient {
|
||||||
this.registered = RegisteredState.UNKNOWN;
|
this.registered = RegisteredState.UNKNOWN;
|
||||||
this.profileKey = null;
|
this.profileKey = null;
|
||||||
this.profileKeyCredential = null;
|
this.profileKeyCredential = null;
|
||||||
this.name = null;
|
this.groupName = null;
|
||||||
this.systemContactPhoto = null;
|
this.systemContactPhoto = null;
|
||||||
this.customLabel = null;
|
this.customLabel = null;
|
||||||
this.contactUri = null;
|
this.contactUri = null;
|
||||||
this.profileName = ProfileName.EMPTY;
|
this.signalProfileName = ProfileName.EMPTY;
|
||||||
this.profileAvatar = null;
|
this.profileAvatar = null;
|
||||||
this.hasProfileImage = false;
|
this.hasProfileImage = false;
|
||||||
this.profileSharing = false;
|
this.profileSharing = false;
|
||||||
|
@ -345,6 +346,7 @@ public class Recipient {
|
||||||
this.wallpaper = null;
|
this.wallpaper = null;
|
||||||
this.about = null;
|
this.about = null;
|
||||||
this.aboutEmoji = null;
|
this.aboutEmoji = null;
|
||||||
|
this.systemProfileName = ProfileName.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
||||||
|
@ -371,11 +373,11 @@ public class Recipient {
|
||||||
this.registered = details.registered;
|
this.registered = details.registered;
|
||||||
this.profileKey = details.profileKey;
|
this.profileKey = details.profileKey;
|
||||||
this.profileKeyCredential = details.profileKeyCredential;
|
this.profileKeyCredential = details.profileKeyCredential;
|
||||||
this.name = details.name;
|
this.groupName = details.groupName;
|
||||||
this.systemContactPhoto = details.systemContactPhoto;
|
this.systemContactPhoto = details.systemContactPhoto;
|
||||||
this.customLabel = details.customLabel;
|
this.customLabel = details.customLabel;
|
||||||
this.contactUri = details.contactUri;
|
this.contactUri = details.contactUri;
|
||||||
this.profileName = details.profileName;
|
this.signalProfileName = details.profileName;
|
||||||
this.profileAvatar = details.profileAvatar;
|
this.profileAvatar = details.profileAvatar;
|
||||||
this.hasProfileImage = details.hasProfileImage;
|
this.hasProfileImage = details.hasProfileImage;
|
||||||
this.profileSharing = details.profileSharing;
|
this.profileSharing = details.profileSharing;
|
||||||
|
@ -390,6 +392,7 @@ public class Recipient {
|
||||||
this.wallpaper = details.wallpaper;
|
this.wallpaper = details.wallpaper;
|
||||||
this.about = details.about;
|
this.about = details.about;
|
||||||
this.aboutEmoji = details.aboutEmoji;
|
this.aboutEmoji = details.aboutEmoji;
|
||||||
|
this.systemProfileName = details.systemProfileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull RecipientId getId() {
|
public @NonNull RecipientId getId() {
|
||||||
|
@ -404,8 +407,8 @@ public class Recipient {
|
||||||
return contactUri;
|
return contactUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getName(@NonNull Context context) {
|
public @Nullable String getGroupName(@NonNull Context context) {
|
||||||
if (this.name == null && groupId != null && groupId.isMms()) {
|
if (this.groupName == null && groupId != null && groupId.isMms()) {
|
||||||
List<String> names = new LinkedList<>();
|
List<String> names = new LinkedList<>();
|
||||||
|
|
||||||
for (Recipient recipient : participants) {
|
for (Recipient recipient : participants) {
|
||||||
|
@ -413,27 +416,32 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Util.join(names, ", ");
|
return Util.join(names, ", ");
|
||||||
} else if (name == null && groupId != null && groupId.isPush()) {
|
} else if (groupName == null && groupId != null && groupId.isPush()) {
|
||||||
return context.getString(R.string.RecipientProvider_unnamed_group);
|
return context.getString(R.string.RecipientProvider_unnamed_group);
|
||||||
} else {
|
} else {
|
||||||
return this.name;
|
return this.groupName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasName() {
|
public boolean hasName() {
|
||||||
return name != null;
|
return groupName != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* False iff it {@link #getDisplayName} would fall back to e164, email or unknown.
|
* False iff it {@link #getDisplayName} would fall back to e164, email or unknown.
|
||||||
*/
|
*/
|
||||||
public boolean hasAUserSetDisplayName(@NonNull Context context) {
|
public boolean hasAUserSetDisplayName(@NonNull Context context) {
|
||||||
return !TextUtils.isEmpty(getName(context)) ||
|
return !TextUtils.isEmpty(getGroupName(context)) ||
|
||||||
|
!TextUtils.isEmpty(getSystemProfileName().toString()) ||
|
||||||
!TextUtils.isEmpty(getProfileName().toString());
|
!TextUtils.isEmpty(getProfileName().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getDisplayName(@NonNull Context context) {
|
public @NonNull String getDisplayName(@NonNull Context context) {
|
||||||
String name = getName(context);
|
String name = getGroupName(context);
|
||||||
|
|
||||||
|
if (Util.isEmpty(name)) {
|
||||||
|
name = getSystemProfileName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (Util.isEmpty(name)) {
|
if (Util.isEmpty(name)) {
|
||||||
name = getProfileName().toString();
|
name = getProfileName().toString();
|
||||||
|
@ -455,7 +463,11 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getDisplayNameOrUsername(@NonNull Context context) {
|
public @NonNull String getDisplayNameOrUsername(@NonNull Context context) {
|
||||||
String name = getName(context);
|
String name = getGroupName(context);
|
||||||
|
|
||||||
|
if (Util.isEmpty(name)) {
|
||||||
|
name = getSystemProfileName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (Util.isEmpty(name)) {
|
if (Util.isEmpty(name)) {
|
||||||
name = StringUtil.isolateBidi(getProfileName().toString());
|
name = StringUtil.isolateBidi(getProfileName().toString());
|
||||||
|
@ -481,11 +493,16 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getMentionDisplayName(@NonNull Context context) {
|
public @NonNull String getMentionDisplayName(@NonNull Context context) {
|
||||||
String name = isSelf ? getProfileName().toString() : getName(context);
|
String name = isSelf ? getProfileName().toString() : getGroupName(context);
|
||||||
name = StringUtil.isolateBidi(name);
|
name = StringUtil.isolateBidi(name);
|
||||||
|
|
||||||
if (Util.isEmpty(name)) {
|
if (Util.isEmpty(name)) {
|
||||||
name = isSelf ? getName(context) : getProfileName().toString();
|
name = isSelf ? getGroupName(context) : getSystemProfileName().toString();
|
||||||
|
name = StringUtil.isolateBidi(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.isEmpty(name)) {
|
||||||
|
name = isSelf ? getGroupName(context) : getProfileName().toString();
|
||||||
name = StringUtil.isolateBidi(name);
|
name = StringUtil.isolateBidi(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,7 +522,8 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getShortDisplayName(@NonNull Context context) {
|
public @NonNull String getShortDisplayName(@NonNull Context context) {
|
||||||
String name = Util.getFirstNonEmpty(getName(context),
|
String name = Util.getFirstNonEmpty(getGroupName(context),
|
||||||
|
getSystemProfileName().getGivenName(),
|
||||||
getProfileName().getGivenName(),
|
getProfileName().getGivenName(),
|
||||||
getDisplayName(context));
|
getDisplayName(context));
|
||||||
|
|
||||||
|
@ -513,7 +531,8 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getShortDisplayNameIncludingUsername(@NonNull Context context) {
|
public @NonNull String getShortDisplayNameIncludingUsername(@NonNull Context context) {
|
||||||
String name = Util.getFirstNonEmpty(getName(context),
|
String name = Util.getFirstNonEmpty(getGroupName(context),
|
||||||
|
getSystemProfileName().getGivenName(),
|
||||||
getProfileName().getGivenName(),
|
getProfileName().getGivenName(),
|
||||||
getDisplayName(context),
|
getDisplayName(context),
|
||||||
getUsername().orNull());
|
getUsername().orNull());
|
||||||
|
@ -526,7 +545,7 @@ public class Recipient {
|
||||||
return MaterialColor.GROUP;
|
return MaterialColor.GROUP;
|
||||||
} else if (color != null) {
|
} else if (color != null) {
|
||||||
return color;
|
return color;
|
||||||
} else if (name != null || profileSharing) {
|
} else if (groupName != null || profileSharing) {
|
||||||
Log.w(TAG, "Had no color for " + id + "! Saving a new one.");
|
Log.w(TAG, "Had no color for " + id + "! Saving a new one.");
|
||||||
|
|
||||||
Context context = ApplicationDependencies.getApplication();
|
Context context = ApplicationDependencies.getApplication();
|
||||||
|
@ -672,7 +691,11 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull ProfileName getProfileName() {
|
public @NonNull ProfileName getProfileName() {
|
||||||
return profileName;
|
return signalProfileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull ProfileName getSystemProfileName() {
|
||||||
|
return systemProfileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getProfileAvatar() {
|
public @Nullable String getProfileAvatar() {
|
||||||
|
@ -748,7 +771,7 @@ public class Recipient {
|
||||||
else if (isResolving()) return fallbackPhotoProvider.getPhotoForResolvingRecipient();
|
else if (isResolving()) return fallbackPhotoProvider.getPhotoForResolvingRecipient();
|
||||||
else if (isGroupInternal()) return fallbackPhotoProvider.getPhotoForGroup();
|
else if (isGroupInternal()) return fallbackPhotoProvider.getPhotoForGroup();
|
||||||
else if (isGroup()) return fallbackPhotoProvider.getPhotoForGroup();
|
else if (isGroup()) return fallbackPhotoProvider.getPhotoForGroup();
|
||||||
else if (!TextUtils.isEmpty(name)) return fallbackPhotoProvider.getPhotoForRecipientWithName(name);
|
else if (!TextUtils.isEmpty(groupName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(groupName);
|
||||||
else return fallbackPhotoProvider.getPhotoForRecipientWithoutName();
|
else return fallbackPhotoProvider.getPhotoForRecipientWithoutName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,11 +1026,12 @@ public class Recipient {
|
||||||
registered == other.registered &&
|
registered == other.registered &&
|
||||||
Arrays.equals(profileKey, other.profileKey) &&
|
Arrays.equals(profileKey, other.profileKey) &&
|
||||||
Objects.equals(profileKeyCredential, other.profileKeyCredential) &&
|
Objects.equals(profileKeyCredential, other.profileKeyCredential) &&
|
||||||
Objects.equals(name, other.name) &&
|
Objects.equals(groupName, other.groupName) &&
|
||||||
Objects.equals(systemContactPhoto, other.systemContactPhoto) &&
|
Objects.equals(systemContactPhoto, other.systemContactPhoto) &&
|
||||||
Objects.equals(customLabel, other.customLabel) &&
|
Objects.equals(customLabel, other.customLabel) &&
|
||||||
Objects.equals(contactUri, other.contactUri) &&
|
Objects.equals(contactUri, other.contactUri) &&
|
||||||
Objects.equals(profileName, other.profileName) &&
|
Objects.equals(signalProfileName, other.signalProfileName) &&
|
||||||
|
Objects.equals(systemProfileName, other.systemProfileName) &&
|
||||||
Objects.equals(profileAvatar, other.profileAvatar) &&
|
Objects.equals(profileAvatar, other.profileAvatar) &&
|
||||||
Objects.equals(notificationChannel, other.notificationChannel) &&
|
Objects.equals(notificationChannel, other.notificationChannel) &&
|
||||||
unidentifiedAccessMode == other.unidentifiedAccessMode &&
|
unidentifiedAccessMode == other.unidentifiedAccessMode &&
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -33,7 +32,7 @@ public class RecipientDetails {
|
||||||
final String e164;
|
final String e164;
|
||||||
final String email;
|
final String email;
|
||||||
final GroupId groupId;
|
final GroupId groupId;
|
||||||
final String name;
|
final String groupName;
|
||||||
final String customLabel;
|
final String customLabel;
|
||||||
final Uri systemContactPhoto;
|
final Uri systemContactPhoto;
|
||||||
final Uri contactUri;
|
final Uri contactUri;
|
||||||
|
@ -69,8 +68,9 @@ public class RecipientDetails {
|
||||||
final ChatWallpaper wallpaper;
|
final ChatWallpaper wallpaper;
|
||||||
final String about;
|
final String about;
|
||||||
final String aboutEmoji;
|
final String aboutEmoji;
|
||||||
|
final ProfileName systemProfileName;
|
||||||
|
|
||||||
public RecipientDetails(@Nullable String name,
|
public RecipientDetails(@Nullable String groupName,
|
||||||
@NonNull Optional<Long> groupAvatarId,
|
@NonNull Optional<Long> groupAvatarId,
|
||||||
boolean systemContact,
|
boolean systemContact,
|
||||||
boolean isSelf,
|
boolean isSelf,
|
||||||
|
@ -117,9 +117,8 @@ public class RecipientDetails {
|
||||||
this.wallpaper = settings.getWallpaper();
|
this.wallpaper = settings.getWallpaper();
|
||||||
this.about = settings.getAbout();
|
this.about = settings.getAbout();
|
||||||
this.aboutEmoji = settings.getAboutEmoji();
|
this.aboutEmoji = settings.getAboutEmoji();
|
||||||
|
this.systemProfileName = settings.getSystemProfileName();
|
||||||
if (name == null) this.name = settings.getSystemDisplayName();
|
this.groupName = groupName;
|
||||||
else this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,7 +158,7 @@ public class RecipientDetails {
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
||||||
this.forceSmsSelection = false;
|
this.forceSmsSelection = false;
|
||||||
this.name = null;
|
this.groupName = null;
|
||||||
this.groupsV2Capability = Recipient.Capability.UNKNOWN;
|
this.groupsV2Capability = Recipient.Capability.UNKNOWN;
|
||||||
this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN;
|
this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
|
@ -167,10 +166,11 @@ public class RecipientDetails {
|
||||||
this.wallpaper = null;
|
this.wallpaper = null;
|
||||||
this.about = null;
|
this.about = null;
|
||||||
this.aboutEmoji = null;
|
this.aboutEmoji = null;
|
||||||
|
this.systemProfileName = ProfileName.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
||||||
boolean systemContact = !TextUtils.isEmpty(settings.getSystemDisplayName());
|
boolean systemContact = !settings.getSystemProfileName().isEmpty();
|
||||||
boolean isSelf = (settings.getE164() != null && settings.getE164().equals(TextSecurePreferences.getLocalNumber(context))) ||
|
boolean isSelf = (settings.getE164() != null && settings.getE164().equals(TextSecurePreferences.getLocalNumber(context))) ||
|
||||||
(settings.getUuid() != null && settings.getUuid().equals(TextSecurePreferences.getLocalUuid(context)));
|
(settings.getUuid() != null && settings.getUuid().equals(TextSecurePreferences.getLocalUuid(context)));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue