Access all RecipientDatabase settings directly from Recipient

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-08-22 10:44:04 -07:00
parent d1790dfe17
commit f17af19d09
29 changed files with 504 additions and 390 deletions

View File

@ -108,8 +108,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientPreferenceEvent; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList; import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
@ -142,7 +141,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DirectoryHelper.Capability;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
@ -574,10 +572,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this) DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
.setExpireMessages(recipient, expirationTime);
recipient.setExpireMessages(expirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000); OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000);
MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null); MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null);
@ -639,8 +634,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.setPositiveButton(R.string.ConversationActivity_unblock, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.ConversationActivity_unblock, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
recipient.setBlocked(false);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
@ -1014,17 +1007,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected boolean[] doInBackground(Recipient... params) { protected boolean[] doInBackground(Recipient... params) {
Context context = ConversationActivity.this; Context context = ConversationActivity.this;
Recipient recipient = params[0]; Recipient recipient = params[0];
Capability capability = DirectoryHelper.getUserCapabilities(context, recipient); RegisteredState registeredState = recipient.resolve().getRegistered();
if (capability == Capability.UNKNOWN) { if (registeredState == RegisteredState.UNKNOWN) {
try { try {
capability = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient); registeredState = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
} }
return new boolean[] {capability == Capability.SUPPORTED, Util.isDefaultSmsProvider(context)}; return new boolean[] {registeredState == RegisteredState.REGISTERED, Util.isDefaultSmsProvider(context)};
} }
@Override @Override
@ -1041,11 +1034,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void onSecurityUpdated() { private void onSecurityUpdated() {
updateRecipientPreferences(); updateInviteReminder(recipient.hasSeenInviteReminder());
} updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
private void updateRecipientPreferences() {
new RecipientPreferencesTask().execute(recipient);
} }
protected void updateInviteReminder(boolean seenInvite) { protected void updateInviteReminder(boolean seenInvite) {
@ -1284,19 +1274,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
titleView.setVerified(identityRecords.isVerified()); titleView.setVerified(identityRecords.isVerified());
setBlockedUserState(recipient, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipient.getColor()); setActionBarColor(recipient.getColor());
updateInviteReminder(recipient.hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
initializeSecurity(isSecureText, isDefaultSms);
invalidateOptionsMenu(); invalidateOptionsMenu();
updateRecipientPreferences();
} }
}); });
} }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRecipientPreferenceUpdate(final RecipientPreferenceEvent event) {
if (event.getRecipient().getAddress().equals(this.recipient.getAddress())) {
new RecipientPreferencesTask().execute(this.recipient);
}
}
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onIdentityRecordUpdate(final IdentityRecord event) { public void onIdentityRecordUpdate(final IdentityRecord event) {
initializeIdentityRecords(); initializeIdentityRecords();
@ -1652,7 +1637,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Long doInBackground(OutgoingMediaMessage... messages) { protected Long doInBackground(OutgoingMediaMessage... messages) {
if (initiating) { if (initiating) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getAddress(), true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() { return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
@ -1692,7 +1677,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Long doInBackground(OutgoingTextMessage... messages) { protected Long doInBackground(OutgoingTextMessage... messages) {
if (initiatingConversation) { if (initiatingConversation) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getAddress(), true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() { return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
@ -1989,27 +1974,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateToggleButtonState(); updateToggleButtonState();
} }
private class RecipientPreferencesTask extends AsyncTask<Recipient, Void, Pair<Recipient,RecipientSettings>> {
@Override
protected Pair<Recipient, RecipientSettings> doInBackground(Recipient... recipient) {
if (recipient.length != 1 || recipient[0] == null) {
throw new AssertionError("task needs exactly one Recipients object");
}
Optional<RecipientSettings> prefs = DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.getRecipientSettings(recipient[0].getAddress());
return new Pair<>(recipient[0], prefs.orNull());
}
@Override
protected void onPostExecute(@NonNull Pair<Recipient, RecipientSettings> result) {
if (result.first == recipient) {
updateInviteReminder(result.second != null && result.second.hasSeenInviteReminder());
updateDefaultSubscriptionId(result.second != null ? result.second.getDefaultSubscriptionId() : Optional.<Integer>absent());
}
}
}
private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener { private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener {
@Override @Override
public void onDismissed(final List<IdentityRecord> unverifiedIdentities) { public void onDismissed(final List<IdentityRecord> unverifiedIdentities) {

View File

@ -18,7 +18,6 @@
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
@ -54,7 +53,7 @@ import org.thoughtcrime.securesms.database.Address;
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.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult; import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
@ -167,9 +166,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
} }
private static boolean isActiveInDirectory(Context context, Recipient recipient) { private static boolean isActiveInDirectory(Recipient recipient) {
Optional<RecipientSettings> preferences = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(recipient.getAddress()); return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
return preferences.isPresent() && preferences.get().isRegistered();
} }
private void addSelectedContacts(@NonNull Recipient... recipients) { private void addSelectedContacts(@NonNull Recipient... recipients) {
@ -495,7 +493,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
final List<Result> results = new LinkedList<>(); final List<Result> results = new LinkedList<>();
for (Recipient recipient : recipients) { for (Recipient recipient : recipients) {
boolean isPush = isActiveInDirectory(activity, recipient); boolean isPush = isActiveInDirectory(recipient);
if (failIfNotPush && !isPush) { if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group, results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,

View File

@ -231,8 +231,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
for (String number : numbers) { for (String number : numbers) {
Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false); Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false);
Optional<RecipientSettings> settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(recipient.getAddress()); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
int subscriptionId = settings.isPresent() ? settings.get().getDefaultSubscriptionId().or(-1) : -1;
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null); MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);

View File

@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
@ -43,7 +44,6 @@ import org.thoughtcrime.securesms.preferences.AdvancedRingtonePreference;
import org.thoughtcrime.securesms.preferences.ColorPreference; import org.thoughtcrime.securesms.preferences.ColorPreference;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
@ -350,8 +350,6 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
uri = Uri.parse(value); uri = Uri.parse(value);
} }
recipient.setRingtone(uri);
new AsyncTask<Uri, Void, Void>() { new AsyncTask<Uri, Void, Void>() {
@Override @Override
protected Void doInBackground(Uri... params) { protected Void doInBackground(Uri... params) {
@ -371,8 +369,6 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
int value = Integer.parseInt((String) newValue); int value = Integer.parseInt((String) newValue);
final VibrateState vibrateState = VibrateState.fromId(value); final VibrateState vibrateState = VibrateState.fromId(value);
recipient.setVibrate(vibrateState);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
@ -397,16 +393,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
if (selectedColor == null) return true; if (selectedColor == null) return true;
if (preference.isEnabled() && !currentColor.equals(selectedColor)) { if (preference.isEnabled() && !currentColor.equals(selectedColor)) {
recipient.setColor(selectedColor);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
Context context = getActivity(); Context context = getActivity();
DatabaseFactory.getRecipientDatabase(context) DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor);
.setColor(recipient, selectedColor);
if (DirectoryHelper.getUserCapabilities(context, recipient) == DirectoryHelper.Capability.SUPPORTED) { if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new MultiDeviceContactUpdateJob(context, recipient.getAddress())); .add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
@ -516,8 +509,6 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
private void setBlocked(final Recipient recipient, final boolean blocked) { private void setBlocked(final Recipient recipient, final boolean blocked) {
recipient.setBlocked(blocked);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {

View File

@ -29,8 +29,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
import java.util.ArrayList; import java.util.ArrayList;
@ -102,9 +102,9 @@ public class ContactsCursorLoader extends CursorLoader {
ContactsDatabase.CONTACT_TYPE_COLUMN}); ContactsDatabase.CONTACT_TYPE_COLUMN});
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
final Recipient recipient = Recipient.from(getContext(), Address.fromExternal(getContext(), number), true); final Recipient recipient = Recipient.from(getContext(), Address.fromExternal(getContext(), number), false);
if (DirectoryHelper.getUserCapabilities(getContext(), recipient) != DirectoryHelper.Capability.SUPPORTED) { if (recipient.resolve().getRegistered() != RecipientDatabase.RegisteredState.REGISTERED) {
matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)), matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)), cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)),
number, number,

View File

@ -32,4 +32,9 @@ public class BitmapContactPhoto implements ContactPhoto {
public Drawable asCallCard(Context context) { public Drawable asCallCard(Context context) {
return new BitmapDrawable(context.getResources(), bitmap); return new BitmapDrawable(context.getResources(), bitmap);
} }
@Override
public boolean isGenerated() {
return false;
}
} }

View File

@ -8,5 +8,6 @@ public interface ContactPhoto {
public Drawable asDrawable(Context context, int color); public Drawable asDrawable(Context context, int color);
public Drawable asDrawable(Context context, int color, boolean inverted); public Drawable asDrawable(Context context, int color, boolean inverted);
public Drawable asCallCard(Context context); public Drawable asCallCard(Context context);
public boolean isGenerated();
} }

View File

@ -50,4 +50,9 @@ public class GeneratedContactPhoto implements ContactPhoto {
public Drawable asCallCard(Context context) { public Drawable asCallCard(Context context) {
return ContextCompat.getDrawable(context, R.drawable.ic_contact_picture_large); return ContextCompat.getDrawable(context, R.drawable.ic_contact_picture_large);
} }
@Override
public boolean isGenerated() {
return true;
}
} }

View File

@ -45,6 +45,11 @@ public class ResourceContactPhoto implements ContactPhoto {
return context.getResources().getDrawable(resourceId); return context.getResources().getDrawable(resourceId);
} }
@Override
public boolean isGenerated() {
return false;
}
private static class ExpandingLayerDrawable extends LayerDrawable { private static class ExpandingLayerDrawable extends LayerDrawable {
public ExpandingLayerDrawable(Drawable[] layers) { public ExpandingLayerDrawable(Drawable[] layers) {
super(layers); super(layers);

View File

@ -26,4 +26,9 @@ public class TransparentContactPhoto implements ContactPhoto {
public Drawable asCallCard(Context context) { public Drawable asCallCard(Context context) {
return ContextCompat.getDrawable(context, R.drawable.ic_contact_picture_large); return ContextCompat.getDrawable(context, R.drawable.ic_contact_picture_large);
} }
@Override
public boolean isGenerated() {
return false;
}
} }

View File

@ -1290,7 +1290,7 @@ public class DatabaseFactory {
String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0)); String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0));
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put("registered", cursor.getInt(1) == 1); contentValues.put("registered", cursor.getInt(1) == 1 ? 1 : 2);
if (db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address}) < 1) { if (db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address}) < 1) {
contentValues.put("recipient_ids", address); contentValues.put("recipient_ids", address);

View File

@ -12,10 +12,11 @@ import android.util.Log;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
@ -74,6 +75,24 @@ public class RecipientDatabase extends Database {
} }
} }
public enum RegisteredState {
UNKNOWN(0), REGISTERED(1), NOT_REGISTERED(2);
private final int id;
RegisteredState(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static RegisteredState fromId(int id) {
return values()[id];
}
}
public static final String CREATE_TABLE = public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " + " (" + ID + " INTEGER PRIMARY KEY, " +
@ -120,7 +139,7 @@ public class RecipientDatabase extends Database {
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null); cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null);
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
return getRecipientPreferences(cursor); return getRecipientSettings(cursor);
} }
return Optional.absent(); return Optional.absent();
@ -129,7 +148,7 @@ public class RecipientDatabase extends Database {
} }
} }
Optional<RecipientSettings> getRecipientPreferences(@NonNull Cursor cursor) { Optional<RecipientSettings> getRecipientSettings(@NonNull Cursor cursor) {
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
@ -139,7 +158,7 @@ public class RecipientDatabase extends Database {
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
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; int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY)); String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)); String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME)); String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
@ -168,7 +187,8 @@ public class RecipientDatabase extends Database {
return Optional.of(new RecipientSettings(blocked, muteUntil, return Optional.of(new RecipientSettings(blocked, muteUntil,
VibrateState.fromId(vibrateState), VibrateState.fromId(vibrateState),
notificationUri, color, seenInviteReminder, notificationUri, color, seenInviteReminder,
defaultSubscriptionId, expireMessages, registered, defaultSubscriptionId, expireMessages,
RegisteredState.fromId(registeredState),
profileKey, systemDisplayName, signalProfileName, profileKey, systemDisplayName, signalProfileName,
signalProfileAvatar, profileSharing)); signalProfileAvatar, profileSharing));
} }
@ -185,117 +205,129 @@ public class RecipientDatabase extends Database {
return new BulkOperationsHandle(database); return new BulkOperationsHandle(database);
} }
public void setColor(Recipient recipient, MaterialColor color) { public void setColor(@NonNull Recipient recipient, @NonNull MaterialColor color) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(COLOR, color.serialize()); values.put(COLOR, color.serialize());
updateOrInsert(recipient.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setColor(color);
} }
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.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
EventBus.getDefault().post(new RecipientPreferenceEvent(recipient)); recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId));
} }
public void setBlocked(Recipient recipient, boolean blocked) { public void setBlocked(@NonNull 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.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setBlocked(blocked);
} }
public void setRingtone(Recipient recipient, @Nullable Uri notification) { public void setRingtone(@NonNull 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.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setRingtone(notification);
} }
public void setVibrate(Recipient recipient, @NonNull VibrateState enabled) { public void setVibrate(@NonNull 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.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setVibrate(enabled);
} }
public void setMuted(Recipient recipient, long until) { public void setMuted(@NonNull Recipient recipient, long until) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(MUTE_UNTIL, until); values.put(MUTE_UNTIL, until);
updateOrInsert(recipient.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setMuted(until);
} }
public void setSeenInviteReminder(Recipient recipient, boolean seen) { public void setSeenInviteReminder(@NonNull 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.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setHasSeenInviteReminder(seen);
} }
public void setExpireMessages(Recipient recipient, int expiration) { public void setExpireMessages(@NonNull Recipient recipient, int expiration) {
recipient.setExpireMessages(expiration); recipient.setExpireMessages(expiration);
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(EXPIRE_MESSAGES, expiration); values.put(EXPIRE_MESSAGES, expiration);
updateOrInsert(recipient.getAddress(), values); updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setExpireMessages(expiration);
} }
public void setSystemDisplayName(@NonNull Address address, @Nullable String systemDisplayName) { public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
ContentValues values = new ContentValues(1);
values.put(SYSTEM_DISPLAY_NAME, systemDisplayName);
updateOrInsert(address, values);
}
public void setProfileKey(@NonNull Address address, @Nullable byte[] profileKey) {
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey)); values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
updateOrInsert(address, values);; updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setProfileKey(profileKey);
} }
public void setProfileName(@NonNull Address address, @Nullable String profileName) { public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(SIGNAL_PROFILE_NAME, profileName); contentValues.put(SIGNAL_PROFILE_NAME, profileName);
updateOrInsert(address, contentValues); updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setProfileName(profileName);
} }
public void setProfileAvatar(@NonNull Address address, @Nullable String profileAvatar) { public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar); contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
updateOrInsert(address, contentValues); updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setProfileAvatar(profileAvatar);
} }
public void setProfileSharing(@NonNull Address address, boolean enabled) { public void setProfileSharing(@NonNull Recipient recipient, boolean enabled) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0); contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
updateOrInsert(address, contentValues); updateOrInsert(recipient.getAddress(), contentValues);
recipient.setProfileSharing(enabled);
} }
public Set<Address> getAllRecipients() { public Set<Recipient> getAllRecipients() {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
Set<Address> results = new HashSet<>(); Set<Recipient> results = new HashSet<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(Address.fromExternal(context, cursor.getString(0))); results.add(Recipient.from(context, Address.fromExternal(context, cursor.getString(0)), true));
} }
} }
return results; return results;
} }
public void setRegistered(@NonNull List<Address> activeAddresses, public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) {
@NonNull List<Address> inactiveAddresses)
{
SQLiteDatabase db = databaseHelper.getWritableDatabase();
for (Address activeAddress : activeAddresses) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, 1); contentValues.put(REGISTERED, registeredState.getId());
updateOrInsert(recipient.getAddress(), contentValues);
updateOrInsert(activeAddress, contentValues); recipient.setRegistered(registeredState);
} }
for (Address inactiveAddress : inactiveAddresses) { public void setRegistered(@NonNull List<Recipient> activeRecipients,
@NonNull List<Recipient> inactiveRecipients)
{
for (Recipient activeRecipient : activeRecipients) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, 0); contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
updateOrInsert(inactiveAddress, contentValues); updateOrInsert(activeRecipient.getAddress(), contentValues);
activeRecipient.setRegistered(RegisteredState.REGISTERED);
}
for (Recipient inactiveRecipient : inactiveRecipients) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
updateOrInsert(inactiveRecipient.getAddress(), contentValues);
inactiveRecipient.setRegistered(RegisteredState.NOT_REGISTERED);
} }
context.getContentResolver().notifyChange(Uri.parse(RECIPIENT_PREFERENCES_URI), null); context.getContentResolver().notifyChange(Uri.parse(RECIPIENT_PREFERENCES_URI), null);
@ -341,20 +373,25 @@ public class RecipientDatabase extends Database {
private final SQLiteDatabase database; private final SQLiteDatabase database;
public BulkOperationsHandle(SQLiteDatabase database) { private final List<Pair<Recipient, String>> pendingDisplayNames = new LinkedList<>();
BulkOperationsHandle(SQLiteDatabase database) {
this.database = database; this.database = database;
} }
public void setDisplayName(@NonNull Address address, @Nullable String displayName) { public void setDisplayName(@NonNull Recipient recipient, @Nullable String displayName) {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(SYSTEM_DISPLAY_NAME, displayName); contentValues.put(SYSTEM_DISPLAY_NAME, displayName);
updateOrInsert(address, contentValues); updateOrInsert(recipient.getAddress(), contentValues);
pendingDisplayNames.add(new Pair<>(recipient, displayName));
} }
public void finish() { public void finish() {
database.setTransactionSuccessful(); database.setTransactionSuccessful();
database.endTransaction(); database.endTransaction();
Recipient.clearCache(context);
Stream.of(pendingDisplayNames).forEach(pair -> pair.first().resolve().setSystemDisplayName(pair.second()));
context.getContentResolver().notifyChange(Uri.parse(RECIPIENT_PREFERENCES_URI), null); context.getContentResolver().notifyChange(Uri.parse(RECIPIENT_PREFERENCES_URI), null);
} }
} }
@ -368,7 +405,7 @@ public class RecipientDatabase extends Database {
private final boolean seenInviteReminder; private final boolean seenInviteReminder;
private final int defaultSubscriptionId; private final int defaultSubscriptionId;
private final int expireMessages; private final int expireMessages;
private final boolean registered; private final RegisteredState registered;
private final byte[] profileKey; private final byte[] profileKey;
private final String systemDisplayName; private final String systemDisplayName;
private final String signalProfileName; private final String signalProfileName;
@ -382,7 +419,7 @@ public class RecipientDatabase extends Database {
boolean seenInviteReminder, boolean seenInviteReminder,
int defaultSubscriptionId, int defaultSubscriptionId,
int expireMessages, int expireMessages,
boolean registered, @NonNull RegisteredState registered,
@Nullable byte[] profileKey, @Nullable byte[] profileKey,
@Nullable String systemDisplayName, @Nullable String systemDisplayName,
@Nullable String signalProfileName, @Nullable String signalProfileName,
@ -437,7 +474,7 @@ public class RecipientDatabase extends Database {
return expireMessages; return expireMessages;
} }
public boolean isRegistered() { public RegisteredState getRegistered() {
return registered; return registered;
} }
@ -485,17 +522,4 @@ public class RecipientDatabase extends Database {
return getCurrent(); return getCurrent();
} }
} }
public static class RecipientPreferenceEvent {
private final Recipient recipient;
public RecipientPreferenceEvent(Recipient recipients) {
this.recipient = recipients;
}
public Recipient getRecipient() {
return recipient;
}
}
} }

