Store system contacts display name in recipient preferences db

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-08-07 15:31:12 -07:00
parent f61c52aace
commit 159fdb317f
7 changed files with 89 additions and 44 deletions

View File

@ -78,6 +78,10 @@ public class ContactAccessor {
return results; return results;
} }
public Cursor getAllSystemContacts(Context context) {
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME}, null, null, null);
}
public boolean isSystemContact(Context context, String number) { public boolean isSystemContact(Context context, String number) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY, String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY,

View File

@ -44,9 +44,11 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
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.Map;
import java.util.Set;
/** /**
* Database to supply all types of contacts that TextSecure needs to know about * Database to supply all types of contacts that TextSecure needs to know about
@ -78,20 +80,17 @@ public class ContactsDatabase {
} }
public synchronized @NonNull List<Address> setRegisteredUsers(@NonNull Account account, public synchronized @NonNull List<Address> setRegisteredUsers(@NonNull Account account,
@NonNull List<ContactTokenDetails> registeredContacts, @NonNull List<Address> registeredAddressList,
boolean remove) boolean remove)
throws RemoteException, OperationApplicationException throws RemoteException, OperationApplicationException
{ {
Set<Address> registeredAddressSet = new HashSet<>();
Map<Address, ContactTokenDetails> registeredAddresses = new HashMap<>();
List<Address> addedAddresses = new LinkedList<>(); List<Address> addedAddresses = new LinkedList<>();
ArrayList<ContentProviderOperation> operations = new ArrayList<>(); ArrayList<ContentProviderOperation> operations = new ArrayList<>();
Map<Address, SignalContact> currentContacts = getSignalRawContacts(account); Map<Address, SignalContact> currentContacts = getSignalRawContacts(account);
for (ContactTokenDetails registeredContact : registeredContacts) { for (Address registeredAddress : registeredAddressList) {
Address registeredAddress = Address.fromSerialized(registeredContact.getNumber()); registeredAddressSet.add(registeredAddress);
registeredAddresses.put(registeredAddress, registeredContact);
if (!currentContacts.containsKey(registeredAddress)) { if (!currentContacts.containsKey(registeredAddress)) {
Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(registeredAddress); Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(registeredAddress);
@ -107,9 +106,7 @@ public class ContactsDatabase {
} }
for (Map.Entry<Address, SignalContact> currentContactEntry : currentContacts.entrySet()) { for (Map.Entry<Address, SignalContact> currentContactEntry : currentContacts.entrySet()) {
ContactTokenDetails tokenDetails = registeredAddresses.get(currentContactEntry.getKey()); if (!registeredAddressSet.contains(currentContactEntry.getKey())) {
if (tokenDetails == null) {
if (remove) { if (remove) {
Log.w(TAG, "Removing number: " + currentContactEntry.getKey()); Log.w(TAG, "Removing number: " + currentContactEntry.getKey());
removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId()); removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId());
@ -132,7 +129,7 @@ public class ContactsDatabase {
return addedAddresses; return addedAddresses;
} }
@NonNull Cursor querySystemContacts(String filter) { @NonNull Cursor querySystemContacts(@Nullable String filter) {
Uri uri; Uri uri;
if (!TextUtils.isEmpty(filter)) { if (!TextUtils.isEmpty(filter)) {

View File

@ -102,7 +102,8 @@ public class DatabaseFactory {
private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37;
private static final int NO_MORE_RECIPIENTS_PLURAL = 38; private static final int NO_MORE_RECIPIENTS_PLURAL = 38;
private static final int INTERNAL_DIRECTORY = 39; private static final int INTERNAL_DIRECTORY = 39;
private static final int DATABASE_VERSION = 39; private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40;
private static final int DATABASE_VERSION = 40;
private static final String DATABASE_NAME = "messages.db"; private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object(); private static final Object lock = new Object();
@ -1292,6 +1293,10 @@ public class DatabaseFactory {
} }
} }
if (oldVersion < INTERNAL_SYSTEM_DISPLAY_NAME) {
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
} }

View File

@ -40,9 +40,10 @@ public class RecipientPreferenceDatabase 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 EXPIRE_MESSAGES = "expire_messages"; private static final String EXPIRE_MESSAGES = "expire_messages";
private static final String REGISTERED = "registered"; private static final String REGISTERED = "registered";
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
private static final String[] RECIPIENT_PROJECTION = new String[] { private static final String[] RECIPIENT_PROJECTION = new String[] {
BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, SYSTEM_DISPLAY_NAME
}; };
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
@ -79,7 +80,8 @@ public class RecipientPreferenceDatabase extends Database {
SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " + SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " +
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " + EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " +
REGISTERED + " INTEGER DEFAULT 0);"; REGISTERED + " INTEGER DEFAULT 0, " +
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL);";
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) { public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
@ -128,6 +130,7 @@ public class RecipientPreferenceDatabase extends Database {
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID)); int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES)); int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
boolean registered = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)) == 1; boolean registered = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)) == 1;
String systemDisplayname = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
MaterialColor color; MaterialColor color;
@ -141,51 +144,52 @@ public class RecipientPreferenceDatabase extends Database {
return new RecipientsPreferences(blocked, muteUntil, return new RecipientsPreferences(blocked, muteUntil,
VibrateState.fromId(vibrateState), VibrateState.fromId(vibrateState),
notificationUri, color, seenInviteReminder, notificationUri, color, seenInviteReminder,
defaultSubscriptionId, expireMessages, registered); defaultSubscriptionId, expireMessages, registered,
systemDisplayname);
} }
public void setColor(Recipient recipient, MaterialColor color) { public void setColor(Recipient recipient, MaterialColor color) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(COLOR, color.serialize()); values.put(COLOR, color.serialize());
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) { public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId); values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
EventBus.getDefault().post(new RecipientPreferenceEvent(recipient)); EventBus.getDefault().post(new RecipientPreferenceEvent(recipient));
} }
public void setBlocked(Recipient recipient, boolean blocked) { public void setBlocked(Recipient recipient, boolean blocked) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(BLOCK, blocked ? 1 : 0); values.put(BLOCK, blocked ? 1 : 0);
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setRingtone(Recipient recipient, @Nullable Uri notification) { public void setRingtone(Recipient recipient, @Nullable Uri notification) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(NOTIFICATION, notification == null ? null : notification.toString()); values.put(NOTIFICATION, notification == null ? null : notification.toString());
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setVibrate(Recipient recipient, @NonNull VibrateState enabled) { public void setVibrate(Recipient recipient, @NonNull VibrateState enabled) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(VIBRATE, enabled.getId()); values.put(VIBRATE, enabled.getId());
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setMuted(Recipient recipient, long until) { public void setMuted(Recipient recipient, long until) {
Log.w(TAG, "Setting muted until: " + until); Log.w(TAG, "Setting muted until: " + until);
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(MUTE_UNTIL, until); values.put(MUTE_UNTIL, until);
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setSeenInviteReminder(Recipient recipient, boolean seen) { public void setSeenInviteReminder(Recipient recipient, boolean seen) {
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0); values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0);
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
} }
public void setExpireMessages(Recipient recipient, int expiration) { public void setExpireMessages(Recipient recipient, int expiration) {
@ -193,7 +197,13 @@ public class RecipientPreferenceDatabase extends Database {
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(EXPIRE_MESSAGES, expiration); values.put(EXPIRE_MESSAGES, expiration);
updateOrInsert(recipient, values); updateOrInsert(recipient.getAddress(), values);
}
public void setSystemDisplayName(@NonNull Address address, @Nullable String systemDisplayName) {
ContentValues values = new ContentValues(1);
values.put(SYSTEM_DISPLAY_NAME, systemDisplayName);
updateOrInsert(address, values);
} }
public Set<Address> getAllRecipients() { public Set<Address> getAllRecipients() {
@ -246,16 +256,16 @@ public class RecipientPreferenceDatabase extends Database {
return results; return results;
} }
private void updateOrInsert(Recipient recipient, ContentValues contentValues) { private void updateOrInsert(Address address, ContentValues contentValues) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction(); database.beginTransaction();
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?",
new String[] {recipient.getAddress().serialize()}); new String[] {address.serialize()});
if (updated < 1) { if (updated < 1) {
contentValues.put(ADDRESS, recipient.getAddress().serialize()); contentValues.put(ADDRESS, address.serialize());
database.insert(TABLE_NAME, null, contentValues); database.insert(TABLE_NAME, null, contentValues);
} }
@ -275,6 +285,7 @@ public class RecipientPreferenceDatabase extends Database {
private final int defaultSubscriptionId; private final int defaultSubscriptionId;
private final int expireMessages; private final int expireMessages;
private final boolean registered; private final boolean registered;
private final String systemDisplayName;
RecipientsPreferences(boolean blocked, long muteUntil, RecipientsPreferences(boolean blocked, long muteUntil,
@NonNull VibrateState vibrateState, @NonNull VibrateState vibrateState,
@ -283,7 +294,8 @@ public class RecipientPreferenceDatabase extends Database {
boolean seenInviteReminder, boolean seenInviteReminder,
int defaultSubscriptionId, int defaultSubscriptionId,
int expireMessages, int expireMessages,
boolean registered) boolean registered,
String systemDisplayName)
{ {
this.blocked = blocked; this.blocked = blocked;
this.muteUntil = muteUntil; this.muteUntil = muteUntil;
@ -294,6 +306,7 @@ public class RecipientPreferenceDatabase extends Database {
this.defaultSubscriptionId = defaultSubscriptionId; this.defaultSubscriptionId = defaultSubscriptionId;
this.expireMessages = expireMessages; this.expireMessages = expireMessages;
this.registered = registered; this.registered = registered;
this.systemDisplayName = systemDisplayName;
} }
public @Nullable MaterialColor getColor() { public @Nullable MaterialColor getColor() {
@ -331,6 +344,10 @@ public class RecipientPreferenceDatabase extends Database {
public boolean isRegistered() { public boolean isRegistered() {
return registered; return registered;
} }
public String getSystemDisplayName() {
return systemDisplayName;
}
} }
public static class BlockedReader { public static class BlockedReader {

View File

@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.recipients;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
@ -26,11 +27,13 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -66,6 +69,7 @@ public class Recipient implements RecipientModifiedListener {
Recipient(@NonNull Address address, Recipient(@NonNull Address address,
@Nullable Recipient stale, @Nullable Recipient stale,
@NonNull Optional<RecipientsPreferences> preferences,
@NonNull ListenableFutureTask<RecipientDetails> future) @NonNull ListenableFutureTask<RecipientDetails> future)
{ {
this.address = address; this.address = address;
@ -86,6 +90,19 @@ public class Recipient implements RecipientModifiedListener {
this.expireMessages = stale.expireMessages; this.expireMessages = stale.expireMessages;
} }
if (preferences.isPresent()) {
if (!TextUtils.isEmpty(preferences.get().getSystemDisplayName())) {
this.name = preferences.get().getSystemDisplayName();
}
this.color = preferences.get().getColor();
this.ringtone = preferences.get().getRingtone();
this.mutedUntil = preferences.get().getMuteUntil();
this.blocked = preferences.get().isBlocked();
this.vibrate = preferences.get().getVibrateState();
this.expireMessages = preferences.get().getExpireMessages();
}
future.addListener(new FutureTaskListener<RecipientDetails>() { future.addListener(new FutureTaskListener<RecipientDetails>() {
@Override @Override
public void onSuccess(RecipientDetails result) { public void onSuccess(RecipientDetails result) {

View File

@ -75,7 +75,7 @@ class RecipientProvider {
} }
if (asynchronous) { if (asynchronous) {
cachedRecipient = new Recipient(address, cachedRecipient, getRecipientDetailsAsync(context, address, preferences)); cachedRecipient = new Recipient(address, cachedRecipient, preferences, getRecipientDetailsAsync(context, address, preferences));
} else { } else {
cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address, preferences, false)); cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address, preferences, false));
} }

View File

@ -5,6 +5,7 @@ import android.accounts.AccountManager;
import android.content.ContentResolver; 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.os.RemoteException; import android.os.RemoteException;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -92,7 +93,7 @@ public class DirectoryHelper {
} }
recipientPreferenceDatabase.setRegistered(activeAddresses, new LinkedList<>(inactiveAddresses)); recipientPreferenceDatabase.setRegistered(activeAddresses, new LinkedList<>(inactiveAddresses));
return updateContactsDatabase(context, activeTokens, true); return updateContactsDatabase(context, activeAddresses, true);
} }
return new RefreshResult(new LinkedList<>(), false); return new RefreshResult(new LinkedList<>(), false);
@ -111,7 +112,7 @@ public class DirectoryHelper {
if (details.isPresent()) { if (details.isPresent()) {
recipientDatabase.setRegistered(Util.asList(recipient.getAddress()), new LinkedList<>()); recipientDatabase.setRegistered(Util.asList(recipient.getAddress()), new LinkedList<>());
RefreshResult result = updateContactsDatabase(context, details.get()); RefreshResult result = updateContactsDatabase(context, Util.asList(recipient.getAddress()), false);
if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) { if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) {
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context)); ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context));
@ -153,22 +154,26 @@ public class DirectoryHelper {
else return Capability.UNKNOWN; else return Capability.UNKNOWN;
} }
private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, @NonNull List<Address> activeAddresses, boolean removeMissing) {
@NonNull final ContactTokenDetails activeToken)
{
return updateContactsDatabase(context, new LinkedList<ContactTokenDetails>() {{add(activeToken);}}, false);
}
private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context,
@NonNull List<ContactTokenDetails> activeTokens,
boolean removeMissing)
{
Optional<AccountHolder> account = getOrCreateAccount(context); Optional<AccountHolder> account = getOrCreateAccount(context);
if (account.isPresent()) { if (account.isPresent()) {
try { try {
List<Address> newUsers = DatabaseFactory.getContactsDatabase(context) List<Address> newUsers = DatabaseFactory.getContactsDatabase(context)
.setRegisteredUsers(account.get().getAccount(), activeTokens, removeMissing); .setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
while (cursor != null && cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (!TextUtils.isEmpty(number)) {
Address address = Address.fromExternal(context, number);
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
DatabaseFactory.getRecipientPreferenceDatabase(context).setSystemDisplayName(address, displayName);
}
}
return new RefreshResult(newUsers, account.get().isFresh()); return new RefreshResult(newUsers, account.get().isFresh());
} catch (RemoteException | OperationApplicationException e) { } catch (RemoteException | OperationApplicationException e) {