View File

@ -624,7 +624,7 @@ public class ThreadDatabase extends Database {
Optional<GroupRecord> groupRecord; Optional<GroupRecord> groupRecord;
if (distributionType != DistributionTypes.ARCHIVE) { if (distributionType != DistributionTypes.ARCHIVE) {
settings = DatabaseFactory.getRecipientDatabase(context).getRecipientPreferences(cursor); settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(cursor);
groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor); groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor);
} else { } else {
settings = Optional.absent(); settings = Optional.absent();

View File

@ -46,6 +46,7 @@ public class GroupManager {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms); final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
final Set<Address> memberAddresses = getMemberAddresses(members); final Set<Address> memberAddresses = getMemberAddresses(members);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
@ -53,10 +54,9 @@ public class GroupManager {
if (!mms) { if (!mms) {
groupDatabase.updateAvatar(groupId, avatarBytes); groupDatabase.updateAvatar(groupId, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Address.fromSerialized(groupId), true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
} else { } else {
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadId);
} }

View File

@ -805,17 +805,12 @@ public class PushDecryptJob extends ContextJob {
{ {
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource()); Address sourceAddress = Address.fromExternal(context, envelope.getSource());
Optional<RecipientSettings> settings = database.getRecipientSettings(sourceAddress); Recipient recipient = Recipient.from(context, sourceAddress, false);
if (!settings.isPresent() || settings.get().getProfileKey() == null || if (recipient.getProfileKey() == null || MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
!MessageDigest.isEqual(message.getProfileKey().get(), settings.get().getProfileKey())) database.setProfileKey(recipient, message.getProfileKey().get());
{
database.setProfileKey(sourceAddress, message.getProfileKey().get());
Recipient recipient = Recipient.from(context, sourceAddress, true);
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileJob(context, recipient)); ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileJob(context, recipient));
} }
} }
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) { private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {

View File

@ -114,7 +114,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient().getAddress()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody()) .withBody(message.getBody())
.withAttachments(attachmentStreams) .withAttachments(attachmentStreams)

View File

@ -1,12 +1,14 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
@ -28,10 +30,10 @@ public abstract class PushReceivedJob extends ContextJob {
public void handle(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) { public void handle(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) {
Address source = Address.fromExternal(context, envelope.getSource()); Address source = Address.fromExternal(context, envelope.getSource());
if (!isActiveNumber(context, source)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(Util.asList(source), new LinkedList<>());
Recipient recipient = Recipient.from(context, source, false); Recipient recipient = Recipient.from(context, source, false);
if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipient)); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipient));
} }
@ -68,9 +70,8 @@ public abstract class PushReceivedJob extends ContextJob {
envelope.getTimestamp())); envelope.getTimestamp()));
} }
private boolean isActiveNumber(Context context, Address address) { private boolean isActiveNumber(@NonNull Recipient recipient) {
Optional<RecipientSettings> settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address); return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
return settings.isPresent() && settings.get().isRegistered();
} }

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -64,17 +65,11 @@ public abstract class PushSendJob extends SendJob {
onPushSend(masterSecret); onPushSend(masterSecret);
} }
protected Optional<byte[]> getProfileKey(Address address) { protected Optional<byte[]> getProfileKey(@NonNull Recipient recipient) {
try { try {
Optional<RecipientSettings> recipientsPreferences = DatabaseFactory.getRecipientDatabase(context) if (!recipient.resolve().isSystemContact() && !recipient.resolve().isProfileSharing()) {
.getRecipientSettings(address); return Optional.absent();
}
if (!recipientsPreferences.isPresent()) return Optional.absent();
boolean isSystemContact = !TextUtils.isEmpty(recipientsPreferences.get().getSystemDisplayName());
boolean isApproved = recipientsPreferences.get().isProfileSharing();
if (!isSystemContact & !isApproved) return Optional.absent();
String profileKey = TextSecurePreferences.getProfileKey(context); String profileKey = TextSecurePreferences.getProfileKey(context);

View File

@ -102,7 +102,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
try { try {
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress()); SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceMessageSender messageSender = messageSenderFactory.create();
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient().getAddress()); Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent()) .withTimestamp(message.getDateSent())
.withBody(message.getBody().getBody()) .withBody(message.getBody().getBody())

View File

@ -5,16 +5,16 @@ import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -52,19 +52,14 @@ public class RetrieveProfileAvatarJob extends ContextJob implements InjectableTy
@Override @Override
public void onRun() throws IOException { public void onRun() throws IOException {
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Optional<RecipientSettings> recipientSettings = database.getRecipientSettings(recipient.getAddress()); byte[] profileKey = recipient.resolve().getProfileKey();
if (!recipientSettings.isPresent()) { if (profileKey == null) {
Log.w(TAG, "Recipient preference row is gone!");
return;
}
if (recipientSettings.get().getProfileKey() == null) {
Log.w(TAG, "Recipient profile key is gone!"); Log.w(TAG, "Recipient profile key is gone!");
return; return;
} }
if (Util.equals(profileAvatar, recipientSettings.get().getProfileAvatar())) { if (Util.equals(profileAvatar, recipient.resolve().getProfileAvatar())) {
Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar); Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar);
return; return;
} }
@ -72,13 +67,14 @@ public class RetrieveProfileAvatarJob extends ContextJob implements InjectableTy
if (TextUtils.isEmpty(profileAvatar)) { if (TextUtils.isEmpty(profileAvatar)) {
Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize()); Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize());
AvatarHelper.delete(context, recipient.getAddress()); AvatarHelper.delete(context, recipient.getAddress());
database.setProfileAvatar(recipient, profileAvatar);
return; return;
} }
File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir()); File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
try { try {
InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, recipientSettings.get().getProfileKey(), MAX_PROFILE_SIZE_BYTES); InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir()); File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
Util.copy(avatarStream, new FileOutputStream(decryptDestination)); Util.copy(avatarStream, new FileOutputStream(decryptDestination));
@ -87,8 +83,11 @@ public class RetrieveProfileAvatarJob extends ContextJob implements InjectableTy
if (downloadDestination != null) downloadDestination.delete(); if (downloadDestination != null) downloadDestination.delete();
} }
database.setProfileAvatar(recipient.getAddress(), profileAvatar); database.setProfileAvatar(recipient, profileAvatar);
Recipient.clearCache(context);
if (recipient.resolve().getContactPhoto().isGenerated()) {
recipient.setContactPhoto(ContactPhotoFactory.getSignalAvatarContactPhoto(context, recipient.getAddress(), recipient.getName(), context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size)));
}
} }
@Override @Override

View File

@ -73,11 +73,10 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
{ {
String number = recipient.getAddress().toPhoneString(); String number = recipient.getAddress().toPhoneString();
SignalServiceProfile profile = retrieveProfile(number); SignalServiceProfile profile = retrieveProfile(number);
Optional<RecipientSettings> recipientSettings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(recipient.getAddress());
setIdentityKey(recipient, profile.getIdentityKey()); setIdentityKey(recipient, profile.getIdentityKey());
setProfileName(recipient, recipientSettings, profile.getName()); setProfileName(recipient, profile.getName());
setProfileAvatar(recipient, recipientSettings, profile.getAvatar()); setProfileAvatar(recipient, profile.getAvatar());
} }
private void handleGroupRecipient(Recipient group) private void handleGroupRecipient(Recipient group)
@ -127,32 +126,30 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
} }
} }
private void setProfileName(Recipient recipient, Optional<RecipientSettings> recipientPreferences, String profileName) { private void setProfileName(Recipient recipient, String profileName) {
try { try {
if (!recipientPreferences.isPresent()) return; byte[] profileKey = recipient.getProfileKey();
if (recipientPreferences.get().getProfileKey() == null) return; if (profileKey == null) return;
String plaintextProfileName = null; String plaintextProfileName = null;
if (profileName != null) { if (profileName != null) {
ProfileCipher profileCipher = new ProfileCipher(recipientPreferences.get().getProfileKey()); ProfileCipher profileCipher = new ProfileCipher(profileKey);
plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName))); plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName)));
} }
if (!Util.equals(plaintextProfileName, recipientPreferences.get().getProfileName())) { if (!Util.equals(plaintextProfileName, recipient.getProfileName())) {
DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient.getAddress(), plaintextProfileName); DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient, plaintextProfileName);
Recipient.clearCache(context);
} }
} catch (ProfileCipher.InvalidCiphertextException | IOException e) { } catch (ProfileCipher.InvalidCiphertextException | IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
} }
private void setProfileAvatar(Recipient recipient, Optional<RecipientSettings> recipientPreferences, String profileAvatar) { private void setProfileAvatar(Recipient recipient, String profileAvatar) {
if (!recipientPreferences.isPresent()) return; if (recipient.getProfileKey() == null) return;
if (recipientPreferences.get().getProfileKey() == null) return;
if (!Util.equals(profileAvatar, recipientPreferences.get().getProfileAvatar())) { if (!Util.equals(profileAvatar, recipient.getProfileAvatar())) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new RetrieveProfileAvatarJob(context, recipient, profileAvatar)); .add(new RetrieveProfileAvatarJob(context, recipient, profileAvatar));

View File

@ -73,9 +73,8 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
long replyThreadId; long replyThreadId;
Optional<RecipientSettings> settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
int subscriptionId = settings.isPresent() ? settings.get().getDefaultSubscriptionId().or(-1) : -1; long expiresIn = recipient.getExpireMessages() * 1000;
long expiresIn = settings.isPresent() ? settings.get().getExpireMessages() * 1000 : 0;
if (recipient.isGroupRecipient()) { if (recipient.isGroupRecipient()) {
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");

View File

@ -67,10 +67,9 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
long threadId; long threadId;
Optional<RecipientSettings> settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address);
int subscriptionId = settings.isPresent() ? settings.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = settings.isPresent() ? settings.get().getExpireMessages() * 1000 : 0;
Recipient recipient = Recipient.from(context, address, false); Recipient recipient = Recipient.from(context, address, false);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000;
if (recipient.isGroupRecipient()) { if (recipient.isGroupRecipient()) {
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0); OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);

View File

@ -52,11 +52,6 @@ public class UnknownSenderView extends FrameLayout {
if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true);
return null; return null;
} }
@Override
protected void onPostExecute(Void result) {
recipient.setBlocked(true);
}
}.execute(); }.execute();
}) })
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
@ -94,7 +89,7 @@ public class UnknownSenderView extends FrameLayout {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getAddress(), true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true);
return null; return null;
} }

View File

@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientDatabase.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;
@ -63,14 +64,22 @@ public class Recipient implements RecipientModifiedListener {
private ContactPhoto contactPhoto; private ContactPhoto contactPhoto;
private Uri contactUri; private Uri contactUri;
private Uri ringtone = null; private @Nullable Uri ringtone = null;
private long mutedUntil = 0; private long mutedUntil = 0;
private boolean blocked = false; private boolean blocked = false;
private VibrateState vibrate = VibrateState.DEFAULT; private VibrateState vibrate = VibrateState.DEFAULT;
private int expireMessages = 0; private int expireMessages = 0;
private String profileName = null; private Optional<Integer> defaultSubscriptionId = Optional.absent();
private @NonNull RegisteredState registered = RegisteredState.UNKNOWN;
private @Nullable MaterialColor color;
private boolean seenInviteReminder;
private @Nullable byte[] profileKey;
private @Nullable String profileName;
private @Nullable String profileAvatar;
private boolean profileSharing;
private boolean isSystemContact;
@Nullable private MaterialColor color;
public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) { public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
if (address == null) throw new AssertionError(address); if (address == null) throw new AssertionError(address);
@ -108,7 +117,14 @@ public class Recipient implements RecipientModifiedListener {
this.blocked = stale.blocked; this.blocked = stale.blocked;
this.vibrate = stale.vibrate; this.vibrate = stale.vibrate;
this.expireMessages = stale.expireMessages; this.expireMessages = stale.expireMessages;
this.seenInviteReminder = stale.seenInviteReminder;
this.defaultSubscriptionId = stale.defaultSubscriptionId;
this.registered = stale.registered;
this.profileKey = stale.profileKey;
this.profileName = stale.profileName; this.profileName = stale.profileName;
this.profileAvatar = stale.profileAvatar;
this.profileSharing = stale.profileSharing;
this.isSystemContact = stale.isSystemContact;
this.participants.clear(); this.participants.clear();
this.participants.addAll(stale.participants); this.participants.addAll(stale.participants);
} }
@ -122,7 +138,14 @@ public class Recipient implements RecipientModifiedListener {
this.blocked = details.get().blocked; this.blocked = details.get().blocked;
this.vibrate = details.get().vibrateState; this.vibrate = details.get().vibrateState;
this.expireMessages = details.get().expireMessages; this.expireMessages = details.get().expireMessages;
this.seenInviteReminder = details.get().seenInviteReminder;
this.defaultSubscriptionId = details.get().defaultSubscriptionId;
this.registered = details.get().registered;
this.profileKey = details.get().profileKey;
this.profileName = details.get().profileName; this.profileName = details.get().profileName;
this.profileAvatar = details.get().profileAvatar;
this.profileSharing = details.get().profileSharing;
this.isSystemContact = details.get().systemContact;
this.participants.clear(); this.participants.clear();
this.participants.addAll(details.get().participants); this.participants.addAll(details.get().participants);
} }
@ -142,7 +165,15 @@ public class Recipient implements RecipientModifiedListener {
Recipient.this.blocked = result.blocked; Recipient.this.blocked = result.blocked;
Recipient.this.vibrate = result.vibrateState; Recipient.this.vibrate = result.vibrateState;
Recipient.this.expireMessages = result.expireMessages; Recipient.this.expireMessages = result.expireMessages;
Recipient.this.seenInviteReminder = result.seenInviteReminder;
Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId;
Recipient.this.registered = result.registered;
Recipient.this.profileKey = result.profileKey;
Recipient.this.profileName = result.profileName; Recipient.this.profileName = result.profileName;
Recipient.this.profileAvatar = result.profileAvatar;
Recipient.this.profileSharing = result.profileSharing;
Recipient.this.profileName = result.profileName;
Recipient.this.isSystemContact = result.systemContact;
Recipient.this.participants.clear(); Recipient.this.participants.clear();
Recipient.this.participants.addAll(result.participants); Recipient.this.participants.addAll(result.participants);
@ -151,6 +182,8 @@ public class Recipient implements RecipientModifiedListener {
if (!listeners.isEmpty()) { if (!listeners.isEmpty()) {
for (Recipient recipient : participants) recipient.addListener(Recipient.this); for (Recipient recipient : participants) recipient.addListener(Recipient.this);
} }
Recipient.this.notifyAll();
} }
notifyListeners(); notifyListeners();
@ -176,7 +209,14 @@ public class Recipient implements RecipientModifiedListener {
this.blocked = details.blocked; this.blocked = details.blocked;
this.vibrate = details.vibrateState; this.vibrate = details.vibrateState;
this.expireMessages = details.expireMessages; this.expireMessages = details.expireMessages;
this.seenInviteReminder = details.seenInviteReminder;
this.defaultSubscriptionId = details.defaultSubscriptionId;
this.registered = details.registered;
this.profileKey = details.profileKey;
this.profileName = details.profileName; this.profileName = details.profileName;
this.profileAvatar = details.profileAvatar;
this.profileSharing = details.profileSharing;
this.isSystemContact = details.systemContact;
this.participants.addAll(details.participants); this.participants.addAll(details.participants);
this.resolving = false; this.resolving = false;
} }
@ -222,10 +262,54 @@ public class Recipient implements RecipientModifiedListener {
return customLabel; return customLabel;
} }
public @Nullable String getProfileName() { public synchronized Optional<Integer> getDefaultSubscriptionId() {
return defaultSubscriptionId;
}
public void setDefaultSubscriptionId(Optional<Integer> defaultSubscriptionId) {
synchronized (this) {
this.defaultSubscriptionId = defaultSubscriptionId;
}
notifyListeners();
}
public synchronized @Nullable String getProfileName() {
return profileName; return profileName;
} }
public void setProfileName(@Nullable String profileName) {
synchronized (this) {
this.profileName = profileName;
}
notifyListeners();
}
public synchronized @Nullable String getProfileAvatar() {
return profileAvatar;
}
public void setProfileAvatar(@Nullable String profileAvatar) {
synchronized (this) {
this.profileAvatar = profileAvatar;
}
notifyListeners();
}
public synchronized boolean isProfileSharing() {
return profileSharing;
}
public void setProfileSharing(boolean value) {
synchronized (this) {
this.profileSharing = value;
}
notifyListeners();
}
public boolean isGroupRecipient() { public boolean isGroupRecipient() {
return address.isGroup(); return address.isGroup();
} }
@ -265,6 +349,14 @@ public class Recipient implements RecipientModifiedListener {
return contactPhoto; return contactPhoto;
} }
public void setContactPhoto(@NonNull ContactPhoto contactPhoto) {
synchronized (this) {
this.contactPhoto = contactPhoto;
}
notifyListeners();
}
public synchronized @Nullable Uri getRingtone() { public synchronized @Nullable Uri getRingtone() {
return ringtone; return ringtone;
} }
@ -325,6 +417,60 @@ public class Recipient implements RecipientModifiedListener {
notifyListeners(); notifyListeners();
} }
public synchronized boolean hasSeenInviteReminder() {
return seenInviteReminder;
}
public void setHasSeenInviteReminder(boolean value) {
synchronized (this) {
this.seenInviteReminder = value;
}
notifyListeners();
}
public synchronized RegisteredState getRegistered() {
return registered;
}
public void setRegistered(@NonNull RegisteredState value) {
synchronized (this) {
this.registered = value;
}
notifyListeners();
}
public synchronized @Nullable byte[] getProfileKey() {
return profileKey;
}
public void setProfileKey(@Nullable byte[] profileKey) {
synchronized (this) {
this.profileKey = profileKey;
}
notifyListeners();
}
public synchronized boolean isSystemContact() {
return isSystemContact;
}
public void setSystemDisplayName(@Nullable String displayName) {
synchronized (this) {
if (displayName == null) this.name = profileName;
else this.name = displayName;
}
notifyListeners();
}
public synchronized Recipient resolve() {
while (resolving) Util.wait(this, 0);
return this;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@ -369,4 +515,5 @@ public class Recipient implements RecipientModifiedListener {
return resolving; return resolving;
} }
} }

View File

@ -33,7 +33,9 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
@ -199,6 +201,13 @@ class RecipientProvider {
public final int expireMessages; public final int expireMessages;
@NonNull public final List<Recipient> participants; @NonNull public final List<Recipient> participants;
@Nullable public final String profileName; @Nullable public final String profileName;
public final boolean seenInviteReminder;
public final Optional<Integer> defaultSubscriptionId;
@NonNull public final RegisteredState registered;
@Nullable public final byte[] profileKey;
@Nullable public final String profileAvatar;
public final boolean profileSharing;
public final boolean systemContact;
public RecipientDetails(@Nullable String name, @Nullable String customLabel, public RecipientDetails(@Nullable String name, @Nullable String customLabel,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar, @Nullable Uri contactUri, @NonNull ContactPhoto avatar,
@ -216,6 +225,13 @@ class RecipientProvider {
this.expireMessages = settings != null ? settings.getExpireMessages() : 0; this.expireMessages = settings != null ? settings.getExpireMessages() : 0;
this.participants = participants == null ? new LinkedList<Recipient>() : participants; this.participants = participants == null ? new LinkedList<Recipient>() : participants;
this.profileName = settings != null ? settings.getProfileName() : null; this.profileName = settings != null ? settings.getProfileName() : null;
this.seenInviteReminder = settings != null && settings.hasSeenInviteReminder();
this.defaultSubscriptionId = settings != null ? settings.getDefaultSubscriptionId() : Optional.absent();
this.registered = settings != null ? settings.getRegistered() : RegisteredState.UNKNOWN;
this.profileKey = settings != null ? settings.getProfileKey() : null;
this.profileAvatar = settings != null ? settings.getProfileAvatar() : null;
this.profileSharing = settings != null && settings.isProfileSharing();
this.systemContact = settings != null && !TextUtils.isEmpty(settings.getSystemDisplayName());
if (name == null && settings != null) this.name = settings.getSystemDisplayName(); if (name == null && settings != null) this.name = settings.getSystemDisplayName();
else this.name = name; else this.name = name;

View File

@ -53,9 +53,8 @@ public class QuickResponseService extends MasterSecretIntentService {
Address address = Address.fromExternal(this, number); Address address = Address.fromExternal(this, number);
Recipient recipient = Recipient.from(this, address, false); Recipient recipient = Recipient.from(this, address, false);
Optional<RecipientSettings> settings = DatabaseFactory.getRecipientDatabase(this).getRecipientSettings(recipient.getAddress()); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
int subscriptionId = settings.isPresent() ? settings.get().getDefaultSubscriptionId().or(-1) : -1; long expiresIn = recipient.getExpireMessages() * 1000;
long expiresIn = settings.isPresent() ? settings.get().getExpireMessages() * 1000 : 0;
if (!TextUtils.isEmpty(content)) { if (!TextUtils.isEmpty(content)) {
MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipient, content, expiresIn, subscriptionId), -1, false, null); MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipient, content, expiresIn, subscriptionId), -1, false, null);

View File

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -50,7 +49,6 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList;
public class MessageSender { public class MessageSender {
@ -232,7 +230,7 @@ public class MessageSender {
return false; return false;
} }
return isPushDestination(context, recipient.getAddress()); return isPushDestination(context, recipient);
} }
private static boolean isPushMediaSend(Context context, Recipient recipient) { private static boolean isPushMediaSend(Context context, Recipient recipient) {
@ -244,7 +242,7 @@ public class MessageSender {
return false; return false;
} }
return isPushDestination(context, recipient.getAddress()); return isPushDestination(context, recipient);
} }
private static boolean isGroupPushSend(Recipient recipient) { private static boolean isGroupPushSend(Recipient recipient) {
@ -264,22 +262,21 @@ public class MessageSender {
return Util.isOwnNumber(context, recipient.getAddress()); return Util.isOwnNumber(context, recipient.getAddress());
} }
private static boolean isPushDestination(Context context, Address destination) { private static boolean isPushDestination(Context context, Recipient destination) {
RecipientDatabase recipientsDatabase = DatabaseFactory.getRecipientDatabase(context); if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
Optional<RecipientSettings> recipientPreferences = recipientsDatabase.getRecipientSettings(destination); return true;
} else if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.NOT_REGISTERED) {
if (recipientPreferences.isPresent()) { return false;
return recipientPreferences.get().isRegistered();
} else { } else {
try { try {
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
Optional<ContactTokenDetails> registeredUser = accountManager.getContact(destination.serialize()); Optional<ContactTokenDetails> registeredUser = accountManager.getContact(destination.getAddress().serialize());
if (!registeredUser.isPresent()) { if (!registeredUser.isPresent()) {
recipientsDatabase.setRegistered(new LinkedList<>(), Util.asList(destination)); DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.NOT_REGISTERED);
return false; return false;
} else { } else {
recipientsDatabase.setRegistered(Util.asList(destination), new LinkedList<>()); DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.REGISTERED);
return true; return true;
} }
} catch (IOException e1) { } catch (IOException e1) {

View File

@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.push.AccountManagerFactory; import org.thoughtcrime.securesms.push.AccountManagerFactory;
@ -44,10 +45,6 @@ import java.util.Set;
public class DirectoryHelper { public class DirectoryHelper {
public enum Capability {
UNKNOWN, SUPPORTED, UNSUPPORTED
}
private static final String TAG = DirectoryHelper.class.getSimpleName(); private static final String TAG = DirectoryHelper.class.getSimpleName();
public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret) public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret)
@ -76,30 +73,35 @@ public class DirectoryHelper {
} }
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
Set<Address> eligibleContactNumbers = recipientDatabase.getAllRecipients(); Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllRecipients()).map(recipient -> recipient.getAddress().serialize());
eligibleContactNumbers.addAll(ContactAccessor.getInstance().getAllContactsWithNumbers(context)); Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context)).map(Address::serialize);
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
Set<String> serializedAddress = Stream.of(eligibleContactNumbers).map(Address::serialize).collect(Collectors.toSet()); List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
List<ContactTokenDetails> activeTokens = accountManager.getContacts(serializedAddress);
if (activeTokens != null) { if (activeTokens != null) {
List<Address> activeAddresses = new LinkedList<>(); List<Recipient> activeRecipients = new LinkedList<>();
Set<Address> inactiveAddresses = new HashSet<>(eligibleContactNumbers); List<Recipient> inactiveRecipients = new LinkedList<>();
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
for (ContactTokenDetails activeToken : activeTokens) { for (ContactTokenDetails activeToken : activeTokens) {
Address activeAddress = Address.fromSerialized(activeToken.getNumber()); activeRecipients.add(Recipient.from(context, Address.fromSerialized(activeToken.getNumber()), true));
activeAddresses.add(activeAddress); inactiveContactNumbers.remove(activeToken.getNumber());
inactiveAddresses.remove(activeAddress);
} }
recipientDatabase.setRegistered(activeAddresses, new LinkedList<>(inactiveAddresses)); for (String inactiveContactNumber : inactiveContactNumbers) {
return updateContactsDatabase(context, activeAddresses, true); inactiveRecipients.add(Recipient.from(context, Address.fromSerialized(inactiveContactNumber), true));
}
recipientDatabase.setRegistered(activeRecipients, inactiveRecipients);
return updateContactsDatabase(context, Stream.of(activeRecipients).map(Recipient::getAddress).toList(), true);
} }
return new RefreshResult(new LinkedList<>(), false); return new RefreshResult(new LinkedList<>(), false);
} }
public static Capability refreshDirectoryFor(@NonNull Context context, public static RegisteredState refreshDirectoryFor(@NonNull Context context,
@Nullable MasterSecret masterSecret, @Nullable MasterSecret masterSecret,
@NonNull Recipient recipient) @NonNull Recipient recipient)
throws IOException throws IOException
@ -110,7 +112,7 @@ public class DirectoryHelper {
Optional<ContactTokenDetails> details = accountManager.getContact(number); Optional<ContactTokenDetails> details = accountManager.getContact(number);
if (details.isPresent()) { if (details.isPresent()) {
recipientDatabase.setRegistered(Util.asList(recipient.getAddress()), new LinkedList<>()); recipientDatabase.setRegistered(recipient, RegisteredState.REGISTERED);
RefreshResult result = updateContactsDatabase(context, Util.asList(recipient.getAddress()), false); RefreshResult result = updateContactsDatabase(context, Util.asList(recipient.getAddress()), false);
@ -122,38 +124,13 @@ public class DirectoryHelper {
notifyNewUsers(context, masterSecret, result.getNewUsers()); notifyNewUsers(context, masterSecret, result.getNewUsers());
} }
return Capability.SUPPORTED; return RegisteredState.REGISTERED;
} else { } else {
recipientDatabase.setRegistered(new LinkedList<>(), Util.asList(recipient.getAddress())); recipientDatabase.setRegistered(recipient, RegisteredState.NOT_REGISTERED);
return Capability.UNSUPPORTED; return RegisteredState.NOT_REGISTERED;
} }
} }
public static @NonNull Capability getUserCapabilities(@NonNull Context context, @Nullable Recipient recipient) {
if (recipient == null) {
return Capability.UNSUPPORTED;
}
if (!TextSecurePreferences.isPushRegistered(context)) {
return Capability.UNSUPPORTED;
}
if (recipient.isMmsGroupRecipient()) {
return Capability.UNSUPPORTED;
}
if (recipient.isPushGroupRecipient()) {
return Capability.SUPPORTED;
}
final RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
final Optional<RecipientSettings> recipientSettings = recipientDatabase.getRecipientSettings(recipient.getAddress());
if (recipientSettings.isPresent() && recipientSettings.get().isRegistered()) return Capability.SUPPORTED;
else if (recipientSettings.isPresent()) return Capability.UNSUPPORTED;
else return Capability.UNKNOWN;
}
private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, @NonNull List<Address> activeAddresses, boolean removeMissing) { private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, @NonNull List<Address> activeAddresses, boolean removeMissing) {
Optional<AccountHolder> account = getOrCreateAccount(context); Optional<AccountHolder> account = getOrCreateAccount(context);
@ -171,9 +148,10 @@ public class DirectoryHelper {
if (!TextUtils.isEmpty(number)) { if (!TextUtils.isEmpty(number)) {
Address address = Address.fromExternal(context, number); Address address = Address.fromExternal(context, number);
Recipient recipient = Recipient.from(context, address, true);
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
handle.setDisplayName(address, displayName); handle.setDisplayName(recipient, displayName);
} }
} }
} finally { } finally {