Migrated to locally-assigned RecipientId's.

Oh boy.
master
Greyson Parrelli 2019-08-07 14:22:51 -04:00
parent 233cc7ecce
commit 0e2d52026e
168 changed files with 3927 additions and 3110 deletions

View File

@ -223,7 +223,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
.setConstraintFactories(JobManagerFactories.getConstraintFactories(this))
.setConstraintObservers(JobManagerFactories.getConstraintObservers(this))
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this)))
.setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), 1, JobManagerFactories.getJobMigrations()))
.setJobMigrator(new JobMigrator(TextSecurePreferences.getJobManagerVersion(this), 2, JobManagerFactories.getJobMigrations(this)))
.build());
}
@ -338,7 +338,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private void executePendingContactSync() {
if (TextSecurePreferences.needsFullContactSync(this)) {
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(true));
}
}

View File

@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.whispersystems.libsignal.util.guava.Optional;
@ -36,7 +37,7 @@ public interface BindableConversationItem extends Unbindable {
interface EventListener {
void onQuoteClicked(MmsMessageRecord messageRecord);
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms);
void onMoreTextClicked(@NonNull RecipientId conversationRecipientId, long messageId, boolean isMms);
void onStickerClicked(@NonNull StickerLocator stickerLocator);
void onViewOnceMessageClicked(@NonNull MmsMessageRecord messageRecord);
void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView);

View File

@ -17,12 +17,14 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -78,6 +80,12 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onStart() {
super.onStart();
getLoaderManager().restartLoader(0, null, this);
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
@ -107,7 +115,7 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Recipient recipient = ((BlockedContactListItem)view).getRecipient();
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
startActivity(intent);
}
@ -129,8 +137,8 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override
public void bindView(View view, Context context, Cursor cursor) {
String address = cursor.getString(1);
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), true);
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID)));
LiveRecipient recipient = Recipient.live(recipientId);
((BlockedContactListItem) view).set(glideRequests, recipient);
}

View File

@ -12,7 +12,6 @@ import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@ -22,6 +21,7 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.VerifySpan;
@ -46,7 +46,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
{
super(context);
Recipient recipient = Recipient.from(context, mismatch.getAddress(), false);
Recipient recipient = Recipient.resolved(mismatch.getRecipientId(context));
String name = recipient.toShortString();
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
@ -59,7 +59,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
setTitle(name);
setMessage(spannableString);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress()));
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getId()));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
}
@ -78,12 +78,12 @@ public class ConfirmIdentityDialog extends AlertDialog {
private final MessageRecord messageRecord;
private final IdentityKeyMismatch mismatch;
private final Address address;
private final RecipientId recipientId;
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, RecipientId recipientId) {
this.messageRecord = messageRecord;
this.mismatch = mismatch;
this.address = address;
this.recipientId = recipientId;
}
@SuppressLint("StaticFieldLeak")
@ -94,7 +94,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireAddress().toPhoneString(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
@ -137,17 +137,17 @@ public class ConfirmIdentityDialog extends AlertDialog {
if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
if (messageRecord.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
if (messageRecord.getRecipient().isPushGroup()) {
MessageSender.resendGroupMessage(getContext(), messageRecord, Recipient.resolved(mismatch.getRecipientId(getContext())).getId());
} else {
MessageSender.resend(getContext(), messageRecord);
}
} else {
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
MessageSender.resend(getContext(), messageRecord);
@ -160,13 +160,13 @@ public class ConfirmIdentityDialog extends AlertDialog {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
boolean legacy = !messageRecord.isContentBundleKeyExchange();
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
messageRecord.getIndividualRecipient().requireAddress().toPhoneString(),
messageRecord.getRecipientDeviceId(),
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody()) : null,

View File

@ -45,7 +45,6 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
@ -112,9 +111,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
SimpleTask.run(getLifecycle(), () -> {
return Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), false);
}, this::initializeProfileIcon);
SimpleTask.run(getLifecycle(), Recipient::self, this::initializeProfileIcon);
}
@Override
@ -191,7 +188,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
Drawable fallback = new GeneratedContactPhoto(name, R.drawable.ic_profile_default).asDrawable(this, fallbackColor.toAvatarColor(this));
GlideApp.with(this)
.load(new ProfileContactPhoto(recipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))))
.load(new ProfileContactPhoto(recipient.requireAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))))
.error(fallback)
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.ALL)
@ -225,7 +222,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
searchToolbar.clearFocus();
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());

View File

@ -76,7 +76,7 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
public long getItemId(@NonNull Cursor cursor) {
ThreadRecord record = getThreadRecord(cursor);
return Conversions.byteArrayToLong(digest.digest(record.getRecipient().getAddress().serialize().getBytes()));
return Conversions.byteArrayToLong(digest.digest(record.getRecipient().getId().serialize().getBytes()));
}
@Override

View File

@ -54,7 +54,7 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
@Override
public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeenTime) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);

View File

@ -39,13 +39,13 @@ import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.TypingIndicatorView;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.SearchUtil;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Collections;
@ -53,7 +53,7 @@ import java.util.Locale;
import java.util.Set;
public class ConversationListItem extends RelativeLayout
implements RecipientModifiedListener,
implements RecipientForeverObserver,
BindableConversationListItem, Unbindable
{
@SuppressWarnings("unused")
@ -65,7 +65,7 @@ public class ConversationListItem extends RelativeLayout
private static final int MAX_SNIPPET_LENGTH = 500;
private Set<Long> selectedThreads;
private Recipient recipient;
private LiveRecipient recipient;
private long threadId;
private GlideRequests glideRequests;
private View subjectContainer;
@ -132,21 +132,23 @@ public class ConversationListItem extends RelativeLayout
boolean batchMode,
@Nullable String highlightSubstring)
{
if (this.recipient != null) this.recipient.removeForeverObserver(this);
this.selectedThreads = selectedThreads;
this.recipient = thread.getRecipient();
this.recipient = thread.getRecipient().live();
this.threadId = thread.getThreadId();
this.glideRequests = glideRequests;
this.unreadCount = thread.getUnreadCount();
this.distributionType = thread.getDistributionType();
this.lastSeen = thread.getLastSeen();
this.recipient.addListener(this);
this.recipient.observeForever(this);
if (highlightSubstring != null) {
String name = recipient.isLocalNumber() ? getContext().getString(R.string.note_to_self) : recipient.getName();
String name = recipient.get().isLocalNumber() ? getContext().getString(R.string.note_to_self) : recipient.get().getName();
this.fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), name, highlightSubstring));
} else {
this.fromView.setText(recipient, unreadCount == 0);
this.fromView.setText(recipient.get(), unreadCount == 0);
}
if (typingThreads.contains(threadId)) {
@ -182,9 +184,9 @@ public class ConversationListItem extends RelativeLayout
setStatusIcons(thread);
setThumbnailSnippet(thread);
setBatchState(batchMode);
setRippleColor(recipient);
setRippleColor(recipient.get());
setUnreadIndicator(thread);
this.contactPhotoImage.setAvatar(glideRequests, recipient, true);
this.contactPhotoImage.setAvatar(glideRequests, recipient.get(), true);
}
public void bind(@NonNull Recipient contact,
@ -192,16 +194,18 @@ public class ConversationListItem extends RelativeLayout
@NonNull Locale locale,
@Nullable String highlightSubstring)
{
if (this.recipient != null) this.recipient.removeForeverObserver(this);
this.selectedThreads = Collections.emptySet();
this.recipient = contact;
this.recipient = contact.live();
this.glideRequests = glideRequests;
this.recipient.addListener(this);
this.recipient.observeForever(this);
String name = recipient.isLocalNumber() ? getContext().getString(R.string.note_to_self) : recipient.getName();
String name = recipient.get().isLocalNumber() ? getContext().getString(R.string.note_to_self) : recipient.get().getName();
fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), name, highlightSubstring));
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.getAddress().toString(), highlightSubstring));
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.requireAddress().toString(), highlightSubstring));
dateView.setText("");
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
@ -211,7 +215,7 @@ public class ConversationListItem extends RelativeLayout
setBatchState(false);
setRippleColor(contact);
contactPhotoImage.setAvatar(glideRequests, recipient, true);
contactPhotoImage.setAvatar(glideRequests, recipient.get(), true);
}
public void bind(@NonNull MessageResult messageResult,
@ -219,13 +223,15 @@ public class ConversationListItem extends RelativeLayout
@NonNull Locale locale,
@Nullable String highlightSubstring)
{
if (this.recipient != null) this.recipient.removeForeverObserver(this);
this.selectedThreads = Collections.emptySet();
this.recipient = messageResult.conversationRecipient;
this.recipient = messageResult.conversationRecipient.live();
this.glideRequests = glideRequests;
this.recipient.addListener(this);
this.recipient.observeForever(this);
fromView.setText(recipient, true);
fromView.setText(recipient.get(), true);
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), messageResult.bodySnippet, highlightSubstring));
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.receivedTimestampMs));
archivedView.setVisibility(GONE);
@ -235,14 +241,14 @@ public class ConversationListItem extends RelativeLayout
thumbnailView.setVisibility(GONE);
setBatchState(false);
setRippleColor(recipient);
contactPhotoImage.setAvatar(glideRequests, recipient, true);
setRippleColor(recipient.get());
contactPhotoImage.setAvatar(glideRequests, recipient.get(), true);
}
@Override
public void unbind() {
if (this.recipient != null) {
this.recipient.removeListener(this);
this.recipient.removeForeverObserver(this);
this.recipient = null;
contactPhotoImage.setAvatar(glideRequests, null, true);
}
@ -253,7 +259,7 @@ public class ConversationListItem extends RelativeLayout
}
public Recipient getRecipient() {
return recipient;
return recipient.get();
}
public long getThreadId() {
@ -339,14 +345,10 @@ public class ConversationListItem extends RelativeLayout
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> {
if (this.recipient == recipient) {
fromView.setText(recipient, unreadCount == 0);
contactPhotoImage.setAvatar(glideRequests, recipient, true);
setRippleColor(recipient);
}
});
public void onRecipientChanged(@NonNull Recipient recipient) {
fromView.setText(recipient, unreadCount == 0);
contactPhotoImage.setAvatar(glideRequests, recipient, true);
setRippleColor(recipient);
}
private static class ThumbnailPositioner implements Runnable {

View File

@ -60,6 +60,7 @@ import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -260,7 +261,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
startActivity(intent);
finish();
}
@ -286,9 +287,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
List<String> selected = data.getStringArrayListExtra("contacts");
for (String contact : selected) {
Address address = Address.fromExternal(this, contact);
Recipient recipient = Recipient.from(this, address, false);
Recipient recipient = Recipient.external(this, contact);
addSelectedContacts(recipient);
}
break;
@ -338,16 +337,17 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected GroupActionResult doInBackground(Void... avoid) {
List<Address> memberAddresses = new LinkedList<>();
List<RecipientId> memberAddresses = new LinkedList<>();
for (Recipient recipient : members) {
memberAddresses.add(recipient.getAddress());
memberAddresses.add(recipient.getId());
}
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(activity)));
memberAddresses.add(Recipient.self().getId());
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
Recipient groupRecipient = Recipient.from(activity, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(activity).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
return new GroupActionResult(groupRecipient, threadId);
}
@ -450,7 +450,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getAddress());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().requireAddress());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
@ -493,7 +493,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
recipient.toShortString())));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) {
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.requireAddress().serialize())) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
} else {
results.add(new Result(recipient, isPush, null));

View File

@ -32,7 +32,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
@Override
protected List<Recipient> doInBackground(Void... params) {
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), true);
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireAddress().toGroupString(), true);
}
@Override
@ -66,7 +66,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
if (recipient.getContactUri() != null) {
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
context.startActivity(intent);
} else {
@ -123,7 +123,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
}
private boolean isLocalNumber(Recipient recipient) {
return Util.isOwnNumber(context, recipient.getAddress());
return Util.isOwnNumber(context, recipient.requireAddress());
}
}
}

View File

@ -75,11 +75,10 @@ public class IncomingMessageProcessor {
public void processEnvelope(@NonNull SignalServiceEnvelope envelope) {
if (envelope.hasSource()) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
Recipient recipient = Recipient.external(context, envelope.getSource());
if (!isActiveNumber(recipient)) {
recipientDatabase.setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
recipientDatabase.setRegistered(recipient.getId(), RecipientDatabase.RegisteredState.REGISTERED);
jobManager.add(new DirectoryRefreshJob(recipient, false));
}
}
@ -101,9 +100,8 @@ public class IncomingMessageProcessor {
private void processReceipt(@NonNull SignalServiceEnvelope envelope) {
Log.i(TAG, String.format(Locale.ENGLISH, "Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()),
envelope.getTimestamp()),
System.currentTimeMillis());
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, envelope.getSource()).getId(), envelope.getTimestamp()),
System.currentTimeMillis());
}
private boolean isActiveNumber(@NonNull Recipient recipient) {

View File

@ -217,13 +217,13 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (context == null) return null;
for (String number : numbers) {
Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false);
Recipient recipient = Recipient.external(context, number);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);
if (recipient.getContactUri() != null) {
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient, true);
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient.getId(), true);
}
}

View File

@ -51,7 +51,6 @@ import android.widget.Toast;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
@ -59,14 +58,15 @@ import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.Buc
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -83,15 +83,15 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@SuppressWarnings("unused")
private final static String TAG = MediaOverviewActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String RECIPIENT_EXTRA = "recipient_id";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
private Recipient recipient;
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
private LiveRecipient recipient;
@Override
protected void onPreCreate() {
@ -129,21 +129,19 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
}
private void initializeResources() {
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
RecipientId recipientId = getIntent().getParcelableExtra(RECIPIENT_EXTRA);
this.viewPager = ViewUtil.findById(this, R.id.pager);
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
this.tabLayout = ViewUtil.findById(this, R.id.tab_layout);
this.recipient = Recipient.from(this, address, true);
this.recipient = Recipient.live(recipientId);
}
private void initializeToolbar() {
setSupportActionBar(this.toolbar);
getSupportActionBar().setTitle(recipient.toShortString());
getSupportActionBar().setTitle(recipient.get().toShortString());
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
this.recipient.addListener(recipient -> {
Util.runOnMain(() -> getSupportActionBar().setTitle(recipient.toShortString()));
});
this.recipient.observe(this, recipient -> getSupportActionBar().setTitle(recipient.toShortString()));
}
public void onEnterMultiSelect() {
@ -171,7 +169,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
else throw new AssertionError();
Bundle args = new Bundle();
args.putString(MediaOverviewGalleryFragment.ADDRESS_EXTRA, recipient.getAddress().serialize());
args.putParcelable(MediaOverviewGalleryFragment.RECIPIENT_EXTRA, recipient.getId());
args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, dynamicLanguage.getCurrentLocale());
fragment.setArguments(args);
@ -194,8 +192,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
public static abstract class MediaOverviewFragment<T> extends Fragment implements LoaderManager.LoaderCallbacks<T> {
public static final String ADDRESS_EXTRA = "address";
public static final String LOCALE_EXTRA = "locale_extra";
public static final String RECIPIENT_EXTRA = "recipient_id";
public static final String LOCALE_EXTRA = "locale_extra";
protected TextView noMedia;
protected Recipient recipient;
@ -206,13 +204,13 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
String address = getArguments().getString(ADDRESS_EXTRA);
Locale locale = (Locale)getArguments().getSerializable(LOCALE_EXTRA);
RecipientId recipientId = getArguments().getParcelable(RECIPIENT_EXTRA);
Locale locale = (Locale)getArguments().getSerializable(LOCALE_EXTRA);
if (address == null) throw new AssertionError();
if (recipientId == null) throw new AssertionError();
if (locale == null) throw new AssertionError();
this.recipient = Recipient.from(getContext(), Address.fromSerialized(address), true);
this.recipient = Recipient.live(recipientId).get();
this.locale = locale;
getLoaderManager().initLoader(0, null, this);
@ -258,7 +256,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@Override
public @NonNull Loader<BucketedThreadMedia> onCreateLoader(int i, Bundle bundle) {
return new BucketedThreadMediaLoader(getContext(), recipient.getAddress());
return new BucketedThreadMediaLoader(getContext(), recipient.getId());
}
@Override
@ -308,7 +306,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, true);
@ -497,13 +495,13 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ThreadMediaLoader(getContext(), recipient.getAddress(), false);
return new ThreadMediaLoader(requireContext(), recipient.getId(), false);
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
((CursorRecyclerViewAdapter)this.recyclerView.getAdapter()).changeCursor(data);
getActivity().invalidateOptionsMenu();
requireActivity().invalidateOptionsMenu();
this.noMedia.setVisibility(data.getCount() > 0 ? View.GONE : View.VISIBLE);
}

View File

@ -63,12 +63,11 @@ import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashMap;
import java.util.Locale;
@ -78,15 +77,14 @@ import java.util.Map;
* Activity for displaying media attachments in-app
*/
public final class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
implements RecipientModifiedListener,
LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>>,
implements LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>>,
MediaRailAdapter.RailItemListener,
MediaPreviewFragment.Events
{
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String RECIPIENT_EXTRA = "recipient_id";
public static final String DATE_EXTRA = "date";
public static final String SIZE_EXTRA = "size";
public static final String CAPTION_EXTRA = "caption";
@ -138,11 +136,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(this::initializeActionBar);
}
@Override
public void onRailItemClicked(int distanceFromActive) {
mediaPager.setCurrentItem(mediaPager.getCurrentItem() + distanceFromActive);
@ -223,7 +216,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
}
private void initializeResources() {
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
RecipientId recipientId = getIntent().getParcelableExtra(RECIPIENT_EXTRA);
initialMediaUri = getIntent().getData();
initialMediaType = getIntent().getType();
@ -232,8 +225,8 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
leftIsRecent = getIntent().getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
restartItem = -1;
if (address != null) {
conversationRecipient = Recipient.from(this, address, true);
if (recipientId != null) {
conversationRecipient = Recipient.live(recipientId).get();
} else {
conversationRecipient = null;
}
@ -305,7 +298,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
private void showOverview() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, conversationRecipient.getId());
startActivity(intent);
}
@ -491,7 +484,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
if (adapter != null) {
MediaItem item = adapter.getMediaItemFor(position);
if (item.recipient != null) item.recipient.addListener(MediaPreviewActivity.this);
if (item.recipient != null) item.recipient.live().observe(MediaPreviewActivity.this, r -> initializeActionBar());
viewModel.setActiveAlbumRailItem(MediaPreviewActivity.this, position);
initializeActionBar();
}
@ -504,7 +497,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
if (adapter != null) {
MediaItem item = adapter.getMediaItemFor(position);
if (item.recipient != null) item.recipient.removeListener(MediaPreviewActivity.this);
if (item.recipient != null) item.recipient.live().removeObservers(MediaPreviewActivity.this);
adapter.pause(position);
}
@ -682,11 +675,11 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position));
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
Address address = mediaRecord.getAddress();
RecipientId recipientId = mediaRecord.getRecipientId();
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
return new MediaItem(address != null ? Recipient.from(context, address,true) : null,
return new MediaItem(Recipient.live(recipientId).get(),
mediaRecord.getAttachment(),
mediaRecord.getAttachment().getDataUri(),
mediaRecord.getContentType(),

View File

@ -52,8 +52,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
@ -73,14 +73,14 @@ import java.util.Locale;
/**
* @author Jake McGinty
*/
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener {
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor> {
private final static String TAG = MessageDetailsActivity.class.getSimpleName();
public final static String MESSAGE_ID_EXTRA = "message_id";
public final static String THREAD_ID_EXTRA = "thread_id";
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
public final static String TYPE_EXTRA = "type";
public final static String ADDRESS_EXTRA = "address";
public static final String MESSAGE_ID_EXTRA = "message_id";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_PUSH_GROUP_EXTRA = "is_push_group";
public static final String TYPE_EXTRA = "type";
public static final String RECIPIENT_EXTRA = "recipient_id";
private GlideRequests glideRequests;
private long threadId;
@ -149,10 +149,10 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipient.addListener(this);
LiveRecipient recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
recipient.observe(this, r -> setActionBarColor(r.getColor()));
setActionBarColor(recipient.getColor());
setActionBarColor(recipient.get().getColor());
}
private void setActionBarColor(MaterialColor color) {
@ -164,11 +164,6 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
}
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> setActionBarColor(recipient.getColor()));
}
private void initializeResources() {
inflater = LayoutInflater.from(this);
View header = inflater.inflate(R.layout.message_details_header, recipientsList, false);
@ -366,20 +361,20 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
List<RecipientDeliveryStatus> recipients = new LinkedList<>();
if (!messageRecord.getRecipient().isGroupRecipient()) {
if (!messageRecord.getRecipient().isGroup()) {
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1));
} else {
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
if (receiptInfoList.isEmpty()) {
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireAddress().toGroupString(), false);
for (Recipient recipient : group) {
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
}
} else {
for (GroupReceiptInfo info : receiptInfoList) {
recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
recipients.add(new RecipientDeliveryStatus(Recipient.resolved(info.getRecipientId()),
getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
info.isUnidentified(),
info.getTimestamp()));

View File

@ -49,7 +49,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
@Override
public long getItemId(int position) {
try {
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.getAddress().serialize().getBytes()));
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.requireAddress().serialize().getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}

View File

@ -25,6 +25,8 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
@ -34,9 +36,8 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
/**
* A simple view to show the recipients of a message
@ -44,7 +45,7 @@ import org.thoughtcrime.securesms.util.Util;
* @author Jake McGinty
*/
public class MessageRecipientListItem extends RelativeLayout
implements RecipientModifiedListener
implements RecipientForeverObserver
{
@SuppressWarnings("unused")
private final static String TAG = MessageRecipientListItem.class.getSimpleName();
@ -84,10 +85,12 @@ public class MessageRecipientListItem extends RelativeLayout
final RecipientDeliveryStatus member,
final boolean isPushGroup)
{
if (this.member != null) this.member.getRecipient().live().removeForeverObserver(this);
this.glideRequests = glideRequests;
this.member = member;
member.getRecipient().addListener(this);
member.getRecipient().live().observeForever(this);
fromView.setText(member.getRecipient());
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
setIssueIndicators(record, isPushGroup);
@ -138,7 +141,7 @@ public class MessageRecipientListItem extends RelativeLayout
private NetworkFailure getNetworkFailure(final MessageRecord record) {
if (record.hasNetworkFailures()) {
for (final NetworkFailure failure : record.getNetworkFailures()) {
if (failure.getAddress().equals(member.getRecipient().getAddress())) {
if (failure.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
return failure;
}
}
@ -149,7 +152,7 @@ public class MessageRecipientListItem extends RelativeLayout
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
if (record.isIdentityMismatchFailure()) {
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
if (mismatch.getAddress().equals(member.getRecipient().getAddress())) {
if (mismatch.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
return mismatch;
}
}
@ -158,14 +161,12 @@ public class MessageRecipientListItem extends RelativeLayout
}
public void unbind() {
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().removeListener(this);
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().live().removeForeverObserver(this);
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> {
fromView.setText(recipient);
contactPhotoImage.setAvatar(glideRequests, recipient, false);
});
public void onRecipientChanged(@NonNull Recipient recipient) {
fromView.setText(recipient);
contactPhotoImage.setAvatar(glideRequests, recipient, false);
}
}

View File

@ -51,10 +51,10 @@ public class NewConversationActivity extends ContactSelectionActivity
@Override
public void onContactSelected(String number) {
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
Recipient recipient = Recipient.external(this, number);
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
intent.setDataAndType(getIntent().getData(), getIntent().getType());

View File

@ -67,8 +67,9 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
import org.thoughtcrime.securesms.preferences.widgets.ContactPreference;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.Dialogs;
@ -78,7 +79,6 @@ import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
@ -86,11 +86,11 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.ExecutionException;
@SuppressLint("StaticFieldLeak")
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<Cursor>
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor>
{
private static final String TAG = RecipientPreferenceActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "recipient_address";
public static final String RECIPIENT_ID = "recipient_address";
public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number";
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
@ -109,7 +109,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private ImageView avatar;
private GlideRequests glideRequests;
private Address address;
private RecipientId recipientId;
private TextView threadPhotoRailLabel;
private ThreadPhotoRailView threadPhotoRailView;
private CollapsingToolbarLayout toolbarLayout;
@ -124,13 +124,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public void onCreate(Bundle instanceState, boolean ready) {
setContentView(R.layout.recipient_preference_activity);
this.glideRequests = GlideApp.with(this);
this.address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
this.recipientId = getIntent().getParcelableExtra(RECIPIENT_ID);
Recipient recipient = Recipient.from(this, address, true);
LiveRecipient recipient = Recipient.live(recipientId);
initializeToolbar();
setHeader(recipient);
recipient.addListener(this);
setHeader(recipient.get());
recipient.observe(this, this::setHeader);
getSupportLoaderManager().initLoader(0, null, this);
}
@ -178,7 +178,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.threadPhotoRailView.setListener(mediaRecord -> {
Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class);
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address);
intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipientId);
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
@ -190,7 +190,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.threadPhotoRailLabel.setOnClickListener(v -> {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, address);
intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, recipientId);
startActivity(intent);
});
@ -206,7 +206,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
private void setHeader(@NonNull Recipient recipient) {
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.requireAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
: recipient.getContactPhoto();
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large)
: recipient.getFallbackContactPhoto();
@ -225,14 +225,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.toolbarLayout.setContentScrimColor(recipient.getColor().toActionBarColor(this));
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> setHeader(recipient));
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ThreadMediaLoader(this, address, true);
return new ThreadMediaLoader(this, recipientId, true);
}
@Override
@ -248,7 +243,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.threadPhotoRailView.setCursor(glideRequests, data);
Bundle bundle = new Bundle();
bundle.putParcelable(ADDRESS_EXTRA, address);
bundle.putParcelable(RECIPIENT_ID, recipientId);
initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), null, bundle);
}
@ -257,12 +252,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.threadPhotoRailView.setCursor(glideRequests, null);
}
public static class RecipientPreferenceFragment
extends CorrectedPreferenceFragment
implements RecipientModifiedListener
{
private Recipient recipient;
private boolean canHaveSafetyNumber;
public static class RecipientPreferenceFragment extends CorrectedPreferenceFragment {
private LiveRecipient recipient;
private boolean canHaveSafetyNumber;
@Override
public void onCreate(Bundle icicle) {
@ -277,20 +269,20 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
Preference customNotificationsPref = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS);
if (NotificationChannels.supported()) {
((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.getNotificationChannel() != null);
((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.get().getNotificationChannel() != null);
customNotificationsPref.setOnPreferenceChangeListener(new CustomNotificationsChangedListener());
this.findPreference(PREFERENCE_MESSAGE_TONE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS);
this.findPreference(PREFERENCE_MESSAGE_VIBRATE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS);
if (recipient.getNotificationChannel() != null) {
final Context context = getContext();
if (recipient.get().getNotificationChannel() != null) {
final Context context = requireContext();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(getContext());
db.setMessageRingtone(recipient, NotificationChannels.getMessageRingtone(context, recipient));
db.setMessageVibrate(recipient, NotificationChannels.getMessageVibrate(context, recipient) ? VibrateState.ENABLED : VibrateState.DISABLED);
db.setMessageRingtone(recipient.getId(), NotificationChannels.getMessageRingtone(context, recipient.get()));
db.setMessageVibrate(recipient.getId(), NotificationChannels.getMessageVibrate(context, recipient.get()) ? VibrateState.ENABLED : VibrateState.DISABLED);
NotificationChannels.ensureCustomChannelConsistency(context);
return null;
}
@ -336,13 +328,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public void onResume() {
super.onResume();
setSummaries(recipient);
setSummaries(recipient.get());
}
@Override
public void onDestroy() {
super.onDestroy();
this.recipient.removeListener(this);
}
@Override
@ -359,8 +350,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
private void initializeRecipients() {
this.recipient = Recipient.from(getActivity(), getArguments().getParcelable(ADDRESS_EXTRA), true);
this.recipient.addListener(this);
this.recipient = Recipient.live(getArguments().getParcelable(RECIPIENT_ID));
this.recipient.observe(this, this::setSummaries);
}
private void setSummaries(Recipient recipient) {
@ -406,7 +397,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
if (privacyCategory != null) privacyCategory.setVisible(false);
if (divider != null) divider.setVisible(false);
if (callCategory != null) callCategory.setVisible(false);
} if (recipient.isGroupRecipient()) {
} if (recipient.isGroup()) {
if (colorPreference != null) colorPreference.setVisible(false);
if (identityPreference != null) identityPreference.setVisible(false);
if (callCategory != null) callCategory.setVisible(false);
@ -417,7 +408,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity()));
aboutPreference.setTitle(formatAddress(recipient.getAddress()));
aboutPreference.setTitle(formatAddress(recipient.requireAddress()));
aboutPreference.setSummary(recipient.getCustomLabel());
aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED);
@ -478,15 +469,6 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> {
if (getContext() != null && getActivity() != null && !getActivity().isFinishing()) {
setSummaries(recipient);
}
});
}
private class RingtoneChangeListener implements Preference.OnPreferenceChangeListener {
private final boolean calls;
@ -514,10 +496,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
protected Void doInBackground(Uri... params) {
if (calls) {
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient, params[0]);
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient.getId(), params[0]);
} else {
DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient, params[0]);
NotificationChannels.updateMessageRingtone(context, recipient, params[0]);
DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient.getId(), params[0]);
NotificationChannels.updateMessageRingtone(context, recipient.get(), params[0]);
}
return null;
}
@ -541,10 +523,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
Uri defaultUri;
if (calls) {
current = recipient.getCallRingtone();
current = recipient.get().getCallRingtone();
defaultUri = TextSecurePreferences.getCallNotificationRingtone(getContext());
} else {
current = recipient.getMessageRingtone();
current = recipient.get().getMessageRingtone();
defaultUri = TextSecurePreferences.getNotificationRingtone(getContext());
}
@ -582,11 +564,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
protected Void doInBackground(Void... params) {
if (call) {
DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient, vibrateState);
DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient.getId(), vibrateState);
}
else {
DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient, vibrateState);
NotificationChannels.updateMessageVibrate(context, recipient, vibrateState);
DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient.getId(), vibrateState);
NotificationChannels.updateMessageVibrate(context, recipient.get(), vibrateState);
}
return null;
}
@ -605,7 +587,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
final int value = (Integer) newValue;
final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(context, value);
final MaterialColor currentColor = recipient.getColor();
final MaterialColor currentColor = recipient.get().getColor();
if (selectedColor == null) return true;
@ -613,12 +595,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor);
DatabaseFactory.getRecipientDatabase(context).setColor(recipient.getId(), selectedColor);
if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
if (recipient.get().resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
.add(new MultiDeviceContactUpdateJob(recipient.getId()));
}
return null;
}
@ -632,30 +614,28 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public boolean onPreferenceClick(Preference preference) {
if (recipient.isMuted()) handleUnmute(preference.getContext());
else handleMute(preference.getContext());
if (recipient.get().isMuted()) handleUnmute(preference.getContext());
else handleMute(preference.getContext());
return true;
}
private void handleMute(@NonNull Context context) {
MuteDialog.show(context, until -> setMuted(context, recipient, until));
MuteDialog.show(context, until -> setMuted(context, recipient.get(), until));
setSummaries(recipient);
setSummaries(recipient.get());
}
private void handleUnmute(@NonNull Context context) {
setMuted(context, recipient, 0);
setMuted(context, recipient.get(), 0);
}
private void setMuted(@NonNull final Context context, final Recipient recipient, final long until) {
recipient.setMuted(until);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(context)
.setMuted(recipient, until);
.setMuted(recipient.getId(), until);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -674,7 +654,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public boolean onPreferenceClick(Preference preference) {
Intent verifyIdentityIntent = new Intent(preference.getContext(), VerifyIdentityActivity.class);
verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress());
verifyIdentityIntent.putExtra(VerifyIdentityActivity.RECIPIENT_EXTRA, recipient.getId());
verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
startActivity(verifyIdentityIntent);
@ -686,8 +666,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private class BlockClickedListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
if (recipient.isBlocked()) handleUnblock(preference.getContext());
else handleBlock(preference.getContext());
if (recipient.get().isBlocked()) handleUnblock(preference.getContext());
else handleBlock(preference.getContext());
return true;
}
@ -700,10 +680,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question;
int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact;
if (recipient.isGroupRecipient()) {
if (recipient.get().isGroup()) {
bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description;
if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) {
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireAddress().toGroupString())) {
titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group;
} else {
titleRes = R.string.RecipientPreferenceActivity_block_group;
@ -721,7 +701,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> {
setBlocked(context, recipient, true);
setBlocked(context, recipient.get(), true);
}).show();
}
}.execute();
@ -731,7 +711,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
int titleRes = R.string.RecipientPreferenceActivity_unblock_this_contact_question;
int bodyRes = R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
if (recipient.isGroupRecipient()) {
if (recipient.resolve().isGroup()) {
titleRes = R.string.RecipientPreferenceActivity_unblock_this_group_question;
bodyRes = R.string.RecipientPreferenceActivity_unblock_this_group_description;
}
@ -741,7 +721,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
.setMessage(bodyRes)
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient, false)).show();
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient.get(), false)).show();
}
private void setBlocked(@NonNull final Context context, final Recipient recipient, final boolean blocked) {
@ -749,9 +729,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(context)
.setBlocked(recipient, blocked);
.setBlocked(recipient.getId(), blocked);
if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) {
if (recipient.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireAddress().toGroupString())) {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
@ -759,9 +739,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
String groupId = recipient.getAddress().toGroupString();
String groupId = recipient.requireAddress().toGroupString();
groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.remove(groupId, Recipient.self().getId());
} else {
Log.w(TAG, "Failed to leave group. Can't block.");
Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show();
@ -787,19 +767,19 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public void onMessageClicked() {
CommunicationActions.startConversation(getContext(), recipient, null);
CommunicationActions.startConversation(getContext(), recipient.get(), null);
}
@Override
public void onSecureCallClicked() {
CommunicationActions.startVoiceCall(getActivity(), recipient);
CommunicationActions.startVoiceCall(getActivity(), recipient.get());
}
@Override
public void onInSecureCallClicked() {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.getAddress().serialize()));
Uri.parse("tel:" + recipient.get().requireAddress().serialize()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);
@ -821,11 +801,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
protected Void doInBackground(Void... params) {
if (enabled) {
String channel = NotificationChannels.createChannelFor(context, recipient);
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, channel);
String channel = NotificationChannels.createChannelFor(context, recipient.get());
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.getId(), channel);
} else {
NotificationChannels.deleteChannelFor(context, recipient);
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, null);
NotificationChannels.deleteChannelFor(context, recipient.get());
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.getId(), null);
}
return null;
}

View File

@ -71,6 +71,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.CaptchaActivity;
import org.thoughtcrime.securesms.registration.PushChallengeRequest;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
@ -735,7 +736,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true);
DatabaseFactory.getIdentityDatabase(RegistrationActivity.this)
.saveIdentity(Address.fromSerialized(registrationState.e164number),
.saveIdentity(Recipient.external(RegistrationActivity.this, registrationState.e164number).getId(),
identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED,
true, System.currentTimeMillis(), true);

View File

@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
@ -55,6 +56,8 @@ import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.libsignal.util.Pair;
import java.io.FileInputStream;
import java.io.IOException;
@ -72,7 +75,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private static final String TAG = ShareActivity.class.getSimpleName();
public static final String EXTRA_THREAD_ID = "thread_id";
public static final String EXTRA_ADDRESS_MARSHALLED = "address_marshalled";
public static final String EXTRA_RECIPIENT_ID = "recipient_id";
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
@ -216,35 +219,30 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
}
private void handleResolvedMedia(Intent intent, boolean animate) {
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
Address address = null;
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
RecipientId recipientId = null;
if (intent.hasExtra(EXTRA_ADDRESS_MARSHALLED)) {
Parcel parcel = Parcel.obtain();
byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESS_MARSHALLED);
parcel.unmarshall(marshalled, 0, marshalled.length);
parcel.setDataPosition(0);
address = parcel.readParcelable(getClassLoader());
parcel.recycle();
if (intent.hasExtra(EXTRA_RECIPIENT_ID)) {
recipientId = RecipientId.from(intent.getStringExtra(EXTRA_RECIPIENT_ID));
}
boolean hasResolvedDestination = threadId != -1 && address != null && distributionType != -1;
boolean hasResolvedDestination = threadId != -1 && recipientId != null && distributionType != -1;
if (!hasResolvedDestination && animate) {
ViewUtil.fadeIn(contactsFragment.getView(), 300);
if (hasResolvedDestination) {
createConversation(threadId, recipientId, distributionType);
} else if (animate) {
ViewUtil.fadeIn(contactsFragment.requireView(), 300);
ViewUtil.fadeOut(progressWheel, 300);
} else if (!hasResolvedDestination) {
contactsFragment.getView().setVisibility(View.VISIBLE);
progressWheel.setVisibility(View.GONE);
} else {
createConversation(threadId, address, distributionType);
contactsFragment.requireView().setVisibility(View.VISIBLE);
progressWheel.setVisibility(View.GONE);
}
}
private void createConversation(long threadId, Address address, int distributionType) {
private void createConversation(long threadId, @NonNull RecipientId recipientId, int distributionType) {
final Intent intent = getBaseShareIntent(ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipientId);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
@ -277,9 +275,14 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
public void onContactSelected(String number) {
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
SimpleTask.run(this.getLifecycle(), () -> {
Recipient recipient = Recipient.external(this, number);
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
return new Pair<>(existingThread, recipient);
}, result -> {
createConversation(result.first(), result.second().getId(), ThreadDatabase.DistributionTypes.DEFAULT);
});
}
@Override

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@ -10,38 +9,36 @@ import androidx.core.app.TaskStackBuilder;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.CommunicationActions;
public class ShortcutLauncherActivity extends AppCompatActivity {
private static final String KEY_SERIALIZED_ADDRESS = "serialized_address";
private static final String KEY_RECIPIENT = "recipient_id";
public static Intent createIntent(@NonNull Context context, @NonNull Address address) {
public static Intent createIntent(@NonNull Context context, @NonNull RecipientId recipientId) {
Intent intent = new Intent(context, ShortcutLauncherActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.putExtra(KEY_SERIALIZED_ADDRESS, address.serialize());
intent.putExtra(KEY_RECIPIENT, recipientId.serialize());
return intent;
}
@SuppressLint("StaticFieldLeak")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String serializedAddress = getIntent().getStringExtra(KEY_SERIALIZED_ADDRESS);
String rawId = getIntent().getStringExtra(KEY_RECIPIENT);
if (serializedAddress == null) {
if (rawId == null) {
Toast.makeText(this, R.string.ShortcutLauncherActivity_invalid_shortcut, Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, ConversationListActivity.class));
finish();
return;
}
Address address = Address.fromSerialized(serializedAddress);
Recipient recipient = Recipient.from(this, address, true);
Recipient recipient = Recipient.live(RecipientId.from(rawId)).get();
TaskStackBuilder backStack = TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, ConversationListActivity.class));

View File

@ -48,13 +48,13 @@ public class SmsSendtoActivity extends Activity {
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
} else {
Recipient recipient = Recipient.from(this, Address.fromExternal(this, destination.getDestination()), true);
Recipient recipient = Recipient.external(this, destination.getDestination());
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
nextIntent = new Intent(this, ConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
nextIntent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
nextIntent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
}
return nextIntent;
}

View File

@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.camera.CameraView;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
@ -72,8 +71,9 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.QrCode;
import org.thoughtcrime.securesms.qr.ScanListener;
import org.thoughtcrime.securesms.qr.ScanningThread;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IdentityUtil;
@ -98,13 +98,13 @@ import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
* @author Moxie Marlinspike
*/
@SuppressLint("StaticFieldLeak")
public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener {
public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements ScanListener, View.OnClickListener {
private static final String TAG = VerifyIdentityActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String IDENTITY_EXTRA = "recipient_identity";
public static final String VERIFIED_EXTRA = "verified_state";
public static final String RECIPIENT_EXTRA = "recipient_id";
public static final String IDENTITY_EXTRA = "recipient_identity";
public static final String VERIFIED_EXTRA = "verified_state";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -123,15 +123,14 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number);
Recipient recipient = Recipient.from(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipient.addListener(this);
LiveRecipient recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
recipient.observe(this, r -> setActionBarNotificationBarColor(r.getColor()));
setActionBarNotificationBarColor(recipient.getColor());
setActionBarNotificationBarColor(recipient.get().getColor());
Bundle extras = new Bundle();
extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA));
extras.putParcelable(VerifyDisplayFragment.RECIPIENT_ID, getIntent().getParcelableExtra(RECIPIENT_EXTRA));
extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA));
extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString());
extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this));
extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false));
@ -151,11 +150,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
return false;
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> setActionBarNotificationBarColor(recipient.getColor()));
}
@Override
public void onQrDataFound(final String data) {
Util.runOnMain(() -> {
@ -198,18 +192,18 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
}
}
public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener {
public static class VerifyDisplayFragment extends Fragment implements CompoundButton.OnCheckedChangeListener {
public static final String REMOTE_ADDRESS = "remote_address";
public static final String RECIPIENT_ID = "recipient_id";
public static final String REMOTE_NUMBER = "remote_number";
public static final String REMOTE_IDENTITY = "remote_identity";
public static final String LOCAL_IDENTITY = "local_identity";
public static final String LOCAL_NUMBER = "local_number";
public static final String VERIFIED_STATE = "verified_state";
private Recipient recipient;
private String localNumber;
private String remoteNumber;
private LiveRecipient recipient;
private String localNumber;
private String remoteNumber;
private IdentityKey localIdentity;
private IdentityKey remoteIdentity;
@ -264,21 +258,21 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Address address = getArguments().getParcelable(REMOTE_ADDRESS);
RecipientId recipientId = getArguments().getParcelable(RECIPIENT_ID);
IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY);
IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY);
if (address == null) throw new AssertionError("Address required");
if (recipientId == null) throw new AssertionError("RecipientId required");
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
this.localNumber = getArguments().getString(LOCAL_NUMBER);
this.localIdentity = localIdentityParcelable.get();
this.remoteNumber = getArguments().getString(REMOTE_NUMBER);
this.recipient = Recipient.from(getActivity(), address, true);
this.recipient = Recipient.live(recipientId);
this.remoteNumber = recipient.get().requireAddress().serialize();
this.remoteIdentity = remoteIdentityParcelable.get();
this.recipient.addListener(this);
this.recipient.observe(this, this::setRecipientText);
new AsyncTask<Void, Void, Fingerprint>() {
@Override
@ -298,16 +292,11 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
setHasOptionsMenu(true);
}
@Override
public void onModified(final Recipient recipient) {
Util.runOnMain(() -> setRecipientText(recipient));
}
@Override
public void onResume() {
super.onResume();
setRecipientText(recipient);
setRecipientText(recipient.get());
if (fingerprint != null) {
setFingerprintViews(fingerprint, false);
@ -322,12 +311,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
}
}
@Override
public void onDestroy() {
super.onDestroy();
recipient.removeListener(this);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view,
ContextMenuInfo menuInfo)
@ -587,31 +570,31 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Recipient... params) {
synchronized (SESSION_LOCK) {
if (isChecked) {
Log.i(TAG, "Saving identity: " + params[0].getAddress());
Log.i(TAG, "Saving identity: " + params[0].requireAddress());
DatabaseFactory.getIdentityDatabase(getActivity())
.saveIdentity(params[0].getAddress(),
.saveIdentity(params[0].getId(),
remoteIdentity,
VerifiedStatus.VERIFIED, false,
System.currentTimeMillis(), true);
} else {
DatabaseFactory.getIdentityDatabase(getActivity())
.setVerified(params[0].getAddress(),
.setVerified(params[0].getId(),
remoteIdentity,
VerifiedStatus.DEFAULT);
}
ApplicationContext.getInstance(getActivity())
.getJobManager()
.add(new MultiDeviceVerifiedUpdateJob(recipient.getAddress(),
.add(new MultiDeviceVerifiedUpdateJob(recipient.getId(),
remoteIdentity,
isChecked ? VerifiedStatus.VERIFIED :
VerifiedStatus.DEFAULT));
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
IdentityUtil.markIdentityVerified(getActivity(), recipient.get(), isChecked, false);
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
}
}

View File

@ -26,7 +26,6 @@ import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.logging.Log;
import android.view.View;
@ -47,7 +46,6 @@ import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SignalProtocolAddress;
@ -280,11 +278,11 @@ public class WebRtcCallActivity extends Activity {
public void onClick(View v) {
synchronized (SESSION_LOCK) {
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.requireAddress().serialize(), 1), theirIdentity, true);
}
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, recipient.getId());
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
startService(intent);
}

View File

@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.Util;
@ -162,7 +163,7 @@ public class FullBackupImporter extends FullBackupBase {
}
private static void processAvatar(@NonNull Context context, @NonNull BackupProtos.Avatar avatar, @NonNull BackupRecordInputStream inputStream) throws IOException {
inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, Address.fromExternal(context, avatar.getName()))), avatar.getLength());
inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, Address.fromSerialized(PhoneNumberFormatter.get(context).format(avatar.getName())))), avatar.getLength());
}
@SuppressLint("ApplySharedPref")

View File

@ -126,7 +126,7 @@ public final class AvatarImageView extends AppCompatImageView {
}
private void setAvatarClickHandler(final Recipient recipient, boolean quickContactEnabled) {
if (!recipient.isGroupRecipient() && quickContactEnabled) {
if (!recipient.isGroup() && quickContactEnabled) {
super.setOnClickListener(v -> {
if (recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);

View File

@ -171,7 +171,7 @@ public class InputPanel extends LinearLayout
public Optional<QuoteModel> getQuote() {
if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) {
return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getAddress(), quoteView.getBody(), false, quoteView.getAttachments()));
return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getId(), quoteView.getBody(), false, quoteView.getAttachments()));
} else {
return Optional.absent();
}

View File

@ -26,12 +26,14 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.RelativeLayout;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import java.util.LinkedList;
import java.util.List;
@ -43,7 +45,7 @@ import java.util.StringTokenizer;
*
* @author Moxie Marlinspike
*/
public class PushRecipientsPanel extends RelativeLayout implements RecipientModifiedListener {
public class PushRecipientsPanel extends RelativeLayout implements RecipientForeverObserver {
private final String TAG = PushRecipientsPanel.class.getSimpleName();
private RecipientsPanelChangedListener panelChangeListener;
@ -67,9 +69,15 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientModi
initialize();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Stream.of(getRecipients()).map(Recipient::live).forEach(r -> r.removeForeverObserver(this));
}
public List<Recipient> getRecipients() {
String rawText = recipientsText.getText().toString();
return getRecipientsFromString(getContext(), rawText, true);
return getRecipientsFromString(getContext(), rawText);
}
public void disable() {
@ -98,9 +106,7 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientModi
List<Recipient> recipients = getRecipients();
for (Recipient recipient : recipients) {
recipient.addListener(this);
}
Stream.of(recipients).map(Recipient::live).forEach(r -> r.observeForever(this));
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
recipientsText.populate(recipients);
@ -117,7 +123,7 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientModi
});
}
private @NonNull List<Recipient> getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
private @NonNull List<Recipient> getRecipientsFromString(Context context, @NonNull String rawText) {
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
List<Recipient> recipients = new LinkedList<>();
@ -125,8 +131,8 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientModi
String token = tokenizer.nextToken().trim();
if (!TextUtils.isEmpty(token)) {
if (hasBracketedNumber(token)) recipients.add(Recipient.from(context, Address.fromExternal(context, parseBracketedNumber(token)), asynchronous));
else recipients.add(Recipient.from(context, Address.fromExternal(context, token), asynchronous));
if (hasBracketedNumber(token)) recipients.add(Recipient.external(context, parseBracketedNumber(token)));
else recipients.add(Recipient.external(context, token));
}
}
@ -149,7 +155,7 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientModi
}
@Override
public void onModified(Recipient recipient) {
public void onRecipientChanged(@NonNull Recipient recipient) {
recipientsText.populate(getRecipients());
}

View File

@ -26,14 +26,15 @@ import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.List;
public class QuoteView extends FrameLayout implements RecipientModifiedListener {
public class QuoteView extends FrameLayout implements RecipientForeverObserver {
private static final String TAG = QuoteView.class.getSimpleName();
@ -52,16 +53,16 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
private TextView attachmentNameView;
private ImageView dismissView;
private long id;
private Recipient author;
private String body;
private TextView mediaDescriptionText;
private TextView missingLinkText;
private SlideDeck attachments;
private int messageType;
private int largeCornerRadius;
private int smallCornerRadius;
private CornerMask cornerMask;
private long id;
private LiveRecipient author;
private String body;
private TextView mediaDescriptionText;
private TextView missingLinkText;
private SlideDeck attachments;
private int messageType;
private int largeCornerRadius;
private int smallCornerRadius;
private CornerMask cornerMask;
public QuoteView(Context context) {
@ -137,6 +138,12 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
cornerMask.mask(canvas);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (author != null) author.removeForeverObserver(this);
}
public void setQuote(GlideRequests glideRequests,
long id,
@NonNull Recipient author,
@ -144,14 +151,14 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
boolean originalMissing,
@NonNull SlideDeck attachments)
{
if (this.author != null) this.author.removeListener(this);
if (this.author != null) this.author.removeForeverObserver(this);
this.id = id;
this.author = author;
this.author = author.live();
this.body = body;
this.attachments = attachments;
author.addListener(this);
this.author.observeForever(this);
setQuoteAuthor(author);
setQuoteText(body, attachments);
setQuoteAttachment(glideRequests, attachments);
@ -164,7 +171,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
}
public void dismiss() {
if (this.author != null) this.author.removeListener(this);
if (this.author != null) this.author.removeForeverObserver(this);
this.id = 0;
this.author = null;
@ -174,17 +181,13 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> {
if (recipient == author) {
setQuoteAuthor(recipient);
}
});
public void onRecipientChanged(@NonNull Recipient recipient) {
setQuoteAuthor(recipient);
}
private void setQuoteAuthor(@NonNull Recipient author) {
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress());
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.requireAddress());
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you)
: author.toShortString());
@ -259,7 +262,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
private void setQuoteMissingFooter(boolean missing) {
footerView.setVisibility(missing ? VISIBLE : GONE);
footerView.setBackgroundColor(author.getColor().toQuoteFooterColor(getContext(), messageType != MESSAGE_TYPE_INCOMING));
footerView.setBackgroundColor(author.get().getColor().toQuoteFooterColor(getContext(), messageType != MESSAGE_TYPE_INCOMING));
}
public long getQuoteId() {
@ -267,7 +270,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
}
public Recipient getAuthor() {
return author;
return author.get();
}
public String getBody() {

View File

@ -24,9 +24,10 @@ import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import java.util.ArrayList;
import java.util.Collections;
@ -35,7 +36,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
public class SharedContactView extends LinearLayout implements RecipientModifiedListener {
public class SharedContactView extends LinearLayout implements RecipientForeverObserver {
private ImageView avatarView;
private TextView nameView;
@ -51,7 +52,7 @@ public class SharedContactView extends LinearLayout implements RecipientModified
private int bigCornerRadius;
private int smallCornerRadius;
private final Map<String, Recipient> activeRecipients = new HashMap<>();
private final Map<RecipientId, LiveRecipient> activeRecipients = new HashMap<>();
public SharedContactView(Context context) {
super(context);
@ -114,12 +115,16 @@ public class SharedContactView extends LinearLayout implements RecipientModified
this.locale = locale;
this.contact = contact;
Stream.of(activeRecipients.values()).forEach(recipient -> recipient.removeListener(this));
Stream.of(activeRecipients.values()).forEach(recipient -> recipient.removeForeverObserver(this));
this.activeRecipients.clear();
presentContact(contact);
presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getDataUri() : null);
presentActionButtons(ContactUtil.getRecipients(getContext(), contact));
for (LiveRecipient recipient : activeRecipients.values()) {
recipient.observeForever(this);
}
}
public void setSingularStyle() {
@ -150,8 +155,8 @@ public class SharedContactView extends LinearLayout implements RecipientModified
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> presentActionButtons(Collections.singletonList(recipient)));
public void onRecipientChanged(@NonNull Recipient recipient) {
presentActionButtons(Collections.singletonList(recipient.getId()));
}
private void presentContact(@Nullable Contact contact) {
@ -180,21 +185,19 @@ public class SharedContactView extends LinearLayout implements RecipientModified
}
}
private void presentActionButtons(@NonNull List<Recipient> recipients) {
for (Recipient recipient : recipients) {
activeRecipients.put(recipient.getAddress().serialize(), recipient);
private void presentActionButtons(@NonNull List<RecipientId> recipients) {
for (RecipientId recipientId : recipients) {
activeRecipients.put(recipientId, Recipient.live(recipientId));
}
List<Recipient> pushUsers = new ArrayList<>(recipients.size());
List<Recipient> systemUsers = new ArrayList<>(recipients.size());
for (Recipient recipient : activeRecipients.values()) {
recipient.addListener(this);
if (recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
pushUsers.add(recipient);
} else if (recipient.isSystemContact()) {
systemUsers.add(recipient);
for (LiveRecipient recipient : activeRecipients.values()) {
if (recipient.get().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
pushUsers.add(recipient.get());
} else if (recipient.get().isSystemContact()) {
systemUsers.add(recipient.get());
}
}

View File

@ -43,7 +43,7 @@ public class TypingStatusRepository {
}
public synchronized void onTypingStarted(@NonNull Context context, long threadId, @NonNull Recipient author, int device) {
if (author.getAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
return;
}
@ -67,7 +67,7 @@ public class TypingStatusRepository {
}
public synchronized void onTypingStopped(@NonNull Context context, long threadId, @NonNull Recipient author, int device, boolean isReplacedByIncomingMessage) {
if (author.getAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
return;
}

View File

@ -46,7 +46,7 @@ public class UntrustedSendDialog extends AlertDialog.Builder implements DialogIn
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setApproval(identityRecord.getAddress(), true);
identityDatabase.setApproval(identityRecord.getRecipientId(), true);
}
}

View File

@ -45,7 +45,7 @@ public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogI
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setVerified(identityRecord.getAddress(),
identityDatabase.setVerified(identityRecord.getRecipientId(),
identityRecord.getIdentityKey(),
IdentityDatabase.VerifiedStatus.DEFAULT);
}

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.components.reminder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
@ -9,25 +10,19 @@ import android.view.View.OnClickListener;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
public class InviteReminder extends Reminder {
@SuppressLint("StaticFieldLeak")
public InviteReminder(final @NonNull Context context,
final @NonNull Recipient recipient)
{
super(context.getString(R.string.reminder_header_invite_title),
context.getString(R.string.reminder_header_invite_text, recipient.toShortString()));
setDismissListener(new OnClickListener() {
@Override public void onClick(View v) {
new AsyncTask<Void,Void,Void>() {
@Override protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient, true);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
setDismissListener(v -> SignalExecutors.BOUNDED.execute(() -> {
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient.getId(), true);
}));
}
}

View File

@ -41,9 +41,9 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.webrtc.CameraState;
@ -57,7 +57,7 @@ import org.whispersystems.libsignal.IdentityKey;
* @author Moxie Marlinspike
*
*/
public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedListener {
public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObserver {
@SuppressWarnings("unused")
private static final String TAG = WebRtcCallScreen.class.getSimpleName();
@ -82,8 +82,8 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
private WebRtcAnswerDeclineButton incomingCallButton;
private Recipient recipient;
private boolean minimized;
private LiveRecipient recipient;
private boolean minimized;
public WebRtcCallScreen(Context context) {
@ -124,14 +124,18 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
}
public void setUntrustedIdentity(Recipient personInfo, IdentityKey untrustedIdentity) {
String name = recipient.toShortString();
String name = recipient.get().toShortString();
String introduction = String.format(getContext().getString(R.string.WebRtcCallScreen_new_safety_numbers), name, name);
SpannableString spannableString = new SpannableString(introduction + " " + getContext().getString(R.string.WebRtcCallScreen_you_may_wish_to_verify_this_contact));
spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity),
spannableString.setSpan(new VerifySpan(getContext(), personInfo.getId(), untrustedIdentity),
introduction.length()+1, spannableString.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (this.recipient != null) this.recipient.removeForeverObserver(this);
this.recipient = personInfo.live();
this.recipient.observeForever(this);
setPersonInfo(personInfo);
incomingCallButton.stopRingingAnimation();
@ -294,9 +298,6 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
}
private void setPersonInfo(final @NonNull Recipient recipient) {
this.recipient = recipient;
this.recipient.addListener(this);
GlideApp.with(getContext().getApplicationContext())
.load(recipient.getContactPhoto())
.fallback(recipient.getFallbackContactPhoto().asCallCard(getContext()))
@ -307,14 +308,19 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
this.name.setText(recipient.getName());
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
this.phoneNumber.setText(recipient.getAddress().serialize() + " (~" + recipient.getProfileName() + ")");
this.phoneNumber.setText(recipient.requireAddress().serialize() + " (~" + recipient.getProfileName() + ")");
} else {
this.phoneNumber.setText(recipient.getAddress().serialize());
this.phoneNumber.setText(recipient.requireAddress().serialize());
}
}
private void setCard(Recipient recipient, String status) {
if (this.recipient != null) this.recipient.removeForeverObserver(this);
this.recipient = recipient.live();
this.recipient.observeForever(this);
setPersonInfo(recipient);
this.status.setText(status);
this.untrustedIdentityContainer.setVisibility(View.GONE);
}
@ -341,17 +347,11 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> {
if (recipient == WebRtcCallScreen.this.recipient) {
setPersonInfo(recipient);
}
});
public void onRecipientChanged(@NonNull Recipient recipient) {
setPersonInfo(recipient);
}
public interface HangupButtonListener {
void onClick();
}
}

View File

@ -29,10 +29,14 @@ import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.ArrayList;
@ -72,7 +76,7 @@ public class ContactAccessor {
try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) {
while (cursor != null && cursor.moveToNext()) {
if (!TextUtils.isEmpty(cursor.getString(0))) {
results.add(Address.fromExternal(context, cursor.getString(0)));
results.add(Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(0))));
}
}
}
@ -105,7 +109,7 @@ public class ContactAccessor {
final ContentResolver resolver = context.getContentResolver();
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
final List<Address> registeredAddresses = DatabaseFactory.getRecipientDatabase(context).getRegistered();
final List<Address> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered()).map(Recipient::resolved).map(Recipient::requireAddress).toList();
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
for (Address registeredAddress : registeredAddresses) {

View File

@ -13,13 +13,14 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
public class ContactSelectionListItem extends LinearLayout implements RecipientForeverObserver {
@SuppressWarnings("unused")
private static final String TAG = ContactSelectionListItem.class.getSimpleName();
@ -31,7 +32,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
private CheckBox checkBox;
private String number;
private Recipient recipient;
private LiveRecipient recipient;
private GlideRequests glideRequests;
public ContactSelectionListItem(Context context) {
@ -60,22 +61,22 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
if (type == ContactsDatabase.NEW_TYPE) {
this.recipient = null;
this.contactPhotoImage.setAvatar(glideRequests, Recipient.from(getContext(), Address.UNKNOWN, true), false);
this.contactPhotoImage.setAvatar(glideRequests, Recipient.UNKNOWN, false);
} else if (!TextUtils.isEmpty(number)) {
Address address = Address.fromExternal(getContext(), number);
this.recipient = Recipient.from(getContext(), address, true);
this.recipient.addListener(this);
// TODO [greyson] We really don't want to have to do a read like this here
this.recipient = Recipient.external(getContext(), number).live();
this.recipient.observeForever(this);
if (this.recipient.getName() != null) {
name = this.recipient.getName();
if (this.recipient.get().getName() != null) {
name = this.recipient.get().getName();
}
}
this.nameView.setTextColor(color);
this.numberView.setTextColor(color);
this.contactPhotoImage.setAvatar(glideRequests, recipient, false);
this.contactPhotoImage.setAvatar(glideRequests, recipient != null ? recipient.get() : null, false);
if (!multiSelect && recipient != null && recipient.isLocalNumber()) {
if (!multiSelect && recipient != null && recipient.get().isLocalNumber()) {
name = getContext().getString(R.string.note_to_self);
}
@ -91,7 +92,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
public void unbind(GlideRequests glideRequests) {
if (recipient != null) {
recipient.removeListener(this);
recipient.removeForeverObserver(this);
recipient = null;
}
}
@ -120,12 +121,8 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
}
@Override
public void onModified(final Recipient recipient) {
if (this.recipient == recipient) {
Util.runOnMain(() -> {
contactPhotoImage.setAvatar(glideRequests, recipient, false);
nameView.setText(recipient.toShortString());
});
}
public void onRecipientChanged(@NonNull Recipient recipient) {
contactPhotoImage.setAvatar(glideRequests, recipient, false);
nameView.setText(recipient.toShortString());
}
}

View File

@ -27,7 +27,6 @@ import androidx.loader.content.CursorLoader;
import android.text.TextUtils;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
@ -36,7 +35,7 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import java.util.ArrayList;
import java.util.List;
@ -170,7 +169,7 @@ public class ContactsCursorLoader extends CursorLoader {
ThreadRecord threadRecord;
while ((threadRecord = reader.getNext()) != null) {
recentConversations.addRow(new Object[] { threadRecord.getRecipient().toShortString(),
threadRecord.getRecipient().getAddress().serialize(),
threadRecord.getRecipient().requireAddress().serialize(),
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactsDatabase.RECENT_TYPE });
@ -230,7 +229,7 @@ public class ContactsCursorLoader extends CursorLoader {
final MatrixCursor matrix = new MatrixCursor(CONTACT_PROJECTION);
while (cursor.moveToNext()) {
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
final Recipient recipient = Recipient.from(getContext(), Address.fromExternal(getContext(), number), false);
final Recipient recipient = Recipient.external(getContext(), number);
if (recipient.resolve().getRegistered() != RecipientDatabase.RegisteredState.REGISTERED) {
matrix.addRow(new Object[]{cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)),

View File

@ -40,6 +40,7 @@ import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
@ -484,7 +485,7 @@ public class ContactsDatabase {
cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null);
while (cursor != null && cursor.moveToNext()) {
Address currentAddress = Address.fromExternal(context, cursor.getString(1));
Address currentAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(1)));
long rawContactId = cursor.getLong(0);
long contactId = cursor.getLong(3);
String supportsVoice = cursor.getString(2);
@ -518,7 +519,7 @@ public class ContactsDatabase {
while (numberCursor != null && numberCursor.moveToNext()) {
String systemNumber = numberCursor.getString(0);
Address systemAddress = Address.fromExternal(context, systemNumber);
Address systemAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(systemNumber));
if (systemAddress.equals(address)) {
idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI,

View File

@ -187,7 +187,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
public static CharSequence contactToToken(Recipient c) {
String name = c.getName();
String number = c.getAddress().serialize();
String number = c.requireAddress().serialize();
SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number));
int len = s.length();
@ -195,7 +195,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
return s;
}
s.setSpan(new Annotation("number", c.getAddress().serialize()), 0, len,
s.setSpan(new Annotation("number", c.requireAddress().serialize()), 0, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return s;

View File

@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -267,7 +268,7 @@ public class ContactRepository {
}
for (Phone phoneNumber : phoneNumbers) {
AvatarInfo recipientAvatar = getRecipientAvatarInfo(Address.fromExternal(context, phoneNumber.getNumber()));
AvatarInfo recipientAvatar = getRecipientAvatarInfo(PhoneNumberFormatter.get(context).format(phoneNumber.getNumber()));
if (recipientAvatar != null) {
return recipientAvatar;
}
@ -286,8 +287,8 @@ public class ContactRepository {
}
@WorkerThread
private @Nullable AvatarInfo getRecipientAvatarInfo(@NonNull Address address) {
Recipient recipient = Recipient.from(context, address, false);
private @Nullable AvatarInfo getRecipientAvatarInfo(String address) {
Recipient recipient = Recipient.external(context, address);
ContactPhoto contactPhoto = recipient.getContactPhoto();
if (contactPhoto != null) {

View File

@ -22,10 +22,12 @@ import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
import org.thoughtcrime.securesms.contactshare.Contact.Email;
import org.thoughtcrime.securesms.contactshare.Contact.Phone;
import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.Util;
@ -112,8 +114,7 @@ public final class ContactUtil {
}
public static @NonNull String getNormalizedPhoneNumber(@NonNull Context context, @NonNull String number) {
Address address = Address.fromExternal(context, number);
return address.serialize();
return PhoneNumberFormatter.get(context).format(number);
}
@MainThread
@ -122,7 +123,7 @@ public final class ContactUtil {
CharSequence[] values = new CharSequence[choices.size()];
for (int i = 0; i < values.length; i++) {
values[i] = getPrettyPhoneNumber(choices.get(i).getAddress().toPhoneString(), locale);
values[i] = getPrettyPhoneNumber(choices.get(i).requireAddress().toPhoneString(), locale);
}
new AlertDialog.Builder(context)
@ -134,8 +135,8 @@ public final class ContactUtil {
}
}
public static List<Recipient> getRecipients(@NonNull Context context, @NonNull Contact contact) {
return Stream.of(contact.getPhoneNumbers()).map(phone -> Recipient.from(context, Address.fromExternal(context, phone.getNumber()), true)).toList();
public static List<RecipientId> getRecipients(@NonNull Context context, @NonNull Contact contact) {
return Stream.of(contact.getPhoneNumbers()).map(phone -> Recipient.external(context, phone.getNumber())).map(Recipient::getId).toList();
}
@WorkerThread

View File

@ -27,13 +27,13 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.Collections;
@ -43,7 +43,7 @@ import java.util.Map;
import static org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.*;
public class SharedContactDetailsActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener {
public class SharedContactDetailsActivity extends PassphraseRequiredActionBarActivity {
private static final int CODE_ADD_EDIT_CONTACT = 2323;
private static final String KEY_CONTACT = "contact";
@ -64,7 +64,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private final Map<String, Recipient> activeRecipients = new HashMap<>();
private final Map<RecipientId, LiveRecipient> activeRecipients = new HashMap<>();
public static Intent getIntent(@NonNull Context context, @NonNull Contact contact) {
Intent intent = new Intent(context, SharedContactDetailsActivity.class);
@ -97,6 +97,10 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
presentContact(contact);
presentActionButtons(ContactUtil.getRecipients(this, contact));
presentAvatar(contact.getAvatarAttachment() != null ? contact.getAvatarAttachment().getDataUri() : null);
for (LiveRecipient recipient : activeRecipients.values()) {
recipient.observe(this, r -> presentActionButtons(Collections.singletonList(r.getId())));
}
}
@Override
@ -144,11 +148,6 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
glideRequests = GlideApp.with(this);
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> presentActionButtons(Collections.singletonList(recipient)));
}
@SuppressLint("StaticFieldLeak")
private void presentContact(@Nullable Contact contact) {
this.contact = contact;
@ -193,21 +192,19 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
}
}
private void presentActionButtons(@NonNull List<Recipient> recipients) {
for (Recipient recipient : recipients) {
activeRecipients.put(recipient.getAddress().serialize(), recipient);
private void presentActionButtons(@NonNull List<RecipientId> recipients) {
for (RecipientId recipientId : recipients) {
activeRecipients.put(recipientId, Recipient.live(recipientId));
}
List<Recipient> pushUsers = new ArrayList<>(recipients.size());
List<Recipient> systemUsers = new ArrayList<>(recipients.size());
for (Recipient recipient : activeRecipients.values()) {
recipient.addListener(this);
if (recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
pushUsers.add(recipient);
} else if (recipient.isSystemContact()) {
systemUsers.add(recipient);
for (LiveRecipient recipient : activeRecipients.values()) {
if (recipient.get().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
pushUsers.add(recipient.get());
} else if (recipient.get().isSystemContact()) {
systemUsers.add(recipient.get());
}
}
@ -230,7 +227,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
inviteButtonView.setOnClickListener(v -> {
ContactUtil.selectRecipientThroughDialog(this, systemUsers, dynamicLanguage.getCurrentLocale(), recipient -> {
CommunicationActions.composeSmsThroughDefaultApp(this, recipient.getAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
CommunicationActions.composeSmsThroughDefaultApp(this, recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
});
});
} else {

View File

@ -128,7 +128,6 @@ import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -184,10 +183,10 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientExporter;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
@ -249,7 +248,6 @@ import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener,
RecipientModifiedListener,
OnKeyboardShownListener,
InputPanel.Listener,
InputPanel.MediaListener,
@ -259,7 +257,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
{
private static final String TAG = ConversationActivity.class.getSimpleName();
public static final String ADDRESS_EXTRA = "address";
public static final String RECIPIENT_EXTRA = "recipient_id";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text";
@ -316,14 +314,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ConversationSearchViewModel searchViewModel;
private ConversationStickerViewModel stickerViewModel;
private Recipient recipient;
private long threadId;
private int distributionType;
private boolean archived;
private boolean isSecureText;
private boolean isDefaultSms = true;
private boolean isMmsEnabled = true;
private boolean isSecurityInitialized = false;
private LiveRecipient recipient;
private long threadId;
private int distributionType;
private boolean archived;
private boolean isSecureText;
private boolean isDefaultSms = true;
private boolean isMmsEnabled = true;
private boolean isSecurityInitialized = false;
private final IdentityRecordList identityRecords = new IdentityRecordList();
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
@ -428,10 +426,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeIdentityRecords();
composeText.setTransport(sendButton.getSelectedTransport());
titleView.setTitle(glideRequests, recipient);
setActionBarColor(recipient.getColor());
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipient);
Recipient recipientSnapshot = recipient.get();
titleView.setTitle(glideRequests, recipientSnapshot);
setActionBarColor(recipientSnapshot.getColor());
setBlockedUserState(recipientSnapshot, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipientSnapshot);
calculateCharactersRemaining();
MessageNotifier.setVisibleThread(threadId);
@ -473,7 +473,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onDestroy() {
saveDraft();
if (recipient != null) recipient.removeListener(this);
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
super.onDestroy();
}
@ -508,11 +507,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
sendSharedContact(data.getParcelableArrayListExtra(ContactShareEditActivity.KEY_CONTACTS));
break;
case GROUP_EDIT:
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
recipient.addListener(this);
titleView.setTitle(glideRequests, recipient);
NotificationChannels.updateContactChannelName(this, recipient);
setBlockedUserState(recipient, isSecureText, isDefaultSms);
Recipient recipientSnapshot = recipient.get();
onRecipientChanged(recipientSnapshot);
titleView.setTitle(glideRequests, recipientSnapshot);
NotificationChannels.updateContactChannelName(this, recipientSnapshot);
setBlockedUserState(recipientSnapshot, isSecureText, isDefaultSms);
supportInvalidateOptionsMenu();
break;
case TAKE_PHOTO:
@ -521,8 +521,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
break;
case ADD_CONTACT:
recipient = Recipient.from(this, recipient.getAddress(), true);
recipient.addListener(this);
onRecipientChanged(recipient.get());
fragment.reloadList();
break;
case PICK_LOCATION:
@ -539,7 +538,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeSecurity(isSecureText, isDefaultSms);
break;
case MEDIA_SENDER:
long expiresIn = recipient.getExpireMessages() * 1000L;
long expiresIn = recipient.get().getExpireMessages() * 1000L;
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
boolean initiating = threadId == -1;
TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT);
@ -617,14 +616,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
menu.clear();
if (isSecureText) {
if (recipient.getExpireMessages() > 0) {
if (recipient.get().getExpireMessages() > 0) {
inflater.inflate(R.menu.conversation_expiring_on, menu);
final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
final View actionView = MenuItemCompat.getActionView(item);
final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.get().getExpireMessages()));
actionView.setOnClickListener(v -> onOptionsItemSelected(item));
} else {
inflater.inflate(R.menu.conversation_expiring_off, menu);
@ -657,14 +656,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inflater.inflate(R.menu.conversation_insecure, menu);
}
if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
else inflater.inflate(R.menu.conversation_unmuted, menu);
if (recipient != null && recipient.get().isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
else inflater.inflate(R.menu.conversation_unmuted, menu);
if (isSingleConversation() && getRecipient().getContactUri() == null) {
inflater.inflate(R.menu.conversation_add_to_contacts, menu);
}
if (recipient != null && recipient.isLocalNumber()) {
if (recipient != null && recipient.get().isLocalNumber()) {
if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false);
else menu.findItem(R.id.menu_call_insecure).setVisible(false);
@ -771,7 +770,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ReminderUpdateEvent event) {
updateReminders(recipient.hasSeenInviteReminder());
updateReminders(recipient.get().hasSeenInviteReminder());
}
@Override
@ -794,11 +793,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
//noinspection CodeBlock2Expr
ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> {
ExpirationDialog.show(this, recipient.get().getExpireMessages(), expirationTime -> {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient.getId(), expirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L);
MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null);
@ -816,13 +815,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleMuteNotifications() {
MuteDialog.show(this, until -> {
recipient.setMuted(until);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient, until);
.setMuted(recipient.getId(), until);
return null;
}
@ -832,7 +829,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleConversationSettings() {
Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA,
isSecureText && !isSelfConversation());
@ -840,13 +837,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleUnmuteNotifications() {
recipient.setMuted(0);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient, 0);
.setMuted(recipient.getId(), 0);
return null;
}
@ -857,7 +852,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
int titleRes = R.string.ConversationActivity_unblock_this_contact_question;
int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
if (recipient.isGroupRecipient()) {
if (recipient.get().isGroup()) {
titleRes = R.string.ConversationActivity_unblock_this_group_question;
bodyRes = R.string.ConversationActivity_unblock_this_group_description;
}
@ -872,7 +867,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setBlocked(recipient, false);
.setBlocked(recipient.getId(), false);
ApplicationContext.getInstance(ConversationActivity.this)
.getJobManager()
@ -904,7 +899,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.appendInvite(inviteText);
} else {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:" + recipient.getAddress().serialize()));
intent.setData(Uri.parse("smsto:" + recipient.get().requireAddress().serialize()));
intent.putExtra("sms_body", inviteText);
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
startActivity(intent);
@ -943,12 +938,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleViewMedia() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, recipient.getId());
startActivity(intent);
}
private void handleAddShortcut() {
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.getAddress());
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().requireAddress());
new AsyncTask<Void, Void, IconCompat>() {
@ -957,9 +952,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Context context = getApplicationContext();
IconCompat icon = null;
if (recipient.getContactPhoto() != null) {
if (recipient.get().getContactPhoto() != null) {
try {
Bitmap bitmap = BitmapFactory.decodeStream(recipient.getContactPhoto().openInputStream(context));
Bitmap bitmap = BitmapFactory.decodeStream(recipient.get().getContactPhoto().openInputStream(context));
bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300);
icon = IconCompat.createWithAdaptiveBitmap(bitmap);
} catch (IOException e) {
@ -968,8 +963,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
if (icon == null) {
icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut
: R.mipmap.ic_person_shortcut);
icon = IconCompat.createWithResource(context, recipient.get().isGroup() ? R.mipmap.ic_group_shortcut
: R.mipmap.ic_person_shortcut);
}
return icon;
@ -978,14 +973,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPostExecute(IconCompat icon) {
Context context = getApplicationContext();
String name = Optional.fromNullable(recipient.getName())
.or(Optional.fromNullable(recipient.getProfileName()))
.or(recipient.toShortString());
String name = Optional.fromNullable(recipient.get().getName())
.or(Optional.fromNullable(recipient.get().getProfileName()))
.or(recipient.get().toShortString());
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis())
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.get().requireAddress().serialize() + '-' + System.currentTimeMillis())
.setShortLabel(name)
.setIcon(icon)
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress()))
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getId()))
.build();
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
@ -1020,9 +1015,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MessageSender.send(this, leaveMessage.get(), threadId, false, null);
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
String groupId = groupRecipient.getAddress().toGroupString();
String groupId = groupRecipient.requireAddress().toGroupString();
groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)));
groupDatabase.remove(groupId, Recipient.self().getId());
initializeEnabledCheck();
} else {
@ -1036,7 +1031,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.get().requireAddress());
startActivityForResult(intent, GROUP_EDIT);
}
@ -1080,7 +1075,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.getAddress().serialize()));
Uri.parse("tel:" + recipient.requireAddress().serialize()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);
@ -1096,20 +1091,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleAddToContacts() {
if (recipient.getAddress().isGroup()) return;
if (recipient.get().requireAddress().isGroup()) return;
try {
startActivityForResult(RecipientExporter.export(recipient).asAddContactIntent(), ADD_CONTACT);
startActivityForResult(RecipientExporter.export(recipient.get()).asAddContactIntent(), ADD_CONTACT);
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
}
}
private boolean handleDisplayQuickContact() {
if (recipient.getAddress().isGroup()) return false;
if (recipient.get().requireAddress().isGroup()) return false;
if (recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
if (recipient.get().getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.get().getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else {
handleAddToContacts();
}
@ -1193,14 +1188,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
this.isDefaultSms = isDefaultSms;
this.isSecurityInitialized = true;
boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent();
boolean isMediaMessage = recipient.get().isMmsGroup() || attachmentManager.isAttachmentPresent();
sendButton.resetAvailableTransports(isMediaMessage);
if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE);
if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS);
if (recipient.get().isPushGroup()) sendButton.disableTransport(Type.SMS);
if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) {
if (!recipient.get().isPushGroup() && recipient.get().isForceSmsSelection()) {
sendButton.setDefaultTransport(Type.SMS);
} else {
if (isSecureText || isPushGroupConversation()) sendButton.setDefaultTransport(Type.TEXTSECURE);
@ -1209,7 +1204,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
calculateCharactersRemaining();
supportInvalidateOptionsMenu();
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setBlockedUserState(recipient.get(), isSecureText, isDefaultSms);
}
///// Initializers
@ -1229,7 +1224,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
if (!Util.isEmpty(mediaList)) {
Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText, sendButton.getSelectedTransport());
Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient.get(), draftText, sendButton.getSelectedTransport());
startActivityForResult(sendIntent, MEDIA_SENDER);
return new SettableFuture<>(false);
}
@ -1344,16 +1339,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected boolean[] doInBackground(Recipient... params) {
Context context = ConversationActivity.this;
Recipient recipient = params[0];
Recipient recipient = params[0].resolve();
Log.i(TAG, "Resolving registered state...");
RegisteredState registeredState;
if (recipient.isPushGroupRecipient()) {
if (recipient.isPushGroup()) {
Log.i(TAG, "Push group recipient...");
registeredState = RegisteredState.REGISTERED;
} else if (recipient.isResolving()) {
Log.i(TAG, "Talking to DB directly.");
registeredState = DatabaseFactory.getRecipientDatabase(ConversationActivity.this).isRegistered(recipient.getAddress());
} else {
Log.i(TAG, "Checking through resolved recipient");
registeredState = recipient.resolve().getRegistered();
@ -1364,7 +1356,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (registeredState == RegisteredState.UNKNOWN) {
try {
Log.i(TAG, "Refreshing directory for user: " + recipient.getAddress().serialize());
Log.i(TAG, "Refreshing directory for user: " + recipient.requireAddress().serialize());
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
} catch (IOException e) {
Log.w(TAG, e);
@ -1385,15 +1377,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
future.set(true);
onSecurityUpdated();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
return future;
}
private void onSecurityUpdated() {
Log.i(TAG, "onSecurityUpdated()");
updateReminders(recipient.hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
updateReminders(recipient.get().hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.get().getDefaultSubscriptionId());
}
protected void updateReminders(boolean seenInvite) {
@ -1410,9 +1402,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
TextSecurePreferences.isShowInviteReminders(this) &&
!isSecureText &&
!seenInvite &&
!recipient.isGroupRecipient())
!recipient.get().isGroup())
{
InviteReminder reminder = new InviteReminder(this, recipient);
InviteReminder reminder = new InviteReminder(this, recipient.get());
reminder.setOkListener(v -> {
handleInviteLink();
reminderView.get().requestDismiss();
@ -1452,16 +1444,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
IdentityRecordList identityRecordList = new IdentityRecordList();
List<Recipient> recipients = new LinkedList<>();
if (params[0].isGroupRecipient()) {
if (params[0].isGroup()) {
recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
.getGroupMembers(params[0].getAddress().toGroupString(), false));
.getGroupMembers(params[0].requireAddress().toGroupString(), false));
} else {
recipients.add(params[0]);
}
for (Recipient recipient : recipients) {
Log.i(TAG, "Loading identity for: " + recipient.getAddress());
identityRecordList.add(identityDatabase.getIdentity(recipient.getAddress()));
Log.i(TAG, "Loading identity for: " + recipient.requireAddress());
identityRecordList.add(identityDatabase.getIdentity(recipient.getId()));
}
String message = null;
@ -1493,7 +1485,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
future.set(true);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
return future;
}
@ -1585,15 +1577,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeResources() {
if (recipient != null) recipient.removeListener(this);
recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
glideRequests = GlideApp.with(this);
recipient.addListener(this);
recipient.observe(this, this::onRecipientChanged);
}
@ -1628,7 +1618,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (!result.getResults().isEmpty()) {
MessageResult messageResult = result.getResults().get(result.getPosition());
fragment.jumpToMessage(messageResult.messageRecipient.getAddress(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult);
fragment.jumpToMessage(messageResult.messageRecipient.getId(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult);
}
searchNav.setData(result.getPosition(), result.getResults().size());
@ -1699,27 +1689,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
ApplicationContext.getInstance(this)
.getJobManager()
.add(new RetrieveProfileJob(recipient));
.add(new RetrieveProfileJob(recipient.get()));
}
@Override
public void onModified(final Recipient recipient) {
Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
Util.runOnMain(() -> {
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
titleView.setTitle(glideRequests, recipient);
titleView.setVerified(identityRecords.isVerified());
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipient.getColor());
setGroupShareProfileReminder(recipient);
updateReminders(recipient.hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
initializeSecurity(isSecureText, isDefaultSms);
private void onRecipientChanged(@NonNull Recipient recipient) {
Log.i(TAG, "onModified(" + recipient.requireAddress().serialize() + ") " + recipient.getRegistered());
titleView.setTitle(glideRequests, recipient);
titleView.setVerified(identityRecords.isVerified());
setBlockedUserState(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipient.getColor());
setGroupShareProfileReminder(recipient);
updateReminders(recipient.hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
initializeSecurity(isSecureText, isDefaultSms);
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
invalidateOptionsMenu();
}
});
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
invalidateOptionsMenu();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -1760,7 +1746,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.i(TAG, "Selected: " + type);
switch (type) {
case AttachmentTypeSelector.ADD_GALLERY:
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); break;
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()); break;
case AttachmentTypeSelector.ADD_DOCUMENT:
AttachmentManager.selectDocument(this, PICK_DOCUMENT); break;
case AttachmentTypeSelector.ADD_SOUND:
@ -1790,7 +1776,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return new SettableFuture<>(false);
} else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) {
Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, Optional.absent(), Optional.absent());
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
return new SettableFuture<>(false);
} else {
return attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints(), width, height);
@ -1812,7 +1798,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void sendSharedContact(List<Contact> contacts) {
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000L;
long expiresIn = recipient.get().getExpireMessages() * 1000L;
boolean initiating = threadId == -1;
sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, false, subscriptionId, initiating, false);
@ -1933,7 +1919,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing()) {
if (recipient.isPushGroup() && !recipient.isProfileSharing()) {
groupShareProfileView.get().setRecipient(recipient);
groupShareProfileView.get().setVisibility(View.VISIBLE);
} else if (groupShareProfileView.resolved()) {
@ -1979,30 +1965,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private boolean isSingleConversation() {
return getRecipient() != null && !getRecipient().isGroupRecipient();
return getRecipient() != null && !getRecipient().isGroup();
}
private boolean isActiveGroup() {
if (!isGroupConversation()) return false;
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getId());
return record.isPresent() && record.get().isActive();
}
@SuppressWarnings("SimplifiableIfStatement")
private boolean isSelfConversation() {
if (!TextSecurePreferences.isPushRegistered(this)) return false;
if (recipient.isGroupRecipient()) return false;
if (recipient.get().isGroup()) return false;
return Util.isOwnNumber(this, recipient.getAddress());
return Util.isOwnNumber(this, recipient.get().requireAddress());
}
private boolean isGroupConversation() {
return getRecipient() != null && getRecipient().isGroupRecipient();
return getRecipient() != null && getRecipient().isGroup();
}
private boolean isPushGroupConversation() {
return getRecipient() != null && getRecipient().isPushGroupRecipient();
return getRecipient() != null && getRecipient().isPushGroup();
}
private boolean isSmsForced() {
@ -2010,7 +1996,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
protected Recipient getRecipient() {
return this.recipient;
return this.recipient.get();
}
protected long getThreadId() {
@ -2090,7 +2076,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
fragment.setLastSeen(0);
if (refreshFragment) {
fragment.reload(recipient, threadId);
fragment.reload(recipient.get(), threadId);
MessageNotifier.setVisibleThread(threadId);
}
@ -2121,8 +2107,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean initiating = threadId == -1;
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
recipient.isGroupRecipient() ||
recipient.getAddress().isEmail() ||
recipient.isGroup() ||
recipient.requireAddress().isEmail() ||
inputPanel.getQuote().isPresent() ||
linkPreviewViewModel.hasLinkPreview() ||
needsSplit;
@ -2130,7 +2116,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection());
Log.i(TAG, "forceSms: " + forceSms);
if ((recipient.isMmsGroupRecipient() || recipient.getAddress().isEmail()) && !isMmsEnabled) {
if ((recipient.isMmsGroup() || recipient.requireAddress().isEmail()) && !isMmsEnabled) {
handleManualMmsRequired();
} else if (!forceSms && identityRecords.isUnverified()) {
handleUnverifiedRecipients();
@ -2186,7 +2172,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews);
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient.get(), slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews);
final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext();
@ -2217,7 +2203,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected Long doInBackground(Void... param) {
if (initiating) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
}
return MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
@ -2250,10 +2236,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
OutgoingTextMessage message;
if (isSecureText && !forceSms) {
message = new OutgoingEncryptedMessage(recipient, messageBody, expiresIn);
message = new OutgoingEncryptedMessage(recipient.get(), messageBody, expiresIn);
ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
} else {
message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId);
message = new OutgoingTextMessage(recipient.get(), messageBody, expiresIn, subscriptionId);
}
Permissions.with(this)
@ -2268,7 +2254,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected Long doInBackground(OutgoingTextMessage... messages) {
if (initiatingConversation) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
}
return MessageSender.send(context, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
@ -2331,10 +2317,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Void... params) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(ConversationActivity.this);
recipientDatabase.setDefaultSubscriptionId(recipient, transportOption.getSimSubscriptionId().or(-1));
recipientDatabase.setDefaultSubscriptionId(recipient.getId(), transportOption.getSimSubscriptionId().or(-1));
if (!recipient.isPushGroupRecipient()) {
recipientDatabase.setForceSmsSelection(recipient, recipient.getRegistered() == RegisteredState.REGISTERED && transportOption.isSms());
if (!recipient.resolve().isPushGroup()) {
recipientDatabase.setForceSmsSelection(recipient.getId(), recipient.get().getRegistered() == RegisteredState.REGISTERED && transportOption.isSms());
}
return null;
@ -2381,7 +2367,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onSuccess(final @NonNull Pair<Uri, Long> result) {
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000L;
long expiresIn = recipient.get().getExpireMessages() * 1000L;
boolean initiating = threadId == -1;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true);
SlideDeck slideDeck = new SlideDeck();
@ -2502,12 +2488,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) {
if (sendButton.getSelectedTransport().isSms()) {
Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, Optional.absent(), Optional.absent());
Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport());
Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport());
startActivityForResult(intent, MEDIA_SENDER);
return;
}
long expiresIn = recipient.getExpireMessages() * 1000L;
long expiresIn = recipient.get().getExpireMessages() * 1000L;
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
boolean initiating = threadId == -1;
TransportOption transport = sendButton.getSelectedTransport();
@ -2538,7 +2524,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
linkPreviewViewModel.onUserCancel();
Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent());
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
}
}
@ -2552,7 +2538,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
.onAllGranted(() -> {
composeText.clearFocus();
startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient, sendButton.getSelectedTransport()), MEDIA_SENDER);
startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient.get(), sendButton.getSelectedTransport()), MEDIA_SENDER);
overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary);
})
.onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
@ -2662,7 +2648,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Recipient author;
if (messageRecord.isOutgoing()) {
author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true);
author = Recipient.self();
} else {
author = messageRecord.getIndividualRecipient();
}
@ -2740,7 +2726,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : unverifiedIdentities) {
identityDatabase.setVerified(identityRecord.getAddress(),
identityDatabase.setVerified(identityRecord.getRecipientId(),
identityRecord.getIdentityKey(),
VerifiedStatus.DEFAULT);
}
@ -2763,7 +2749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.i(TAG, "onClicked: " + unverifiedIdentities.size());
if (unverifiedIdentities.size() == 1) {
Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(0).getAddress());
intent.putExtra(VerifyIdentityActivity.RECIPIENT_EXTRA, unverifiedIdentities.get(0).getRecipientId());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(0).getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false);
@ -2772,7 +2758,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String[] unverifiedNames = new String[unverifiedIdentities.size()];
for (int i=0;i<unverifiedIdentities.size();i++) {
unverifiedNames[i] = Recipient.from(ConversationActivity.this, unverifiedIdentities.get(i).getAddress(), false).toShortString();
unverifiedNames[i] = Recipient.resolved(unverifiedIdentities.get(i).getRecipientId()).toShortString();
}
AlertDialog.Builder builder = new AlertDialog.Builder(ConversationActivity.this);
@ -2780,7 +2766,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
builder.setTitle("No longer verified");
builder.setItems(unverifiedNames, (dialog, which) -> {
Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(which).getAddress());
intent.putExtra(VerifyIdentityActivity.RECIPIENT_EXTRA, unverifiedIdentities.get(which).getRecipientId());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(which).getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false);
@ -2803,7 +2789,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected MessageRecord doInBackground(Void... voids) {
QuoteId quoteId = QuoteId.deserialize(serialized);
QuoteId quoteId = QuoteId.deserialize(ConversationActivity.this, serialized);
if (quoteId != null) {
return DatabaseFactory.getMmsSmsDatabase(getApplicationContext()).getMessageFor(quoteId.getId(), quoteId.getAuthor());

View File

@ -69,7 +69,6 @@ import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@ -90,7 +89,9 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.profiles.UnknownSenderView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity;
import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
@ -134,7 +135,7 @@ public class ConversationFragment extends Fragment
private ConversationFragmentListener listener;
private Recipient recipient;
private LiveRecipient recipient;
private long threadId;
private long lastSeen;
private int startingPosition;
@ -253,12 +254,12 @@ public class ConversationFragment extends Fragment
}
private void initializeResources() {
this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
this.recipient = Recipient.live(getActivity().getIntent().getParcelableExtra(ConversationActivity.RECIPIENT_EXTRA));
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
this.firstLoad = true;
this.unknownSenderView = new UnknownSenderView(getActivity(), recipient, threadId);
this.unknownSenderView = new UnknownSenderView(getActivity(), recipient.get(), threadId);
OnScrollListener scrollListener = new ConversationScrollListener(getActivity());
list.addOnScrollListener(scrollListener);
@ -266,7 +267,7 @@ public class ConversationFragment extends Fragment
private void initializeListAdapter() {
if (this.recipient != null && this.threadId != -1) {
ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient);
ConversationAdapter adapter = new ConversationAdapter(requireContext(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient.get());
list.setAdapter(adapter);
list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
@ -302,7 +303,7 @@ public class ConversationFragment extends Fragment
replacedByIncomingMessage = false;
}
typingView.setTypists(GlideApp.with(ConversationFragment.this), recipients, recipient.isGroupRecipient());
typingView.setTypists(GlideApp.with(ConversationFragment.this), recipients, recipient.get().isGroup());
ConversationAdapter adapter = getListAdapter();
@ -417,7 +418,7 @@ public class ConversationFragment extends Fragment
}
public void reload(Recipient recipient, long threadId) {
this.recipient = recipient;
this.recipient = recipient.live();
if (this.threadId != threadId) {
this.threadId = threadId;
@ -521,8 +522,8 @@ public class ConversationFragment extends Fragment
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.isGroupRecipient() && message.isPush());
intent.putExtra(MessageDetailsActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.get().isGroup() && message.isPush());
startActivity(intent);
}
@ -663,7 +664,7 @@ public class ConversationFragment extends Fragment
setLastSeen(loader.getLastSeen());
}
if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
if (!loader.hasSent() && !recipient.get().isSystemContact() && !recipient.get().isGroup() && recipient.get().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
adapter.setHeaderView(unknownSenderView);
} else {
clearHeaderIfNotTyping(adapter);
@ -789,7 +790,7 @@ public class ConversationFragment extends Fragment
}
}
public void jumpToMessage(@NonNull Address author, long timestamp, @Nullable Runnable onMessageNotFound) {
public void jumpToMessage(@NonNull RecipientId author, long timestamp, @Nullable Runnable onMessageNotFound) {
SimpleTask.run(getLifecycle(), () -> {
return DatabaseFactory.getMmsSmsDatabase(getContext())
.getMessagePositionInConversation(threadId, timestamp, author);
@ -951,9 +952,9 @@ public class ConversationFragment extends Fragment
}
@Override
public void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms) {
public void onMoreTextClicked(@NonNull RecipientId conversationRecipientId, long messageId, boolean isMms) {
if (getContext() != null && getActivity() != null) {
startActivity(LongMessageActivity.getIntent(getContext(), conversationAddress, messageId, isMms));
startActivity(LongMessageActivity.getIntent(getContext(), conversationRecipientId, messageId, isMms));
}
}
@ -990,7 +991,7 @@ public class ConversationFragment extends Fragment
ApplicationContext.getInstance(requireContext())
.getJobManager()
.add(new MultiDeviceViewOnceOpenJob(new MessagingDatabase.SyncMessageId(messageRecord.getIndividualRecipient().getAddress(), messageRecord.getDateSent())));
.add(new MultiDeviceViewOnceOpenJob(new MessagingDatabase.SyncMessageId(messageRecord.getIndividualRecipient().getId(), messageRecord.getDateSent())));
return tempUri;
} catch (IOException e) {
@ -1046,7 +1047,7 @@ public class ConversationFragment extends Fragment
if (getContext() == null) return;
ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> {
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.getAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
});
}
}

View File

@ -68,6 +68,8 @@ import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.Outliner;
import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageView;
import org.thoughtcrime.securesms.components.SharedContactView;
import org.thoughtcrime.securesms.components.StickerView;
@ -98,7 +100,6 @@ import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.stickers.StickerUrl;
import org.thoughtcrime.securesms.util.DateUtils;
@ -108,7 +109,6 @@ import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.SearchUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.util.guava.Optional;
@ -127,8 +127,8 @@ import java.util.Set;
*
*/
public class ConversationItem extends LinearLayout
implements RecipientModifiedListener, BindableConversationItem
public class ConversationItem extends LinearLayout implements BindableConversationItem,
RecipientForeverObserver
{
private static final String TAG = ConversationItem.class.getSimpleName();
@ -138,7 +138,7 @@ public class ConversationItem extends LinearLayout
private MessageRecord messageRecord;
private Locale locale;
private boolean groupThread;
private Recipient recipient;
private LiveRecipient recipient;
private GlideRequests glideRequests;
protected ViewGroup bodyBubble;
@ -156,7 +156,7 @@ public class ConversationItem extends LinearLayout
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
private @NonNull Outliner outliner = new Outliner();
private Recipient conversationRecipient;
private LiveRecipient conversationRecipient;
private Stub<ConversationItemThumbnail> mediaThumbnailStub;
private Stub<AudioView> audioViewStub;
private Stub<DocumentView> documentViewStub;
@ -238,16 +238,19 @@ public class ConversationItem extends LinearLayout
@Nullable String searchQuery,
boolean pulseHighlight)
{
if (this.recipient != null) this.recipient.removeForeverObserver(this);
if (this.conversationRecipient != null) this.conversationRecipient.removeForeverObserver(this);
this.messageRecord = messageRecord;
this.locale = locale;
this.glideRequests = glideRequests;
this.batchSelected = batchSelected;
this.conversationRecipient = conversationRecipient;
this.groupThread = conversationRecipient.isGroupRecipient();
this.recipient = messageRecord.getIndividualRecipient();
this.conversationRecipient = conversationRecipient.live();
this.groupThread = conversationRecipient.isGroup();
this.recipient = messageRecord.getIndividualRecipient().live();
this.recipient.addListener(this);
this.conversationRecipient.addListener(this);
this.recipient.observeForever(this);
this.conversationRecipient.observeForever(this);
setGutterSizes(messageRecord, groupThread);
setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
@ -256,8 +259,8 @@ public class ConversationItem extends LinearLayout
setBodyText(messageRecord, searchQuery);
setBubbleState(messageRecord);
setStatusIcons(messageRecord);
setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient);
setContactPhoto(recipient.get());
setGroupMessageStatus(messageRecord, recipient.get());
setGroupAuthorColor(messageRecord);
setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
@ -320,6 +323,14 @@ public class ConversationItem extends LinearLayout
}
}
@Override
public void onRecipientChanged(@NonNull Recipient modified) {
setBubbleState(messageRecord);
setContactPhoto(recipient.get());
setGroupMessageStatus(messageRecord, recipient.get());
setAudioViewTint(messageRecord, conversationRecipient.get());
}
private int getAvailableMessageBubbleWidth(@NonNull View forView) {
int availableWidth;
if (hasAudio(messageRecord)) {
@ -346,7 +357,7 @@ public class ConversationItem extends LinearLayout
@Override
public void unbind() {
if (recipient != null) {
recipient.removeListener(this);
recipient.removeForeverObserver(this);;
}
}
@ -372,7 +383,7 @@ public class ConversationItem extends LinearLayout
}
if (audioViewStub.resolved()) {
setAudioViewTint(messageRecord, this.conversationRecipient);
setAudioViewTint(messageRecord, this.conversationRecipient.get());
}
}
@ -820,7 +831,7 @@ public class ConversationItem extends LinearLayout
if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) {
Quote quote = ((MediaMmsMessageRecord)current).getQuote();
//noinspection ConstantConditions
quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quote.getText(), quote.isOriginalMissing(), quote.getAttachment());
quoteView.setQuote(glideRequests, quote.getId(), Recipient.live(quote.getAuthor()).get(), quote.getText(), quote.isOriginalMissing(), quote.getAttachment());
quoteView.setVisibility(View.VISIBLE);
quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
@ -944,7 +955,7 @@ public class ConversationItem extends LinearLayout
if (isGroupThread && !current.isOutgoing()) {
contactPhotoHolder.setVisibility(VISIBLE);
if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()) ||
if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().requireAddress().equals(previous.get().getRecipient().requireAddress()) ||
!DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp()))
{
groupSenderHolder.setVisibility(VISIBLE);
@ -952,7 +963,7 @@ public class ConversationItem extends LinearLayout
groupSenderHolder.setVisibility(GONE);
}
if (!next.isPresent() || next.get().isUpdate() || !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress())) {
if (!next.isPresent() || next.get().isUpdate() || !current.getRecipient().requireAddress().equals(next.get().getRecipient().requireAddress())) {
contactPhoto.setVisibility(VISIBLE);
} else {
contactPhoto.setVisibility(GONE);
@ -1012,7 +1023,7 @@ public class ConversationItem extends LinearLayout
private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, boolean isGroupThread) {
if (isGroupThread) {
return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) ||
!current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress());
!current.getRecipient().requireAddress().equals(previous.get().getRecipient().requireAddress());
} else {
return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) ||
current.isOutgoing() != previous.get().isOutgoing();
@ -1022,7 +1033,7 @@ public class ConversationItem extends LinearLayout
private boolean isEndOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (isGroupThread) {
return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) ||
!current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress());
!current.getRecipient().requireAddress().equals(next.get().getRecipient().requireAddress());
} else {
return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) ||
current.isOutgoing() != next.get().isOutgoing();
@ -1074,7 +1085,7 @@ public class ConversationItem extends LinearLayout
if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
message = getResources().getString(R.string.ConversationItem_read_more);
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getId(), messageRecord.getId(), messageRecord.isMms());
} else if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) {
message = getResources().getString(R.string.ConversationItem_pending);
action = () -> {};
@ -1083,11 +1094,11 @@ public class ConversationItem extends LinearLayout
action = () -> singleDownloadClickListener.onClick(bodyText, slide);
} else {
message = getResources().getString(R.string.ConversationItem_read_more);
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getId(), messageRecord.getId(), messageRecord.isMms());
}
} else {
message = getResources().getString(R.string.ConversationItem_read_more);
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms());
action = () -> eventListener.onMoreTextClicked(conversationRecipient.getId(), messageRecord.getId(), messageRecord.isMms());
}
SpannableStringBuilder span = new SpannableStringBuilder(message);
@ -1108,16 +1119,6 @@ public class ConversationItem extends LinearLayout
return span;
}
@Override
public void onModified(final Recipient modified) {
Util.runOnMain(() -> {
setBubbleState(messageRecord);
setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient);
setAudioViewTint(messageRecord, conversationRecipient);
});
}
private class SharedContactEventListener implements SharedContactView.EventListener {
@Override
public void onAddToContactsClicked(@NonNull Contact contact) {
@ -1251,7 +1252,7 @@ public class ConversationItem extends LinearLayout
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(slide.getUri(), slide.getContentType());
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, conversationRecipient.getId());
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
@ -1309,7 +1310,7 @@ public class ConversationItem extends LinearLayout
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush());
intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
intent.putExtra(MessageDetailsActivity.RECIPIENT_EXTRA, conversationRecipient.getId());
context.startActivity(intent);
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
handleApproveIdentity();
@ -1352,7 +1353,7 @@ public class ConversationItem extends LinearLayout
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SmsSendJob(context, messageRecord.getId(),
messageRecord.getIndividualRecipient().getAddress()));
messageRecord.getIndividualRecipient()));
}
});

View File

@ -84,7 +84,7 @@ public class ConversationPopupActivity extends ConversationActivity {
public void onSuccess(Long result) {
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, getRecipient().getAddress());
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, getRecipient().getId());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
startActivity(intent, transition.toBundle());

View File

@ -105,7 +105,7 @@ public class ConversationTitleView extends RelativeLayout {
}
private void setRecipientTitle(Recipient recipient) {
if (recipient.isGroupRecipient()) setGroupRecipientTitle(recipient);
if (recipient.isGroup()) setGroupRecipientTitle(recipient);
else if (recipient.isLocalNumber()) setSelfTitle();
else if (TextUtils.isEmpty(recipient.getName())) setNonContactRecipientTitle(recipient);
else setContactRecipientTitle(recipient);
@ -116,7 +116,7 @@ public class ConversationTitleView extends RelativeLayout {
this.title.setText(recipient.getName());
this.subtitle.setText(Stream.of(recipient.getParticipants())
.filter(r -> !r.getAddress().serialize().equals(localNumber))
.filter(r -> !r.requireAddress().serialize().equals(localNumber))
.map(Recipient::toShortString)
.collect(Collectors.joining(", ")));
@ -130,7 +130,7 @@ public class ConversationTitleView extends RelativeLayout {
@SuppressLint("SetTextI18n")
private void setNonContactRecipientTitle(Recipient recipient) {
this.title.setText(recipient.getAddress().serialize());
this.title.setText(recipient.requireAddress().serialize());
if (TextUtils.isEmpty(recipient.getProfileName())) {
this.subtitle.setText(null);

View File

@ -22,13 +22,13 @@ import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
@ -37,7 +37,7 @@ import java.util.Set;
import java.util.concurrent.ExecutionException;
public class ConversationUpdateItem extends LinearLayout
implements RecipientModifiedListener, BindableConversationItem
implements RecipientForeverObserver, BindableConversationItem
{
private static final String TAG = ConversationUpdateItem.class.getSimpleName();
@ -47,7 +47,7 @@ public class ConversationUpdateItem extends LinearLayout
private TextView title;
private TextView body;
private TextView date;
private Recipient sender;
private LiveRecipient sender;
private MessageRecord messageRecord;
private Locale locale;
@ -98,12 +98,28 @@ public class ConversationUpdateItem extends LinearLayout
}
private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
if (this.sender != null) {
this.sender.removeForeverObserver(this);
}
if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).removeObserver(this);
}
this.messageRecord = messageRecord;
this.sender = messageRecord.getIndividualRecipient();
this.sender = messageRecord.getIndividualRecipient().live();
this.locale = locale;
this.sender.addListener(this);
this.sender.observeForever(this);
if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).addObserver(this);
}
present(messageRecord);
}
private void present(MessageRecord messageRecord) {
if (messageRecord.isGroupAction()) setGroupRecord(messageRecord);
else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
@ -174,7 +190,6 @@ public class ConversationUpdateItem extends LinearLayout
icon.setImageResource(R.drawable.ic_group_grey600_24dp);
icon.clearColorFilter();
GroupUtil.getDescription(getContext(), messageRecord.getBody()).addListener(this);
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(GONE);
@ -203,8 +218,8 @@ public class ConversationUpdateItem extends LinearLayout
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> bind(messageRecord, locale));
public void onRecipientChanged(@NonNull Recipient recipient) {
present(messageRecord);
}
@Override
@ -215,7 +230,10 @@ public class ConversationUpdateItem extends LinearLayout
@Override
public void unbind() {
if (sender != null) {
sender.removeListener(this);
sender.removeForeverObserver(this);
}
if (this.messageRecord != null && messageRecord.isGroupAction()) {
GroupUtil.getDescription(getContext(), messageRecord.getBody()).removeObserver(this);
}
}
@ -238,14 +256,14 @@ public class ConversationUpdateItem extends LinearLayout
return;
}
final Recipient sender = ConversationUpdateItem.this.sender;
final Recipient sender = ConversationUpdateItem.this.sender.get();
IdentityUtil.getRemoteIdentityKey(getContext(), sender).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
@Override
public void onSuccess(Optional<IdentityRecord> result) {
if (result.isPresent()) {
Intent intent = new Intent(getContext(), VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress());
intent.putExtra(VerifyIdentityActivity.RECIPIENT_EXTRA, sender.getId());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);

View File

@ -10,7 +10,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
@ -47,12 +49,12 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
Address signalAddress = Address.fromExternal(context, address.getName());
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(signalAddress);
Recipient recipient = Recipient.external(context, address.getName());
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
if (!identityRecord.isPresent()) {
Log.i(TAG, "Saving new identity...");
identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
identityDatabase.saveIdentity(recipient.getId(), identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
return false;
}
@ -68,15 +70,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
verifiedStatus = VerifiedStatus.DEFAULT;
}
identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, Recipient.from(context, signalAddress, true));
identityDatabase.saveIdentity(recipient.getId(), identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, recipient);
SessionUtil.archiveSiblingSessions(context, address);
return true;
}
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.i(TAG, "Setting approval status...");
identityDatabase.setApproval(signalAddress, nonBlockingApproval);
identityDatabase.setApproval(recipient.getId(), nonBlockingApproval);
return false;
}
@ -93,15 +95,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
String ourNumber = TextSecurePreferences.getLocalNumber(context);
Address theirAddress = Address.fromExternal(context, address.getName());
RecipientId ourRecipientId = Recipient.self().getId();
RecipientId theirRecipientId = Recipient.external(context, address.getName()).getId();
if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) {
if (ourRecipientId.equals(theirRecipientId)) {
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
}
switch (direction) {
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress));
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirRecipientId));
case RECEIVING: return true;
default: throw new AssertionError("Unknown direction: " + direction);
}
@ -110,7 +112,8 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
@Override
public IdentityKey getIdentity(SignalProtocolAddress address) {
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(Address.fromSerialized(address.getName()));
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipientId);
if (record.isPresent()) {
return record.get().getIdentityKey();

View File

@ -7,6 +7,8 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
import org.whispersystems.libsignal.state.SessionRecord;
@ -29,7 +31,8 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
synchronized (FILE_LOCK) {
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId());
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
if (sessionRecord == null) {
Log.w(TAG, "No existing session information found.");
@ -43,14 +46,16 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).store(Address.fromSerialized(address.getName()), address.getDeviceId(), record);
RecipientId id = Recipient.external(context, address.getName()).getId();
DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record);
}
}
@Override
public boolean containsSession(SignalProtocolAddress address) {
synchronized (FILE_LOCK) {
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId());
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
return sessionRecord != null &&
sessionRecord.getSessionState().hasSenderChain() &&
@ -61,32 +66,36 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public void deleteSession(SignalProtocolAddress address) {
synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).delete(Address.fromSerialized(address.getName()), address.getDeviceId());
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId());
}
}
@Override
public void deleteAllSessions(String name) {
synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.fromSerialized(name));
RecipientId recipientId = Recipient.external(context, name).getId();
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId);
}
}
@Override
public List<Integer> getSubDeviceSessions(String name) {
synchronized (FILE_LOCK) {
return DatabaseFactory.getSessionDatabase(context).getSubDevices(Address.fromSerialized(name));
RecipientId recipientId = Recipient.external(context, name).getId();
return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId);
}
}
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
synchronized (FILE_LOCK) {
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(Address.fromSerialized(address.getName()));
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId);
for (SessionDatabase.SessionRow row : sessions) {
if (row.getDeviceId() != address.getDeviceId()) {
row.getRecord().archiveCurrentState();
storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord());
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
}
}
}
@ -98,7 +107,7 @@ public class TextSecureSessionStore implements SessionStore {
for (SessionDatabase.SessionRow row : sessions) {
row.getRecord().archiveCurrentState();
storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord());
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
}
}
}

View File

@ -1,36 +1,12 @@
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Pair;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
public class Address implements Parcelable, Comparable<Address> {
@ -48,8 +24,6 @@ public class Address implements Parcelable, Comparable<Address> {
private static final String TAG = Address.class.getSimpleName();
private static final AtomicReference<Pair<String, ExternalAddressFormatter>> cachedFormatter = new AtomicReference<>();
private final String address;
private Address(@NonNull String address) {
@ -65,50 +39,6 @@ public class Address implements Parcelable, Comparable<Address> {
return new Address(serialized);
}
public static Address fromExternal(@NonNull Context context, @Nullable String external) {
return new Address(getExternalAddressFormatter(context).format(external));
}
public static @NonNull List<Address> fromSerializedList(@NonNull String serialized, char delimiter) {
String[] escapedAddresses = DelimiterUtil.split(serialized, delimiter);
List<Address> addresses = new LinkedList<>();
for (String escapedAddress : escapedAddresses) {
addresses.add(Address.fromSerialized(DelimiterUtil.unescape(escapedAddress, delimiter)));
}
return addresses;
}
public static @NonNull String toSerializedList(@NonNull List<Address> addresses, char delimiter) {
Collections.sort(addresses);
List<String> escapedAddresses = new LinkedList<>();
for (Address address : addresses) {
escapedAddresses.add(DelimiterUtil.escape(address.serialize(), delimiter));
}
return Util.join(escapedAddresses, delimiter + "");
}
private static @NonNull ExternalAddressFormatter getExternalAddressFormatter(Context context) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (!TextUtils.isEmpty(localNumber)) {
Pair<String, ExternalAddressFormatter> cached = cachedFormatter.get();
if (cached != null && cached.first.equals(localNumber)) return cached.second;
ExternalAddressFormatter formatter = new ExternalAddressFormatter(localNumber);
cachedFormatter.set(new Pair<>(localNumber, formatter));
return formatter;
} else {
return new ExternalAddressFormatter(Util.getSimCountryIso(context).or("US"), true);
}
}
public boolean isGroup() {
return GroupUtil.isEncodedGroup(address);
}
@ -179,160 +109,4 @@ public class Address implements Parcelable, Comparable<Address> {
public int compareTo(@NonNull Address other) {
return address.compareTo(other.address);
}
@VisibleForTesting
public static class ExternalAddressFormatter {
private static final String TAG = ExternalAddressFormatter.class.getSimpleName();
private static final Set<String> SHORT_COUNTRIES = new HashSet<String>() {{
add("NU");
add("TK");
add("NC");
add("AC");
}};
private static final Pattern US_NO_AREACODE = Pattern.compile("^(\\d{7})$");
private static final Pattern BR_NO_AREACODE = Pattern.compile("^(9?\\d{8})$");
private final Optional<PhoneNumber> localNumber;
private final String localCountryCode;
private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]");
ExternalAddressFormatter(@NonNull String localNumberString) {
try {
Phonenumber.PhoneNumber libNumber = phoneNumberUtil.parse(localNumberString, null);
int countryCode = libNumber.getCountryCode();
this.localNumber = Optional.of(new PhoneNumber(localNumberString, countryCode, parseAreaCode(localNumberString, countryCode)));
this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(libNumber);
} catch (NumberParseException e) {
throw new AssertionError(e);
}
}
ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) {
this.localNumber = Optional.absent();
this.localCountryCode = localCountryCode;
}
public String format(@Nullable String number) {
if (number == null) return "Unknown";
if (GroupUtil.isEncodedGroup(number)) return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", "");
if (bareNumber.length() == 0) {
if (number.trim().length() == 0) return "Unknown";
else return number.trim();
}
// libphonenumber doesn't seem to be correct for Germany and Finland
if (bareNumber.length() <= 6 && ("DE".equals(localCountryCode) || "FI".equals(localCountryCode) || "SK".equals(localCountryCode))) {
return bareNumber;
}
// libphonenumber seems incorrect for Russia and a few other countries with 4 digit short codes.
if (bareNumber.length() <= 4 && !SHORT_COUNTRIES.contains(localCountryCode)) {
return bareNumber;
}
if (isShortCode(bareNumber, localCountryCode)) {
return bareNumber;
}
String processedNumber = applyAreaCodeRules(localNumber, bareNumber);
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(processedNumber, localCountryCode);
return phoneNumberUtil.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
} catch (NumberParseException e) {
Log.w(TAG, e);
if (bareNumber.charAt(0) == '+')
return bareNumber;
String localNumberImprecise = localNumber.isPresent() ? localNumber.get().getE164Number() : "";
if (localNumberImprecise.charAt(0) == '+')
localNumberImprecise = localNumberImprecise.substring(1);
if (localNumberImprecise.length() == bareNumber.length() || bareNumber.length() > localNumberImprecise.length())
return "+" + number;
int difference = localNumberImprecise.length() - bareNumber.length();
return "+" + localNumberImprecise.substring(0, difference) + bareNumber;
}
}
private boolean isShortCode(@NonNull String bareNumber, String localCountryCode) {
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(bareNumber, localCountryCode);
return ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(parsedNumber, localCountryCode);
} catch (NumberParseException e) {
return false;
}
}
private @Nullable String parseAreaCode(@NonNull String e164Number, int countryCode) {
switch (countryCode) {
case 1:
return e164Number.substring(2, 5);
case 55:
return e164Number.substring(3, 5);
}
return null;
}
private @NonNull String applyAreaCodeRules(@NonNull Optional<PhoneNumber> localNumber, @NonNull String testNumber) {
if (!localNumber.isPresent() || !localNumber.get().getAreaCode().isPresent()) {
return testNumber;
}
Matcher matcher;
switch (localNumber.get().getCountryCode()) {
case 1:
matcher = US_NO_AREACODE.matcher(testNumber);
if (matcher.matches()) {
return localNumber.get().getAreaCode() + matcher.group();
}
break;
case 55:
matcher = BR_NO_AREACODE.matcher(testNumber);
if (matcher.matches()) {
return localNumber.get().getAreaCode() + matcher.group();
}
}
return testNumber;
}
private static class PhoneNumber {
private final String e164Number;
private final int countryCode;
private final Optional<String> areaCode;
PhoneNumber(String e164Number, int countryCode, @Nullable String areaCode) {
this.e164Number = e164Number;
this.countryCode = countryCode;
this.areaCode = Optional.fromNullable(areaCode);
}
String getE164Number() {
return e164Number;
}
int getCountryCode() {
return countryCode;
}
Optional<String> getAreaCode() {
return areaCode;
}
}
}
}

View File

@ -1,6 +1,9 @@
package org.thoughtcrime.securesms.database;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.LRUCache;
import java.util.HashMap;
@ -11,13 +14,17 @@ public class EarlyReceiptCache {
private static final String TAG = EarlyReceiptCache.class.getSimpleName();
private final LRUCache<Long, Map<Address, Long>> cache = new LRUCache<>(100);
private final LRUCache<Long, Map<RecipientId, Long>> cache = new LRUCache<>(100);
private final String name;
public synchronized void increment(long timestamp, Address origin) {
Log.i(TAG, this+"");
Log.i(TAG, String.format(Locale.US, "Early receipt: (%d, %s)", timestamp, origin.serialize()));
public EarlyReceiptCache(@NonNull String name) {
this.name = name;
}
Map<Address, Long> receipts = cache.get(timestamp);
public synchronized void increment(long timestamp, @NonNull RecipientId origin) {
Log.i(TAG, String.format(Locale.US, "[%s] Timestamp: %d, Recipient: %s", name, timestamp, origin.serialize()));
Map<RecipientId, Long> receipts = cache.get(timestamp);
if (receipts == null) {
receipts = new HashMap<>();
@ -34,8 +41,8 @@ public class EarlyReceiptCache {
cache.put(timestamp, receipts);
}
public synchronized Map<Address, Long> remove(long timestamp) {
Map<Address, Long> receipts = cache.remove(timestamp);
public synchronized Map<RecipientId, Long> remove(long timestamp) {
Map<RecipientId, Long> receipts = cache.remove(timestamp);
Log.i(TAG, this+"");
Log.i(TAG, String.format(Locale.US, "Checking early receipts (%d): %d", timestamp, receipts == null ? 0 : receipts.size()));

View File

@ -15,7 +15,9 @@ import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
@ -37,6 +39,7 @@ public class GroupDatabase extends Database {
static final String TABLE_NAME = "groups";
private static final String ID = "_id";
static final String GROUP_ID = "group_id";
static final String RECIPIENT_ID = "recipient_id";
private static final String TITLE = "title";
private static final String MEMBERS = "members";
private static final String AVATAR = "avatar";
@ -53,6 +56,7 @@ public class GroupDatabase extends Database {
"CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
GROUP_ID + " TEXT, " +
RECIPIENT_ID + " INTEGER, " +
TITLE + " TEXT, " +
MEMBERS + " TEXT, " +
AVATAR + " BLOB, " +
@ -67,10 +71,11 @@ public class GroupDatabase extends Database {
public static final String[] CREATE_INDEXS = {
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
"CREATE UNIQUE INDEX IF NOT EXISTS group_recipient_id_index ON " + TABLE_NAME + " (" + RECIPIENT_ID + ");",
};
private static final String[] GROUP_PROJECTION = {
GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
GROUP_ID, RECIPIENT_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
TIMESTAMP, ACTIVE, MMS
};
@ -80,6 +85,16 @@ public class GroupDatabase extends Database {
super(context, databaseHelper);
}
public Optional<GroupRecord> getGroup(RecipientId recipientId) {
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, RECIPIENT_ID + " = ?", new String[] {recipientId.serialize()}, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
return getGroup(cursor);
}
return Optional.absent();
}
}
public Optional<GroupRecord> getGroup(String groupId) {
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
new String[] {groupId},
@ -111,12 +126,12 @@ public class GroupDatabase extends Database {
return new Reader(cursor);
}
public String getOrCreateGroupForMembers(List<Address> members, boolean mms) {
public String getOrCreateGroupForMembers(List<RecipientId> members, boolean mms) {
Collections.sort(members);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID},
MEMBERS + " = ? AND " + MMS + " = ?",
new String[] {Address.toSerializedList(members, ','), mms ? "1" : "0"},
new String[] {RecipientId.toSerializedList(members), mms ? "1" : "0"},
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
@ -138,28 +153,30 @@ public class GroupDatabase extends Database {
}
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
List<Address> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>();
List<RecipientId> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>();
for (Address member : members) {
if (!includeSelf && Util.isOwnNumber(context, member))
for (RecipientId member : members) {
if (!includeSelf && Recipient.resolved(member).isLocalNumber()) {
continue;
}
recipients.add(Recipient.from(context, member, false));
recipients.add(Recipient.resolved(member));
}
return recipients;
}
public void create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
public void create(@NonNull String groupId, @Nullable String title, @NonNull List<RecipientId> members,
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay)
{
Collections.sort(members);
ContentValues contentValues = new ContentValues();
contentValues.put(RECIPIENT_ID, DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId).serialize());
contentValues.put(GROUP_ID, groupId);
contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Address.toSerializedList(members, ','));
contentValues.put(MEMBERS, RecipientId.toSerializedList(members));
if (avatar != null) {
contentValues.put(AVATAR_ID, avatar.getId());
@ -175,11 +192,8 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title);
recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null);
recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList());
});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
notifyConversationListListeners();
}
@ -199,10 +213,8 @@ public class GroupDatabase extends Database {
GROUP_ID + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title);
recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null);
});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
notifyConversationListListeners();
}
@ -213,8 +225,8 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupId});
Recipient recipient = Recipient.from(context, Address.fromSerialized(groupId), false);
recipient.setName(title);
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void updateAvatar(String groupId, Bitmap avatar) {
@ -235,44 +247,39 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId));
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void updateMembers(String groupId, List<Address> members) {
public void updateMembers(String groupId, List<RecipientId> members) {
Collections.sort(members);
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(members, ','));
contents.put(MEMBERS, RecipientId.toSerializedList(members));
contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setParticipants(Stream.of(members).map(a -> Recipient.from(context, a, false)).toList());
});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
public void remove(String groupId, Address source) {
List<Address> currentMembers = getCurrentMembers(groupId);
public void remove(String groupId, RecipientId source) {
List<RecipientId> currentMembers = getCurrentMembers(groupId);
currentMembers.remove(source);
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(currentMembers, ','));
contents.put(MEMBERS, RecipientId.toSerializedList(currentMembers));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
List<Recipient> current = recipient.getParticipants();
Recipient removal = Recipient.from(context, source, false);
current.remove(removal);
recipient.setParticipants(current);
});
RecipientId groupRecipient = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient.live(groupRecipient).refresh();
}
private List<Address> getCurrentMembers(String groupId) {
private List<RecipientId> getCurrentMembers(String groupId) {
Cursor cursor = null;
try {
@ -283,7 +290,7 @@ public class GroupDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) {
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
return Address.fromSerializedList(serializedMembers, ',');
return RecipientId.fromSerializedList(serializedMembers);
}
return new LinkedList<>();
@ -334,6 +341,7 @@ public class GroupDatabase extends Database {
}
return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)),
RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getString(cursor.getColumnIndexOrThrow(TITLE)),
cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)),
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR)),
@ -355,23 +363,25 @@ public class GroupDatabase extends Database {
public static class GroupRecord {
private final String id;
private final String title;
private final List<Address> members;
private final byte[] avatar;
private final long avatarId;
private final byte[] avatarKey;
private final byte[] avatarDigest;
private final String avatarContentType;
private final String relay;
private final boolean active;
private final boolean mms;
private final String id;
private final RecipientId recipientId;
private final String title;
private final List<RecipientId> members;
private final byte[] avatar;
private final long avatarId;
private final byte[] avatarKey;
private final byte[] avatarDigest;
private final String avatarContentType;
private final String relay;
private final boolean active;
private final boolean mms;
public GroupRecord(String id, String title, String members, byte[] avatar,
public GroupRecord(String id, @NonNull RecipientId recipientId, String title, String members, byte[] avatar,
long avatarId, byte[] avatarKey, String avatarContentType,
String relay, boolean active, byte[] avatarDigest, boolean mms)
{
this.id = id;
this.recipientId = recipientId;
this.title = title;
this.avatar = avatar;
this.avatarId = avatarId;
@ -382,7 +392,7 @@ public class GroupDatabase extends Database {
this.active = active;
this.mms = mms;
if (!TextUtils.isEmpty(members)) this.members = Address.fromSerializedList(members, ',');
if (!TextUtils.isEmpty(members)) this.members = RecipientId.fromSerializedList(members);
else this.members = new LinkedList<>();
}
@ -394,6 +404,10 @@ public class GroupDatabase extends Database {
}
}
public @NonNull RecipientId getRecipientId() {
return recipientId;
}
public String getEncodedId() {
return id;
}
@ -402,7 +416,7 @@ public class GroupDatabase extends Database {
return title;
}
public List<Address> getMembers() {
public List<RecipientId> getMembers() {
return members;
}

View File

@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.LinkedList;
import java.util.List;
@ -19,7 +20,7 @@ public class GroupReceiptDatabase extends Database {
private static final String ID = "_id";
public static final String MMS_ID = "mms_id";
private static final String ADDRESS = "address";
private static final String RECIPIENT_ID = "address";
private static final String STATUS = "status";
private static final String TIMESTAMP = "timestamp";
private static final String UNIDENTIFIED = "unidentified";
@ -30,7 +31,7 @@ public class GroupReceiptDatabase extends Database {
public static final int STATUS_READ = 2;
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + ADDRESS + " TEXT, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
MMS_ID + " INTEGER, " + RECIPIENT_ID + " INTEGER, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXES = {
"CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
@ -40,13 +41,13 @@ public class GroupReceiptDatabase extends Database {
super(context, databaseHelper);
}
public void insert(List<Address> addresses, long mmsId, int status, long timestamp) {
public void insert(List<RecipientId> recipientIds, long mmsId, int status, long timestamp) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
for (Address address : addresses) {
for (RecipientId recipientId : recipientIds) {
ContentValues values = new ContentValues(4);
values.put(MMS_ID, mmsId);
values.put(ADDRESS, address.serialize());
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(STATUS, status);
values.put(TIMESTAMP, timestamp);
@ -54,23 +55,23 @@ public class GroupReceiptDatabase extends Database {
}
}
public void update(Address address, long mmsId, int status, long timestamp) {
public void update(@NonNull RecipientId recipientId, long mmsId, int status, long timestamp) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(2);
values.put(STATUS, status);
values.put(TIMESTAMP, timestamp);
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + ADDRESS + " = ? AND " + STATUS + " < ?",
new String[] {String.valueOf(mmsId), address.serialize(), String.valueOf(status)});
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + RECIPIENT_ID + " = ? AND " + STATUS + " < ?",
new String[] {String.valueOf(mmsId), recipientId.serialize(), String.valueOf(status)});
}
public void setUnidentified(Address address, long mmsId, boolean unidentified) {
public void setUnidentified(RecipientId recipientId, long mmsId, boolean unidentified) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(1);
values.put(UNIDENTIFIED, unidentified ? 1 : 0);
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + ADDRESS + " = ?",
new String[] {String.valueOf(mmsId), address.serialize()});
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + RECIPIENT_ID + " = ?",
new String[] {String.valueOf(mmsId), recipientId.serialize()});
}
@ -80,7 +81,7 @@ public class GroupReceiptDatabase extends Database {
try (Cursor cursor = db.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)}, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
results.add(new GroupReceiptInfo(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
results.add(new GroupReceiptInfo(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1));
@ -101,20 +102,20 @@ public class GroupReceiptDatabase extends Database {
}
public static class GroupReceiptInfo {
private final Address address;
private final int status;
private final long timestamp;
private final boolean unidentified;
private final RecipientId recipientId;
private final int status;
private final long timestamp;
private final boolean unidentified;
GroupReceiptInfo(Address address, int status, long timestamp, boolean unidentified) {
this.address = address;
GroupReceiptInfo(@NonNull RecipientId recipientId, int status, long timestamp, boolean unidentified) {
this.recipientId = recipientId;
this.status = status;
this.timestamp = timestamp;
this.unidentified = unidentified;
}
public Address getAddress() {
return address;
public @NonNull RecipientId getRecipientId() {
return recipientId;
}
public int getStatus() {

View File

@ -26,6 +26,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@ -40,7 +41,7 @@ public class IdentityDatabase extends Database {
private static final String TABLE_NAME = "identities";
private static final String ID = "_id";
private static final String ADDRESS = "address";
private static final String RECIPIENT_ID = "address";
private static final String IDENTITY_KEY = "key";
private static final String TIMESTAMP = "timestamp";
private static final String FIRST_USE = "first_use";
@ -49,7 +50,7 @@ public class IdentityDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
ADDRESS + " TEXT UNIQUE, " +
RECIPIENT_ID + " INTEGER UNIQUE, " +
IDENTITY_KEY + " TEXT, " +
FIRST_USE + " INTEGER DEFAULT 0, " +
TIMESTAMP + " INTEGER DEFAULT 0, " +
@ -88,13 +89,13 @@ public class IdentityDatabase extends Database {
return new IdentityReader(cursor);
}
public Optional<IdentityRecord> getIdentity(Address address) {
public Optional<IdentityRecord> getIdentity(@NonNull RecipientId recipientId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?",
new String[] {address.serialize()}, null, null, null);
cursor = database.query(TABLE_NAME, null, RECIPIENT_ID + " = ?",
new String[] {recipientId.serialize()}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return Optional.of(getIdentityRecord(cursor));
@ -108,14 +109,14 @@ public class IdentityDatabase extends Database {
return Optional.absent();
}
public void saveIdentity(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus,
public void saveIdentity(@NonNull RecipientId recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus,
boolean firstUse, long timestamp, boolean nonBlockingApproval)
{
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String identityKeyString = Base64.encodeBytes(identityKey.serialize());
ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, address.serialize());
contentValues.put(RECIPIENT_ID, recipientId.serialize());
contentValues.put(IDENTITY_KEY, identityKeyString);
contentValues.put(TIMESTAMP, timestamp);
contentValues.put(VERIFIED, verifiedStatus.toInt());
@ -124,36 +125,36 @@ public class IdentityDatabase extends Database {
database.replace(TABLE_NAME, null, contentValues);
EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus,
EventBus.getDefault().post(new IdentityRecord(recipientId, identityKey, verifiedStatus,
firstUse, timestamp, nonBlockingApproval));
}
public void setApproval(Address address, boolean nonBlockingApproval) {
public void setApproval(@NonNull RecipientId recipientId, boolean nonBlockingApproval) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(2);
contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval);
database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()});
database.update(TABLE_NAME, contentValues, RECIPIENT_ID + " = ?", new String[] {recipientId.serialize()});
}
public void setVerified(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
public void setVerified(@NonNull RecipientId recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
contentValues.put(VERIFIED, verifiedStatus.toInt());
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?",
new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())});
int updated = database.update(TABLE_NAME, contentValues, RECIPIENT_ID + " = ? AND " + IDENTITY_KEY + " = ?",
new String[] {recipientId.serialize(), Base64.encodeBytes(identityKey.serialize())});
if (updated > 0) {
Optional<IdentityRecord> record = getIdentity(address);
Optional<IdentityRecord> record = getIdentity(recipientId);
if (record.isPresent()) EventBus.getDefault().post(record.get());
}
}
private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException {
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID));
String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED));
@ -161,23 +162,23 @@ public class IdentityDatabase extends Database {
boolean firstUse = cursor.getInt(cursor.getColumnIndexOrThrow(FIRST_USE)) == 1;
IdentityKey identity = new IdentityKey(Base64.decode(serializedIdentity), 0);
return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval);
return new IdentityRecord(RecipientId.from(recipientId), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval);
}
public static class IdentityRecord {
private final Address address;
private final RecipientId recipientId;
private final IdentityKey identitykey;
private final VerifiedStatus verifiedStatus;
private final boolean firstUse;
private final long timestamp;
private final boolean nonblockingApproval;
private IdentityRecord(Address address,
private IdentityRecord(@NonNull RecipientId recipientId,
IdentityKey identitykey, VerifiedStatus verifiedStatus,
boolean firstUse, long timestamp, boolean nonblockingApproval)
{
this.address = address;
this.recipientId = recipientId;
this.identitykey = identitykey;
this.verifiedStatus = verifiedStatus;
this.firstUse = firstUse;
@ -185,8 +186,8 @@ public class IdentityDatabase extends Database {
this.nonblockingApproval = nonblockingApproval;
}
public Address getAddress() {
return address;
public RecipientId getRecipientId() {
return recipientId;
}
public IdentityKey getIdentityKey() {
@ -211,7 +212,7 @@ public class IdentityDatabase extends Database {
@Override
public @NonNull String toString() {
return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}";
return "{recipientId: " + recipientId + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}";
}
}

View File

@ -10,6 +10,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.List;
@ -41,7 +42,7 @@ public class MediaDatabase extends Database {
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.ADDRESS + " "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.RECIPIENT_ID + " "
+ "FROM " + AttachmentDatabase.TABLE_NAME + " LEFT JOIN " + MmsDatabase.TABLE_NAME
+ " ON " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " "
+ "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID
@ -88,27 +89,22 @@ public class MediaDatabase extends Database {
public static class MediaRecord {
private final DatabaseAttachment attachment;
private final Address address;
private final RecipientId recipientId;
private final long date;
private final boolean outgoing;
private MediaRecord(DatabaseAttachment attachment, @Nullable Address address, long date, boolean outgoing) {
this.attachment = attachment;
this.address = address;
this.date = date;
this.outgoing = outgoing;
private MediaRecord(DatabaseAttachment attachment, @NonNull RecipientId recipientId, long date, boolean outgoing) {
this.attachment = attachment;
this.recipientId = recipientId;
this.date = date;
this.outgoing = outgoing;
}
public static MediaRecord from(@NonNull Context context, @NonNull Cursor cursor) {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
List<DatabaseAttachment> attachments = attachmentDatabase.getAttachment(cursor);
String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID)));
boolean outgoing = MessagingDatabase.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)));
Address address = null;
if (serializedAddress != null) {
address = Address.fromSerialized(serializedAddress);
}
long date;
@ -118,7 +114,7 @@ public class MediaDatabase extends Database {
date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE_RECEIVED));
}
return new MediaRecord(attachments != null && attachments.size() > 0 ? attachments.get(0) : null, address, date, outgoing);
return new MediaRecord(attachments != null && attachments.size() > 0 ? attachments.get(0) : null, recipientId, date, outgoing);
}
public DatabaseAttachment getAttachment() {
@ -129,8 +125,8 @@ public class MediaDatabase extends Database {
return attachment.getContentType();
}
public @Nullable Address getAddress() {
return address;
public @NonNull RecipientId getRecipientId() {
return recipientId;
}
public long getDate() {

View File

@ -13,11 +13,10 @@ import org.thoughtcrime.securesms.database.documents.Document;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.ArrayList;
@ -40,41 +39,20 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
public abstract void markAsSent(long messageId, boolean secure);
public abstract void markUnidentified(long messageId, boolean unidentified);
public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) {
List<IdentityKeyMismatch> items = new ArrayList<IdentityKeyMismatch>() {{
add(new IdentityKeyMismatch(address, identityKey));
}};
IdentityKeyMismatchList document = new IdentityKeyMismatchList(items);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction();
try {
setDocument(database, messageId, MISMATCHED_IDENTITIES, document);
database.setTransactionSuccessful();
} catch (IOException ioe) {
Log.w(TAG, ioe);
} finally {
database.endTransaction();
}
}
public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
public void addMismatchedIdentity(long messageId, @NonNull RecipientId recipientId, IdentityKey identityKey) {
try {
addToDocument(messageId, MISMATCHED_IDENTITIES,
new IdentityKeyMismatch(address, identityKey),
new IdentityKeyMismatch(recipientId, identityKey),
IdentityKeyMismatchList.class);
} catch (IOException e) {
Log.w(TAG, e);
}
}
public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
public void removeMismatchedIdentity(long messageId, @NonNull RecipientId recipientId, IdentityKey identityKey) {
try {
removeFromDocument(messageId, MISMATCHED_IDENTITIES,
new IdentityKeyMismatch(address, identityKey),
new IdentityKeyMismatch(recipientId, identityKey),
IdentityKeyMismatchList.class);
} catch (IOException e) {
Log.w(TAG, e);
@ -178,16 +156,16 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
public static class SyncMessageId {
private final Address address;
private final long timetamp;
private final RecipientId recipientId;
private final long timetamp;
public SyncMessageId(Address address, long timetamp) {
this.address = address;
this.timetamp = timetamp;
public SyncMessageId(@NonNull RecipientId recipientId, long timetamp) {
this.recipientId = recipientId;
this.timetamp = timetamp;
}
public Address getAddress() {
return address;
public RecipientId getRecipientId() {
return recipientId;
}
public long getTimetamp() {

View File

@ -62,7 +62,7 @@ import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
@ -115,7 +115,7 @@ public class MmsDatabase extends MessagingDatabase {
THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " +
READ + " INTEGER DEFAULT 0, " + "m_id" + " TEXT, " + "sub" + " TEXT, " +
"sub_cs" + " INTEGER, " + BODY + " TEXT, " + PART_COUNT + " INTEGER, " +
"ct_t" + " TEXT, " + CONTENT_LOCATION + " TEXT, " + ADDRESS + " TEXT, " +
"ct_t" + " TEXT, " + CONTENT_LOCATION + " TEXT, " + RECIPIENT_ID + " INTEGER, " +
ADDRESS_DEVICE_ID + " INTEGER, " +
EXPIRY + " INTEGER, " + "m_cls" + " TEXT, " + MESSAGE_TYPE + " INTEGER, " +
"v" + " INTEGER, " + MESSAGE_SIZE + " INTEGER, " + "pri" + " INTEGER, " +
@ -148,7 +148,7 @@ public class MmsDatabase extends MessagingDatabase {
MESSAGE_BOX, READ,
CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE,
MESSAGE_SIZE, STATUS, TRANSACTION_ID,
BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
BODY, PART_COUNT, RECIPIENT_ID, ADDRESS_DEVICE_ID,
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING,
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE,
@ -179,8 +179,8 @@ public class MmsDatabase extends MessagingDatabase {
private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?";
private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
private final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache();
private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache("MmsDelivery");
private final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache("MmsRead");
public MmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@ -230,15 +230,15 @@ public class MmsDatabase extends MessagingDatabase {
boolean found = false;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
RecipientId ourRecipientId = messageId.getRecipientId();
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int status = deliveryReceipt ? GroupReceiptDatabase.STATUS_DELIVERED : GroupReceiptDatabase.STATUS_READ;
@ -249,7 +249,7 @@ public class MmsDatabase extends MessagingDatabase {
columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
DatabaseFactory.getGroupReceiptDatabase(context).update(ourAddress, id, status, timestamp);
DatabaseFactory.getGroupReceiptDatabase(context).update(ourRecipientId, id, status, timestamp);
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
@ -257,8 +257,8 @@ public class MmsDatabase extends MessagingDatabase {
}
if (!found) {
if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
}
} finally {
if (cursor != null)
@ -285,12 +285,13 @@ public class MmsDatabase extends MessagingDatabase {
}
}
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException {
private long getThreadIdFor(@NonNull IncomingMediaMessage retrieved) {
if (retrieved.getGroupId() != null) {
Recipient groupRecipients = Recipient.from(context, retrieved.getGroupId(), true);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(retrieved.getGroupId());
Recipient groupRecipients = Recipient.resolved(groupRecipientId);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
} else {
Recipient sender = Recipient.from(context, retrieved.getFrom(), true);
Recipient sender = Recipient.resolved(retrieved.getFrom());
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(sender);
}
}
@ -299,7 +300,7 @@ public class MmsDatabase extends MessagingDatabase {
String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null
? Util.toIsoString(notification.getFrom().getTextString())
: "";
Recipient recipient = Recipient.from(context, Address.fromExternal(context, fromString), false);
Recipient recipient = Recipient.external(context, fromString);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
}
@ -462,11 +463,11 @@ public class MmsDatabase extends MessagingDatabase {
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null);
while(cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
SyncMessageId syncMessageId = new SyncMessageId(RecipientId.from(cursor.getLong(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true);
result.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -492,13 +493,13 @@ public class MmsDatabase extends MessagingDatabase {
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, RECIPIENT_ID}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
RecipientId ourRecipientId = messageId.getRecipientId();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
@ -583,7 +584,7 @@ public class MmsDatabase extends MessagingDatabase {
cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)});
if (cursor != null && cursor.moveToNext()) {
return Optional.of(new MmsNotificationInfo(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)),
return Optional.of(new MmsNotificationInfo(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))));
@ -614,14 +615,14 @@ public class MmsDatabase extends MessagingDatabase {
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
boolean viewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(VIEW_ONCE)) == 1;
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID));
String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR));
RecipientId quoteAuthor = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR)));
String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_BODY));
boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE_MISSING)) == 1;
List<Attachment> quoteAttachments = Stream.of(associatedAttachments).filter(Attachment::isQuote).map(a -> (Attachment)a).toList();
@ -634,13 +635,13 @@ public class MmsDatabase extends MessagingDatabase {
.filterNot(previewAttachments::contains)
.map(a -> (Attachment)a).toList();
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
Recipient recipient = Recipient.resolved(RecipientId.from(recipientId));
List<NetworkFailure> networkFailures = new LinkedList<>();
List<IdentityKeyMismatch> mismatches = new LinkedList<>();
QuoteModel quote = null;
if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) {
quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments);
quote = new QuoteModel(quoteId, quoteAuthor, quoteText, quoteMissing, quoteAttachments);
}
if (!TextUtils.isEmpty(mismatchDocument)) {
@ -762,7 +763,7 @@ public class MmsDatabase extends MessagingDatabase {
try {
OutgoingMediaMessage request = getOutgoingMessage(messageId);
ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize());
contentValues.put(RECIPIENT_ID, request.getRecipient().getId().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis());
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT);
contentValues.put(THREAD_ID, getThreadIdForMessage(messageId));
@ -814,19 +815,13 @@ public class MmsDatabase extends MessagingDatabase {
throws MmsException
{
if (threadId == -1 || retrieved.isGroupMessage()) {
try {
threadId = getThreadIdFor(retrieved);
} catch (RecipientFormattingException e) {
Log.w("MmsDatabase", e);
if (threadId == -1)
throw new MmsException(e);
}
threadId = getThreadIdFor(retrieved);
}
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, retrieved.getSentTimeMillis());
contentValues.put(ADDRESS, retrieved.getFrom().serialize());
contentValues.put(RECIPIENT_ID, retrieved.getFrom().serialize());
contentValues.put(MESSAGE_BOX, mailbox);
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
@ -924,7 +919,8 @@ public class MmsDatabase extends MessagingDatabase {
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
if (notification.getFrom() != null) {
contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize());
Recipient recipient = Recipient.external(context, Util.toIsoString(notification.getFrom().getTextString()));
contentValues.put(RECIPIENT_ID, recipient.getId().serialize());
}
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE);
@ -980,8 +976,8 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
}
Map<Address, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
Map<Address, Long> earlyReadReceipts = earlyReadReceiptCache.remove(message.getSentTimeMillis());
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
Map<RecipientId, Long> earlyReadReceipts = earlyReadReceiptCache.remove(message.getSentTimeMillis());
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, message.getSentTimeMillis());
@ -994,7 +990,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(VIEW_ONCE, message.isViewOnce());
contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize());
contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize());
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum());
@ -1011,15 +1007,15 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener);
if (message.getRecipient().getAddress().isGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false);
if (message.getRecipient().requireAddress().isGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireAddress().toGroupString(), false);
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
receiptDatabase.insert(Stream.of(members).map(Recipient::getAddress).toList(),
receiptDatabase.insert(Stream.of(members).map(Recipient::getId).toList(),
messageId, defaultReceiptStatus, message.getSentTimeMillis());
for (Address address : earlyDeliveryReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1);
for (Address address : earlyReadReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_READ, -1);
for (RecipientId recipientId : earlyDeliveryReceipts.keySet()) receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1);
for (RecipientId recipientId : earlyReadReceipts.keySet()) receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_READ, -1);
}
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
@ -1171,7 +1167,7 @@ public class MmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingMediaMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + RECIPIENT_ID + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimeMillis()), message.getFrom().serialize(), String.valueOf(threadId)},
null, null, null, "1");
@ -1330,13 +1326,13 @@ public class MmsDatabase extends MessagingDatabase {
}
public static class MmsNotificationInfo {
private final Address from;
private final String contentLocation;
private final String transactionId;
private final int subscriptionId;
private final RecipientId from;
private final String contentLocation;
private final String transactionId;
private final int subscriptionId;
MmsNotificationInfo(@Nullable String from, String contentLocation, String transactionId, int subscriptionId) {
this.from = from == null ? null : Address.fromSerialized(from);
MmsNotificationInfo(@NonNull RecipientId from, String contentLocation, String transactionId, int subscriptionId) {
this.from = from;
this.contentLocation = contentLocation;
this.transactionId = transactionId;
this.subscriptionId = subscriptionId;
@ -1354,7 +1350,7 @@ public class MmsDatabase extends MessagingDatabase {
return subscriptionId;
}
public @Nullable Address getFrom() {
public @NonNull RecipientId getFrom() {
return from;
}
}
@ -1428,9 +1424,9 @@ public class MmsDatabase extends MessagingDatabase {
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
Recipient recipient = getRecipientFor(address);
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION));
String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID));
@ -1470,7 +1466,7 @@ public class MmsDatabase extends MessagingDatabase {
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
@ -1488,7 +1484,7 @@ public class MmsDatabase extends MessagingDatabase {
readReceiptCount = 0;
}
Recipient recipient = getRecipientFor(address);
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument);
List<NetworkFailure> networkFailures = getFailures(networkDocument);
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor);
@ -1506,18 +1502,6 @@ public class MmsDatabase extends MessagingDatabase {
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified);
}
private Recipient getRecipientFor(String serialized) {
Address address;
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
address = Address.UNKNOWN;
} else {
address = Address.fromSerialized(serialized);
}
return Recipient.from(context, address, true);
}
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {
if (!TextUtils.isEmpty(document)) {
try {
@ -1551,15 +1535,15 @@ public class MmsDatabase extends MessagingDatabase {
private @Nullable Quote getQuote(@NonNull Cursor cursor) {
long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_ID));
String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_AUTHOR));
RecipientId quoteAuthor = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_AUTHOR)));
String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_BODY));
boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_MISSING)) == 1;
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor);
List<? extends Attachment> quoteAttachments = Stream.of(attachments).filter(Attachment::isQuote).toList();
SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments);
if (quoteId > 0 && !TextUtils.isEmpty(quoteAuthor)) {
return new Quote(quoteId, Address.fromExternal(context, quoteAuthor), quoteText, quoteMissing, quoteDeck);
if (quoteId > 0 && !quoteAuthor.isUnknown()) {
return new Quote(quoteId, quoteAuthor, quoteText, quoteMissing, quoteDeck);
} else {
return null;
}

View File

@ -9,7 +9,7 @@ public interface MmsSmsColumns {
public static final String THREAD_ID = "thread_id";
public static final String READ = "read";
public static final String BODY = "body";
public static final String ADDRESS = "address";
public static final String RECIPIENT_ID = "address";
public static final String ADDRESS_DEVICE_ID = "address_device_id";
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
public static final String READ_RECEIPT_COUNT = "read_receipt_count";

View File

@ -27,6 +27,8 @@ import net.sqlcipher.database.SQLiteQueryBuilder;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet;
@ -44,7 +46,7 @@ public class MmsSmsDatabase extends Database {
private static final String[] PROJECTION = {MmsSmsColumns.ID, MmsSmsColumns.UNIQUE_ROW_ID,
SmsDatabase.BODY, SmsDatabase.TYPE,
MmsSmsColumns.THREAD_ID,
SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT,
SmsDatabase.RECIPIENT_ID, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT,
MmsSmsColumns.NORMALIZED_DATE_SENT,
MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX,
@ -77,7 +79,7 @@ public class MmsSmsDatabase extends Database {
super(context, databaseHelper);
}
public @Nullable MessageRecord getMessageFor(long timestamp, Address author) {
public @Nullable MessageRecord getMessageFor(long timestamp, RecipientId author) {
MmsSmsDatabase db = DatabaseFactory.getMmsSmsDatabase(context);
try (Cursor cursor = queryTables(PROJECTION, MmsSmsColumns.NORMALIZED_DATE_SENT + " = " + timestamp, null, null)) {
@ -86,8 +88,8 @@ public class MmsSmsDatabase extends Database {
MessageRecord messageRecord;
while ((messageRecord = reader.getNext()) != null) {
if ((Util.isOwnNumber(context, author) && messageRecord.isOutgoing()) ||
(!Util.isOwnNumber(context, author) && messageRecord.getIndividualRecipient().getAddress().equals(author)))
if ((Recipient.resolved(author).isLocalNumber() && messageRecord.isOutgoing()) ||
(!Recipient.resolved(author).isLocalNumber() && messageRecord.getIndividualRecipient().getId().equals(author)))
{
return messageRecord;
}
@ -164,19 +166,18 @@ public class MmsSmsDatabase extends Database {
DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, false, true);
}
public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull Address address) {
public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_SENT, MmsSmsColumns.ADDRESS }, selection, order, null)) {
String serializedAddress = address.serialize();
boolean isOwnNumber = Util.isOwnNumber(context, address);
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_SENT, MmsSmsColumns.RECIPIENT_ID}, selection, order, null)) {
boolean isOwnNumber = Recipient.resolved(recipientId).isLocalNumber();
while (cursor != null && cursor.moveToNext()) {
boolean quoteIdMatches = cursor.getLong(0) == quoteId;
boolean addressMatches = serializedAddress.equals(cursor.getString(1));
boolean quoteIdMatches = cursor.getLong(0) == quoteId;
boolean recipientIdMatches = recipientId.equals(RecipientId.from(cursor.getLong(1)));
if (quoteIdMatches && (addressMatches || isOwnNumber)) {
if (quoteIdMatches && (recipientIdMatches || isOwnNumber)) {
return cursor.getPosition();
}
}
@ -184,19 +185,18 @@ public class MmsSmsDatabase extends Database {
return -1;
}
public int getMessagePositionInConversation(long threadId, long receivedTimestamp, @NonNull Address address) {
public int getMessagePositionInConversation(long threadId, long receivedTimestamp, @NonNull RecipientId recipientId) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_RECEIVED, MmsSmsColumns.ADDRESS }, selection, order, null)) {
String serializedAddress = address.serialize();
boolean isOwnNumber = Util.isOwnNumber(context, address);
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_RECEIVED, MmsSmsColumns.RECIPIENT_ID}, selection, order, null)) {
boolean isOwnNumber = Recipient.resolved(recipientId).isLocalNumber();
while (cursor != null && cursor.moveToNext()) {
boolean timestampMatches = cursor.getLong(0) == receivedTimestamp;
boolean addressMatches = serializedAddress.equals(cursor.getString(1));
boolean timestampMatches = cursor.getLong(0) == receivedTimestamp;
boolean recipientIdMatches = recipientId.equals(RecipientId.from(cursor.getLong(1)));
if (timestampMatches && (addressMatches || isOwnNumber)) {
if (timestampMatches && (recipientIdMatches || isOwnNumber)) {
return cursor.getPosition();
}
}
@ -255,7 +255,7 @@ public class MmsSmsDatabase extends Database {
"'" + AttachmentDatabase.STICKER_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID +
")) AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS,
SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
SmsDatabase.TYPE, SmsDatabase.RECIPIENT_ID, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
@ -282,7 +282,7 @@ public class MmsSmsDatabase extends Database {
+ " AS " + MmsSmsColumns.UNIQUE_ROW_ID,
"NULL AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS,
SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
SmsDatabase.TYPE, SmsDatabase.RECIPIENT_ID, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
@ -318,7 +318,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsSmsColumns.READ);
mmsColumnsPresent.add(MmsSmsColumns.THREAD_ID);
mmsColumnsPresent.add(MmsSmsColumns.BODY);
mmsColumnsPresent.add(MmsSmsColumns.ADDRESS);
mmsColumnsPresent.add(MmsSmsColumns.RECIPIENT_ID);
mmsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
mmsColumnsPresent.add(MmsSmsColumns.DELIVERY_RECEIPT_COUNT);
mmsColumnsPresent.add(MmsSmsColumns.READ_RECEIPT_COUNT);
@ -375,7 +375,7 @@ public class MmsSmsDatabase extends Database {
Set<String> smsColumnsPresent = new HashSet<>();
smsColumnsPresent.add(MmsSmsColumns.ID);
smsColumnsPresent.add(MmsSmsColumns.BODY);
smsColumnsPresent.add(MmsSmsColumns.ADDRESS);
smsColumnsPresent.add(MmsSmsColumns.RECIPIENT_ID);
smsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
smsColumnsPresent.add(MmsSmsColumns.READ);
smsColumnsPresent.add(MmsSmsColumns.THREAD_ID);

View File

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
@ -32,40 +33,53 @@ public class RecipientDatabase extends Database {
private static final String TAG = RecipientDatabase.class.getSimpleName();
static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id";
static final String ADDRESS = "recipient_ids";
private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate";
static final String TABLE_NAME = "recipient";
public static final String ID = "_id";
private static final String UUID = "uuid";
static final String PHONE = "phone";
static final String EMAIL = "email";
static final String GROUP_ID = "group_id";
private static final String BLOCKED = "blocked";
private static final String MESSAGE_RINGTONE = "message_ringtone";
private static final String MESSAGE_VIBRATE = "message_vibrate";
private static final String CALL_RINGTONE = "call_ringtone";
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
private static final String MUTE_UNTIL = "mute_until";
private static final String COLOR = "color";
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
private static final String EXPIRE_MESSAGES = "expire_messages";
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
static final String REGISTERED = "registered";
private static final String PROFILE_KEY = "profile_key";
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
private static final String SYSTEM_PHOTO_URI = "system_contact_photo";
private static final String SYSTEM_PHOTO_URI = "system_photo_uri";
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
private static final String PROFILE_KEY = "profile_key";
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
private static final String PROFILE_SHARING = "profile_sharing_approval";
private static final String CALL_RINGTONE = "call_ringtone";
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
private static final String PROFILE_SHARING = "profile_sharing";
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
private static final String[] RECIPIENT_PROJECTION = new String[] {
BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
UUID, PHONE, EMAIL, GROUP_ID,
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
UNIDENTIFIED_ACCESS_MODE,
FORCE_SMS_SELECTION,
};
private static final String[] ID_PROJECTION = new String[] { ID };
private static Address addressFromCursor(Cursor cursor) {
String phone = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
return phone != null ? Address.fromSerialized(phone) : email != null ? Address.fromSerialized(email) : Address.fromSerialized(groupId);
}
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList();
@ -125,40 +139,105 @@ public class RecipientDatabase extends Database {
}
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
ADDRESS + " TEXT UNIQUE, " +
BLOCK + " INTEGER DEFAULT 0," +
NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
MUTE_UNTIL + " INTEGER DEFAULT 0, " +
COLOR + " TEXT DEFAULT NULL, " +
SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " +
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " +
REGISTERED + " INTEGER DEFAULT 0, " +
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " +
PROFILE_KEY + " TEXT DEFAULT NULL, " +
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);";
"CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
UUID + " TEXT UNIQUE DEFAULT NULL, " +
PHONE + " TEXT UNIQUE DEFAULT NULL, " +
EMAIL + " TEXT UNIQUE DEFAULT NULL, " +
GROUP_ID + " TEXT UNIQUE DEFAULT NULL, " +
BLOCKED + " INTEGER DEFAULT 0," +
MESSAGE_RINGTONE + " TEXT DEFAULT NULL, " +
MESSAGE_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
MUTE_UNTIL + " INTEGER DEFAULT 0, " +
COLOR + " TEXT DEFAULT NULL, " +
SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " +
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " +
REGISTERED + " INTEGER DEFAULT " + RegisteredState.UNKNOWN.getId() + ", " +
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " +
PROFILE_KEY + " TEXT DEFAULT NULL, " +
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);";
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}
public RecipientId getOrInsertFromE164(@NonNull String e164) {
if (TextUtils.isEmpty(e164)) {
throw new AssertionError("Phone number cannot be empty.");
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String query = PHONE + " = ?";
String[] args = new String[] { e164 };
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return RecipientId.from(cursor.getLong(0));
} else {
ContentValues values = new ContentValues();
values.put(PHONE, e164);
long id = db.insert(TABLE_NAME, null, values);
return RecipientId.from(id);
}
}
}
public RecipientId getOrInsertFromEmail(@NonNull String email) {
if (TextUtils.isEmpty(email)) {
throw new AssertionError("Email cannot be empty.");
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String query = EMAIL + " = ?";
String[] args = new String[] { email };
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return RecipientId.from(cursor.getLong(0));
} else {
ContentValues values = new ContentValues();
values.put(EMAIL, email);
long id = db.insert(TABLE_NAME, null, values);
return RecipientId.from(id);
}
}
}
public RecipientId getOrInsertFromGroupId(@NonNull String groupId) {
if (TextUtils.isEmpty(groupId)) {
throw new AssertionError("GroupId cannot be empty.");
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String query = GROUP_ID + " = ?";
String[] args = new String[] { groupId };
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return RecipientId.from(cursor.getLong(0));
} else {
ContentValues values = new ContentValues();
values.put(GROUP_ID, groupId);
long id = db.insert(TABLE_NAME, null, values);
return RecipientId.from(id);
}
}
}
public Cursor getBlocked() {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
return database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1",
return database.query(TABLE_NAME, ID_PROJECTION, BLOCKED + " = 1",
null, null, null, null, null);
}
@ -168,40 +247,40 @@ public class RecipientDatabase extends Database {
public RecipientReader getRecipientsWithNotificationChannels() {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, NOTIFICATION_CHANNEL + " NOT NULL",
Cursor cursor = database.query(TABLE_NAME, ID_PROJECTION, NOTIFICATION_CHANNEL + " NOT NULL",
null, null, null, null, null);
return new RecipientReader(context, cursor);
}
public Optional<RecipientSettings> getRecipientSettings(@NonNull Address address) {
public @NonNull RecipientSettings getRecipientSettings(@NonNull RecipientId id) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null);
String query = ID + " = ?";
String[] args = new String[] { id.serialize() };
try (Cursor cursor = database.query(TABLE_NAME, null, query, args, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
return getRecipientSettings(cursor);
} else {
// TODO: Maybe not make this an error?
throw new AssertionError("Couldn't find recipient!");
}
return Optional.absent();
} finally {
if (cursor != null) cursor.close();
}
}
Optional<RecipientSettings> getRecipientSettings(@NonNull Cursor cursor) {
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
@NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
Address address = addressFromCursor(cursor);
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(MESSAGE_VIBRATE));
int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE));
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(MESSAGE_EXPIRATION_TIME));
int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
@ -234,18 +313,18 @@ public class RecipientDatabase extends Database {
}
}
return Optional.of(new RecipientSettings(blocked, muteUntil,
VibrateState.fromId(messageVibrateState),
VibrateState.fromId(callVibrateState),
Util.uri(messageRingtone), Util.uri(callRingtone),
color, seenInviteReminder,
defaultSubscriptionId, expireMessages,
RegisteredState.fromId(registeredState),
profileKey, systemDisplayName, systemContactPhoto,
systemPhoneLabel, systemContactUri,
signalProfileName, signalProfileAvatar, profileSharing,
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
forceSmsSelection));
return new RecipientSettings(RecipientId.from(id), address, blocked, muteUntil,
VibrateState.fromId(messageVibrateState),
VibrateState.fromId(callVibrateState),
Util.uri(messageRingtone), Util.uri(callRingtone),
color, seenInviteReminder,
defaultSubscriptionId, expireMessages,
RegisteredState.fromId(registeredState),
profileKey, systemDisplayName, systemContactPhoto,
systemPhoneLabel, systemContactUri,
signalProfileName, signalProfileAvatar, profileSharing,
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
forceSmsSelection);
}
public BulkOperationsHandle resetAllSystemContactInfo() {
@ -263,187 +342,185 @@ public class RecipientDatabase extends Database {
return new BulkOperationsHandle(database);
}
public void setColor(@NonNull Recipient recipient, @NonNull MaterialColor color) {
public void setColor(@NonNull RecipientId id, @NonNull MaterialColor color) {
ContentValues values = new ContentValues();
values.put(COLOR, color.serialize());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setColor(color);
update(id, values);
Recipient.live(id).refresh();
}
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
public void setDefaultSubscriptionId(@NonNull RecipientId id, int defaultSubscriptionId) {
ContentValues values = new ContentValues();
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId));
update(id, values);
Recipient.live(id).refresh();
}
public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) {
public void setForceSmsSelection(@NonNull RecipientId id, boolean forceSmsSelection) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setForceSmsSelection(forceSmsSelection);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void setBlocked(@NonNull Recipient recipient, boolean blocked) {
public void setBlocked(@NonNull RecipientId id, boolean blocked) {
ContentValues values = new ContentValues();
values.put(BLOCK, blocked ? 1 : 0);
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setBlocked(blocked);
values.put(BLOCKED, blocked ? 1 : 0);
update(id, values);
Recipient.live(id).refresh();
}
public void setMessageRingtone(@NonNull Recipient recipient, @Nullable Uri notification) {
public void setMessageRingtone(@NonNull RecipientId id, @Nullable Uri notification) {
ContentValues values = new ContentValues();
values.put(NOTIFICATION, notification == null ? null : notification.toString());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setMessageRingtone(notification);
values.put(MESSAGE_RINGTONE, notification == null ? null : notification.toString());
update(id, values);
Recipient.live(id).refresh();
}
public void setCallRingtone(@NonNull Recipient recipient, @Nullable Uri ringtone) {
public void setCallRingtone(@NonNull RecipientId id, @Nullable Uri ringtone) {
ContentValues values = new ContentValues();
values.put(CALL_RINGTONE, ringtone == null ? null : ringtone.toString());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setCallRingtone(ringtone);
update(id, values);
Recipient.live(id).refresh();
}
public void setMessageVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) {
public void setMessageVibrate(@NonNull RecipientId id, @NonNull VibrateState enabled) {
ContentValues values = new ContentValues();
values.put(VIBRATE, enabled.getId());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setMessageVibrate(enabled);
values.put(MESSAGE_VIBRATE, enabled.getId());
update(id, values);
Recipient.live(id).refresh();
}
public void setCallVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) {
public void setCallVibrate(@NonNull RecipientId id, @NonNull VibrateState enabled) {
ContentValues values = new ContentValues();
values.put(CALL_VIBRATE, enabled.getId());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setCallVibrate(enabled);
update(id, values);
Recipient.live(id).refresh();
}
public void setMuted(@NonNull Recipient recipient, long until) {
public void setMuted(@NonNull RecipientId id, long until) {
ContentValues values = new ContentValues();
values.put(MUTE_UNTIL, until);
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setMuted(until);
update(id, values);
Recipient.live(id).refresh();
}
public void setSeenInviteReminder(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean seen) {
public void setSeenInviteReminder(@NonNull RecipientId id, @SuppressWarnings("SameParameterValue") boolean seen) {
ContentValues values = new ContentValues(1);
values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0);
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setHasSeenInviteReminder(seen);
update(id, values);
Recipient.live(id).refresh();
}
public void setExpireMessages(@NonNull Recipient recipient, int expiration) {
recipient.setExpireMessages(expiration);
public void setExpireMessages(@NonNull RecipientId id, int expiration) {
ContentValues values = new ContentValues(1);
values.put(EXPIRE_MESSAGES, expiration);
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setExpireMessages(expiration);
values.put(MESSAGE_EXPIRATION_TIME, expiration);
update(id, values);
Recipient.live(id).refresh();
}
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
public void setUnidentifiedAccessMode(@NonNull RecipientId id, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
ContentValues values = new ContentValues(1);
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
update(id, values);
Recipient.live(id).refresh();
}
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
public void setProfileKey(@NonNull RecipientId id, @Nullable byte[] profileKey) {
ContentValues values = new ContentValues(1);
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setProfileKey(profileKey);
update(id, values);
Recipient.live(id).refresh();
}
public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
public void setProfileName(@NonNull RecipientId id, @Nullable String profileName) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(SIGNAL_PROFILE_NAME, profileName);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setProfileName(profileName);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
public void setProfileAvatar(@NonNull RecipientId id, @Nullable String profileAvatar) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setProfileAvatar(profileAvatar);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void setProfileSharing(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean enabled) {
public void setProfileSharing(@NonNull RecipientId id, @SuppressWarnings("SameParameterValue") boolean enabled) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.setProfileSharing(enabled);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) {
public void setNotificationChannel(@NonNull RecipientId id, @Nullable String notificationChannel) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(NOTIFICATION_CHANNEL, notificationChannel);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.setNotificationChannel(notificationChannel);
update(id, contentValues);
Recipient.live(id).refresh();
}
public Set<Address> getAllAddresses() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Set<Address> 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[] { ID, UUID, PHONE, EMAIL, GROUP_ID }, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
results.add(Address.fromExternal(context, cursor.getString(0)));
results.add(addressFromCursor(cursor));
}
}
return results;
}
public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) {
public void setRegistered(@NonNull RecipientId id, RegisteredState registeredState) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, registeredState.getId());
updateOrInsert(recipient.getAddress(), contentValues);
recipient.setRegistered(registeredState);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void setRegistered(@NonNull List<Address> activeAddresses,
@NonNull List<Address> inactiveAddresses)
public void setRegistered(@NonNull List<RecipientId> activeIds,
@NonNull List<RecipientId> inactiveIds)
{
for (Address activeAddress : activeAddresses) {
for (RecipientId activeId : activeIds) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
updateOrInsert(activeAddress, contentValues);
Recipient.applyCached(activeAddress, recipient -> recipient.setRegistered(RegisteredState.REGISTERED));
update(activeId, contentValues);
Recipient.live(activeId).refresh();
}
for (Address inactiveAddress : inactiveAddresses) {
for (RecipientId inactiveId : inactiveIds) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
updateOrInsert(inactiveAddress, contentValues);
Recipient.applyCached(inactiveAddress, recipient -> recipient.setRegistered(RegisteredState.NOT_REGISTERED));
update(inactiveId, contentValues);
Recipient.live(inactiveId).refresh();
}
}
public List<Address> getRegistered() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<Address> results = new LinkedList<>();
public List<RecipientId> getRegistered() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<RecipientId> results = new LinkedList<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
results.add(Address.fromSerialized(cursor.getString(0)));
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
}
}
return results;
}
public List<Address> getSystemContacts() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<Address> results = new LinkedList<>();
public List<RecipientId> getSystemContacts() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<RecipientId> results = new LinkedList<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
results.add(Address.fromSerialized(cursor.getString(0)));
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))));
}
}
@ -451,93 +528,61 @@ public class RecipientDatabase extends Database {
}
public void updateSystemContactColors(@NonNull ColorUpdater updater) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Map<Address, MaterialColor> updates = new HashMap<>();
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Map<RecipientId, MaterialColor> updates = new HashMap<>();
db.beginTransaction();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ID, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
ContentValues contentValues = new ContentValues(1);
contentValues.put(COLOR, newColor.serialize());
db.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[]{address.serialize()});
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] { String.valueOf(id) });
updates.put(address, newColor);
updates.put(RecipientId.from(id), newColor);
}
} finally {
db.setTransactionSuccessful();
db.endTransaction();
Stream.of(updates.entrySet()).forEach(entry -> {
Recipient.applyCached(entry.getKey(), recipient -> {
recipient.setColor(entry.getValue());
});
});
Stream.of(updates.entrySet()).forEach(entry -> Recipient.live(entry.getKey()).refresh());
}
}
// XXX This shouldn't be here, and is just a temporary workaround
public RegisteredState isRegistered(@NonNull Address address) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {REGISTERED}, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) return RegisteredState.fromId(cursor.getInt(0));
else return RegisteredState.UNKNOWN;
}
}
private void updateOrInsert(Address address, ContentValues contentValues) {
private void update(@NonNull RecipientId id, ContentValues contentValues) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction();
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?",
new String[] {address.serialize()});
if (updated < 1) {
contentValues.put(ADDRESS, address.serialize());
database.insert(TABLE_NAME, null, contentValues);
}
database.setTransactionSuccessful();
database.endTransaction();
database.update(TABLE_NAME, contentValues, ID + " = ?", new String[] { id.serialize() });
}
public class BulkOperationsHandle {
private final SQLiteDatabase database;
private final Map<Address, PendingContactInfo> pendingContactInfoMap = new HashMap<>();
private final Map<RecipientId, PendingContactInfo> pendingContactInfoMap = new HashMap<>();
BulkOperationsHandle(SQLiteDatabase database) {
this.database = database;
}
public void setSystemContactInfo(@NonNull Address address, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) {
public void setSystemContactInfo(@NonNull RecipientId id, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(SYSTEM_DISPLAY_NAME, displayName);
contentValues.put(SYSTEM_PHOTO_URI, photoUri);
contentValues.put(SYSTEM_PHONE_LABEL, systemPhoneLabel);
contentValues.put(SYSTEM_CONTACT_URI, systemContactUri);
updateOrInsert(address, contentValues);
pendingContactInfoMap.put(address, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri));
update(id, contentValues);
pendingContactInfoMap.put(id, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri));
}
public void finish() {
database.setTransactionSuccessful();
database.endTransaction();
Stream.of(pendingContactInfoMap.entrySet())
.forEach(entry -> Recipient.applyCached(entry.getKey(), recipient -> {
recipient.setName(entry.getValue().displayName);
recipient.setSystemContactPhoto(Util.uri(entry.getValue().photoUri));
recipient.setCustomLabel(entry.getValue().phoneLabel);
recipient.setContactUri(Util.uri(entry.getValue().contactUri));
}));
Stream.of(pendingContactInfoMap.entrySet()).forEach(entry -> Recipient.live(entry.getKey()).refresh());
}
}
@ -546,6 +591,8 @@ public class RecipientDatabase extends Database {
}
public static class RecipientSettings {
private final RecipientId id;
private final Address address;
private final boolean blocked;
private final long muteUntil;
private final VibrateState messageVibrateState;
@ -569,7 +616,8 @@ public class RecipientDatabase extends Database {
private final UnidentifiedAccessMode unidentifiedAccessMode;
private final boolean forceSmsSelection;
RecipientSettings(boolean blocked, long muteUntil,
RecipientSettings(@NonNull RecipientId id,
@NonNull Address address, boolean blocked, long muteUntil,
@NonNull VibrateState messageVibrateState,
@NonNull VibrateState callVibrateState,
@Nullable Uri messageRingtone,
@ -591,6 +639,8 @@ public class RecipientDatabase extends Database {
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
boolean forceSmsSelection)
{
this.id = id;
this.address = address;
this.blocked = blocked;
this.muteUntil = muteUntil;
this.messageVibrateState = messageVibrateState;
@ -615,6 +665,14 @@ public class RecipientDatabase extends Database {
this.forceSmsSelection = forceSmsSelection;
}
public RecipientId getId() {
return id;
}
public @NonNull Address getAddress() {
return address;
}
public @Nullable MaterialColor getColor() {
return color;
}
@ -715,8 +773,8 @@ public class RecipientDatabase extends Database {
}
public @NonNull Recipient getCurrent() {
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
return Recipient.from(context, Address.fromSerialized(serialized), false);
RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
return Recipient.live(id).get();
}
public @Nullable Recipient getNext() {

View File

@ -20,12 +20,12 @@ public class SearchDatabase extends Database {
public static final String SMS_FTS_TABLE_NAME = "sms_fts";
public static final String MMS_FTS_TABLE_NAME = "mms_fts";
public static final String ID = "rowid";
public static final String BODY = MmsSmsColumns.BODY;
public static final String THREAD_ID = MmsSmsColumns.THREAD_ID;
public static final String SNIPPET = "snippet";
public static final String CONVERSATION_ADDRESS = "conversation_address";
public static final String MESSAGE_ADDRESS = "message_address";
public static final String ID = "rowid";
public static final String BODY = MmsSmsColumns.BODY;
public static final String THREAD_ID = MmsSmsColumns.THREAD_ID;
public static final String SNIPPET = "snippet";
public static final String CONVERSATION_RECIPIENT = "conversation_recipient";
public static final String MESSAGE_RECIPIENT = "message_recipient";
public static final String[] CREATE_TABLE = {
"CREATE VIRTUAL TABLE " + SMS_FTS_TABLE_NAME + " USING fts5(" + BODY + ", " + THREAD_ID + " UNINDEXED, content=" + SmsDatabase.TABLE_NAME + ", content_rowid=" + SmsDatabase.ID + ");",
@ -58,8 +58,8 @@ public class SearchDatabase extends Database {
private static final String MESSAGES_QUERY =
"SELECT " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
SMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
@ -69,8 +69,8 @@ public class SearchDatabase extends Database {
"WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? " +
"UNION ALL " +
"SELECT " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
MMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
@ -83,8 +83,8 @@ public class SearchDatabase extends Database {
private static final String MESSAGES_FOR_THREAD_QUERY =
"SELECT " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
SMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
@ -94,8 +94,8 @@ public class SearchDatabase extends Database {
"WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? AND " + SmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " +
"UNION ALL " +
"SELECT " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " AS " + CONVERSATION_RECIPIENT + ", " +
MmsSmsColumns.RECIPIENT_ID + " AS " + MESSAGE_RECIPIENT + ", " +
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
MMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +

View File

@ -11,6 +11,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -24,37 +25,37 @@ public class SessionDatabase extends Database {
public static final String TABLE_NAME = "sessions";
private static final String ID = "_id";
public static final String ADDRESS = "address";
public static final String DEVICE = "device";
public static final String RECORD = "record";
private static final String ID = "_id";
public static final String RECIPIENT_ID = "address";
public static final String DEVICE = "device";
public static final String RECORD = "record";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
"(" + ID + " INTEGER PRIMARY KEY, " + ADDRESS + " TEXT NOT NULL, " +
"(" + ID + " INTEGER PRIMARY KEY, " + RECIPIENT_ID + " INTEGER NOT NULL, " +
DEVICE + " INTEGER NOT NULL, " + RECORD + " BLOB NOT NULL, " +
"UNIQUE(" + ADDRESS + "," + DEVICE + ") ON CONFLICT REPLACE);";
"UNIQUE(" + RECIPIENT_ID + "," + DEVICE + ") ON CONFLICT REPLACE);";
SessionDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}
public void store(@NonNull Address address, int deviceId, @NonNull SessionRecord record) {
public void store(@NonNull RecipientId recipientId, int deviceId, @NonNull SessionRecord record) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ADDRESS, address.serialize());
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(DEVICE, deviceId);
values.put(RECORD, record.serialize());
database.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
public @Nullable SessionRecord load(@NonNull Address address, int deviceId) {
public @Nullable SessionRecord load(@NonNull RecipientId recipientId, int deviceId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
try (Cursor cursor = database.query(TABLE_NAME, new String[]{RECORD},
ADDRESS + " = ? AND " + DEVICE + " = ?",
new String[] {address.serialize(), String.valueOf(deviceId)},
RECIPIENT_ID + " = ? AND " + DEVICE + " = ?",
new String[] {recipientId.serialize(), String.valueOf(deviceId)},
null, null, null))
{
if (cursor != null && cursor.moveToFirst()) {
@ -69,18 +70,18 @@ public class SessionDatabase extends Database {
return null;
}
public @NonNull List<SessionRow> getAllFor(@NonNull Address address) {
public @NonNull List<SessionRow> getAllFor(@NonNull RecipientId recipientId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
List<SessionRow> results = new LinkedList<>();
try (Cursor cursor = database.query(TABLE_NAME, null,
ADDRESS + " = ?",
new String[] {address.serialize()},
RECIPIENT_ID + " = ?",
new String[] {recipientId.serialize()},
null, null, null))
{
while (cursor != null && cursor.moveToNext()) {
try {
results.add(new SessionRow(address,
results.add(new SessionRow(recipientId,
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)),
new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD)))));
} catch (IOException e) {
@ -99,7 +100,7 @@ public class SessionDatabase extends Database {
try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
try {
results.add(new SessionRow(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
results.add(new SessionRow(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))),
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)),
new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD)))));
} catch (IOException e) {
@ -111,13 +112,13 @@ public class SessionDatabase extends Database {
return results;
}
public @NonNull List<Integer> getSubDevices(@NonNull Address address) {
public @NonNull List<Integer> getSubDevices(@NonNull RecipientId recipientId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
List<Integer> results = new LinkedList<>();
try (Cursor cursor = database.query(TABLE_NAME, new String[] {DEVICE},
ADDRESS + " = ?",
new String[] {address.serialize()},
RECIPIENT_ID + " = ?",
new String[] {recipientId.serialize()},
null, null, null))
{
while (cursor != null && cursor.moveToNext()) {
@ -132,31 +133,31 @@ public class SessionDatabase extends Database {
return results;
}
public void delete(@NonNull Address address, int deviceId) {
public void delete(@NonNull RecipientId recipientId, int deviceId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, ADDRESS + " = ? AND " + DEVICE + " = ?",
new String[] {address.serialize(), String.valueOf(deviceId)});
database.delete(TABLE_NAME, RECIPIENT_ID + " = ? AND " + DEVICE + " = ?",
new String[] {recipientId.serialize(), String.valueOf(deviceId)});
}
public void deleteAllFor(@NonNull Address address) {
public void deleteAllFor(@NonNull RecipientId recipientId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, ADDRESS + " = ?", new String[] {address.serialize()});
database.delete(TABLE_NAME, RECIPIENT_ID + " = ?", new String[] {recipientId.serialize()});
}
public static final class SessionRow {
private final Address address;
private final RecipientId recipientId;
private final int deviceId;
private final SessionRecord record;
public SessionRow(Address address, int deviceId, SessionRecord record) {
this.address = address;
this.deviceId = deviceId;
this.record = record;
public SessionRow(@NonNull RecipientId recipientId, int deviceId, SessionRecord record) {
this.recipientId = recipientId;
this.deviceId = deviceId;
this.record = record;
}
public Address getAddress() {
return address;
public RecipientId getRecipientId() {
return recipientId;
}
public int getDeviceId() {

View File

@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@ -73,7 +74,7 @@ public class SmsDatabase extends MessagingDatabase {
public static final String SERVICE_CENTER = "service_center";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " integer PRIMARY KEY, " +
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " +
THREAD_ID + " INTEGER, " + RECIPIENT_ID + " INTEGER, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " +
DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " +
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " +
@ -91,7 +92,7 @@ public class SmsDatabase extends MessagingDatabase {
};
private static final String[] MESSAGE_PROJECTION = new String[] {
ID, THREAD_ID, ADDRESS, ADDRESS_DEVICE_ID, PERSON,
ID, THREAD_ID, RECIPIENT_ID, ADDRESS_DEVICE_ID, PERSON,
DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
PROTOCOL, READ, STATUS, TYPE,
@ -100,8 +101,8 @@ public class SmsDatabase extends MessagingDatabase {
NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED
};
private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache();
private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache("SmsDelivery");
private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache("SmsRead");
public SmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@ -310,17 +311,17 @@ public class SmsDatabase extends MessagingDatabase {
boolean foundMessage = false;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE},
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
Address theirAddress = messageId.getAddress();
Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
RecipientId theirRecipientId = messageId.getRecipientId();
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
if (ourAddress.equals(theirAddress)) {
if (outRecipientId.equals(theirRecipientId)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
database.execSQL("UPDATE " + TABLE_NAME +
@ -336,8 +337,8 @@ public class SmsDatabase extends MessagingDatabase {
}
if (!foundMessage) {
if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
}
} finally {
@ -352,15 +353,15 @@ public class SmsDatabase extends MessagingDatabase {
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE, EXPIRES_IN, EXPIRE_STARTED},
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, EXPIRES_IN, EXPIRE_STARTED},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null);
while (cursor.moveToNext()) {
Address theirAddress = messageId.getAddress();
Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
RecipientId theirRecipientId = messageId.getRecipientId();
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
if (ourAddress.equals(theirAddress)) {
if (outRecipientId.equals(theirRecipientId)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
@ -405,11 +406,11 @@ public class SmsDatabase extends MessagingDatabase {
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_ID, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null);
while (cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
SyncMessageId syncMessageId = new SyncMessageId(RecipientId.from(cursor.getLong(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false);
results.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -461,7 +462,7 @@ public class SmsDatabase extends MessagingDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE);
contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize());
contentValues.put(RECIPIENT_ID, record.getIndividualRecipient().getId().serialize());
contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(DATE_SENT, record.getDateSent());
@ -485,24 +486,24 @@ public class SmsDatabase extends MessagingDatabase {
}
}
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull Address address) {
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address) {
return insertCallLog(address, Types.INCOMING_CALL_TYPE, false);
}
public @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull Address address) {
public @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull RecipientId address) {
return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false);
}
public @NonNull Pair<Long, Long> insertMissedCall(@NonNull Address address) {
public @NonNull Pair<Long, Long> insertMissedCall(@NonNull RecipientId address) {
return insertCallLog(address, Types.MISSED_CALL_TYPE, true);
}
private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) {
Recipient recipient = Recipient.from(context, address, true);
private @NonNull Pair<Long, Long> insertCallLog(@NonNull RecipientId recipientId, long type, boolean unread) {
Recipient recipient = Recipient.resolved(recipientId);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, address.serialize());
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
@ -547,14 +548,15 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Recipient recipient = Recipient.from(context, message.getSender(), true);
Recipient recipient = Recipient.resolved(message.getSender());
Recipient groupRecipient;
if (message.getGroupId() == null) {
groupRecipient = null;
} else {
groupRecipient = Recipient.from(context, message.getGroupId(), true);
RecipientId id = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(message.getGroupId().serialize());
groupRecipient = Recipient.resolved(id);
}
boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
@ -567,7 +569,7 @@ public class SmsDatabase extends MessagingDatabase {
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, message.getSender().serialize());
values.put(RECIPIENT_ID, message.getSender().serialize());
values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId());
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, message.getSentTimestampMillis());
@ -602,7 +604,7 @@ public class SmsDatabase extends MessagingDatabase {
}
if (message.getSubscriptionId() != -1) {
DatabaseFactory.getRecipientDatabase(context).setDefaultSubscriptionId(recipient, message.getSubscriptionId());
DatabaseFactory.getRecipientDatabase(context).setDefaultSubscriptionId(recipient.getId(), message.getSubscriptionId());
}
notifyConversationListeners(threadId);
@ -632,12 +634,12 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Address address = message.getRecipient().getAddress();
Map<Address, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
Map<Address, Long> earlyReadReceipts = earlyReadReceiptCache.remove(date);
RecipientId recipientId = message.getRecipient().getId();
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
Map<RecipientId, Long> earlyReadReceipts = earlyReadReceiptCache.remove(date);
ContentValues contentValues = new ContentValues(6);
contentValues.put(ADDRESS, address.serialize());
contentValues.put(RECIPIENT_ID, recipientId.serialize());
contentValues.put(THREAD_ID, threadId);
contentValues.put(BODY, message.getMessageBody());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
@ -650,7 +652,7 @@ public class SmsDatabase extends MessagingDatabase {
contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum());
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
long messageId = db.insert(TABLE_NAME, null, contentValues);
if (insertListener != null) {
insertListener.onComplete();
@ -724,7 +726,7 @@ public class SmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingTextMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + RECIPIENT_ID + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender().serialize(), String.valueOf(threadId)},
null, null, null, "1");
@ -783,19 +785,19 @@ public class SmsDatabase extends MessagingDatabase {
}
/*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) {
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " +
PERSON + ", " +
DATE_SENT + ", " +
DATE_RECEIVED + ", " +
PROTOCOL + ", " +
READ + ", " +
STATUS + ", " +
TYPE + ", " +
REPLY_PATH_PRESENT + ", " +
SUBJECT + ", " +
BODY + ", " +
SERVICE_CENTER +
", " + THREAD_ID + ") " +
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + RECIPIENT_ID + ", " +
PERSON + ", " +
DATE_SENT + ", " +
DATE_RECEIVED + ", " +
PROTOCOL + ", " +
READ + ", " +
STATUS + ", " +
TYPE + ", " +
REPLY_PATH_PRESENT + ", " +
SUBJECT + ", " +
BODY + ", " +
SERVICE_CENTER +
", " + THREAD_ID + ") " +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
}
@ -859,7 +861,7 @@ public class SmsDatabase extends MessagingDatabase {
public SmsMessageRecord getCurrent() {
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)));
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.RECIPIENT_ID));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));
@ -880,7 +882,7 @@ public class SmsDatabase extends MessagingDatabase {
}
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
Recipient recipient = Recipient.from(context, address, true);
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
return new SmsMessageRecord(messageId, body, recipient,
recipient,

View File

@ -22,15 +22,18 @@ import android.database.sqlite.SQLiteException;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
@ -39,6 +42,20 @@ public class SmsMigrator {
private static final String TAG = SmsMigrator.class.getSimpleName();
private static class SystemColumns {
private static final String ADDRESS = "address";
private static final String PERSON = "person";
private static final String DATE_RECEIVED = "date";
private static final String PROTOCOL = "protocol";
private static final String READ = "read";
private static final String STATUS = "status";
private static final String TYPE = "type";
private static final String SUBJECT = "subject";
private static final String REPLY_PATH_PRESENT = "reply_path_present";
private static final String BODY = "body";
private static final String SERVICE_CENTER = "service_center";
}
private static void addStringToStatement(SQLiteStatement statement, Cursor cursor,
int index, String key)
{
@ -85,23 +102,22 @@ public class SmsMigrator {
ourType == MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE;
}
private static void getContentValuesForRow(Context context, Cursor cursor,
long threadId, SQLiteStatement statement)
{
String theirAddress = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
statement.bindString(1, Address.fromExternal(context, theirAddress).serialize());
private static void getContentValuesForRow(Context context, Cursor cursor, long threadId, SQLiteStatement statement) {
String address = cursor.getString(cursor.getColumnIndexOrThrow(SystemColumns.ADDRESS));
RecipientId id = Recipient.external(context, address).getId();
addIntToStatement(statement, cursor, 2, SmsDatabase.PERSON);
addIntToStatement(statement, cursor, 3, SmsDatabase.DATE_RECEIVED);
addIntToStatement(statement, cursor, 4, SmsDatabase.DATE_RECEIVED);
addIntToStatement(statement, cursor, 5, SmsDatabase.PROTOCOL);
addIntToStatement(statement, cursor, 6, SmsDatabase.READ);
addIntToStatement(statement, cursor, 7, SmsDatabase.STATUS);
addTranslatedTypeToStatement(statement, cursor, 8, SmsDatabase.TYPE);
addIntToStatement(statement, cursor, 9, SmsDatabase.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 10, SmsDatabase.SUBJECT);
addStringToStatement(statement, cursor, 11, SmsDatabase.BODY);
addStringToStatement(statement, cursor, 12, SmsDatabase.SERVICE_CENTER);
statement.bindString(1, id.serialize());
addIntToStatement(statement, cursor, 2, SystemColumns.PERSON);
addIntToStatement(statement, cursor, 3, SystemColumns.DATE_RECEIVED);
addIntToStatement(statement, cursor, 4, SystemColumns.DATE_RECEIVED);
addIntToStatement(statement, cursor, 5, SystemColumns.PROTOCOL);
addIntToStatement(statement, cursor, 6, SystemColumns.READ);
addIntToStatement(statement, cursor, 7, SystemColumns.STATUS);
addTranslatedTypeToStatement(statement, cursor, 8, SystemColumns.TYPE);
addIntToStatement(statement, cursor, 9, SystemColumns.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 10, SystemColumns.SUBJECT);
addStringToStatement(statement, cursor, 11, SystemColumns.BODY);
addStringToStatement(statement, cursor, 12, SystemColumns.SERVICE_CENTER);
statement.bindLong(13, threadId);
}
@ -136,7 +152,7 @@ public class SmsMigrator {
String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address != null) {
recipientList.add(Recipient.from(context, Address.fromExternal(context, address), true));
recipientList.add(Recipient.external(context, address));
}
}
@ -212,17 +228,14 @@ public class SmsMigrator {
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next());
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} else if (ourRecipients.size() > 1) {
ourRecipients.add(Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
ourRecipients.add(Recipient.self());
List<Address> memberAddresses = new LinkedList<>();
List<RecipientId> recipientIds = Stream.of(ourRecipients).map(Recipient::getId).toList();
for (Recipient recipient : ourRecipients) {
memberAddresses.add(recipient.getAddress());
}
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, true);
Recipient ourGroupRecipient = Recipient.from(context, Address.fromSerialized(ourGroupId), true);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(recipientIds, true);
RecipientId ourGroupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(ourGroupId);
Recipient ourGroupRecipient = Recipient.resolved(ourGroupRecipientId);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
}

View File

@ -32,9 +32,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -44,12 +42,11 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.Closeable;
import java.io.IOException;
@ -65,7 +62,7 @@ public class ThreadDatabase extends Database {
public static final String ID = "_id";
public static final String DATE = "date";
public static final String MESSAGE_COUNT = "message_count";
public static final String ADDRESS = "recipient_ids";
public static final String RECIPIENT_ID = "recipient_ids";
public static final String SNIPPET = "snippet";
private static final String SNIPPET_CHARSET = "snippet_cs";
public static final String READ = "read";
@ -86,7 +83,7 @@ public class ThreadDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + RECIPIENT_ID + " INTEGER, " + SNIPPET + " TEXT, " +
SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " +
@ -97,12 +94,12 @@ public class ThreadDatabase extends Database {
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_ID + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
};
private static final String[] THREAD_PROJECTION = {
ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE,
ID, DATE, MESSAGE_COUNT, RECIPIENT_ID, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE,
SNIPPET_URI, SNIPPET_CONTENT_TYPE, SNIPPET_EXTRAS, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT
};
@ -119,12 +116,12 @@ public class ThreadDatabase extends Database {
super(context, databaseHelper);
}
private long createThreadForRecipient(Address address, boolean group, int distributionType) {
private long createThreadForRecipient(@NonNull RecipientId recipientId, boolean group, int distributionType) {
ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis();
contentValues.put(DATE, date - date % 1000);
contentValues.put(ADDRESS, address.serialize());
contentValues.put(RECIPIENT_ID, recipientId.serialize());
if (group)
contentValues.put(TYPE, distributionType);
@ -340,24 +337,24 @@ public class ThreadDatabase extends Database {
}
public Cursor getFilteredConversationList(@Nullable List<Address> filter) {
public Cursor getFilteredConversationList(@Nullable List<RecipientId> filter) {
if (filter == null || filter.size() == 0)
return null;
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<List<Address>> partitionedAddresses = Util.partition(filter, 900);
List<Cursor> cursors = new LinkedList<>();
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<List<RecipientId>> splitRecipientIds = Util.partition(filter, 900);
List<Cursor> cursors = new LinkedList<>();
for (List<Address> addresses : partitionedAddresses) {
String selection = TABLE_NAME + "." + ADDRESS + " = ?";
String[] selectionArgs = new String[addresses.size()];
for (List<RecipientId> recipientIds : splitRecipientIds) {
String selection = TABLE_NAME + "." + RECIPIENT_ID + " = ?";
String[] selectionArgs = new String[recipientIds.size()];
for (int i=0;i<addresses.size()-1;i++)
selection += (" OR " + TABLE_NAME + "." + ADDRESS + " = ?");
for (int i=0;i<recipientIds.size()-1;i++)
selection += (" OR " + TABLE_NAME + "." + RECIPIENT_ID + " = ?");
int i= 0;
for (Address address : addresses) {
selectionArgs[i++] = DelimiterUtil.escape(address.serialize(), ' ');
for (RecipientId recipientId : recipientIds) {
selectionArgs[i++] = recipientId.serialize();
}
String query = createQuery(selection, 0);
@ -378,7 +375,7 @@ public class ThreadDatabase extends Database {
public Cursor getRecentPushConversationList(int limit) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = MESSAGE_COUNT + " != 0 AND (" + RecipientDatabase.REGISTERED + " = " + RecipientDatabase.RegisteredState.REGISTERED.getId() + " OR " + GroupDatabase.GROUP_ID + " NOT NULL)";
String where = MESSAGE_COUNT + " != 0 AND (" + RecipientDatabase.REGISTERED + " = " + RecipientDatabase.RegisteredState.REGISTERED.getId() + " OR " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.GROUP_ID + " NOT NULL)";
String query = createQuery(where, limit);
return db.rawQuery(query, null);
@ -496,10 +493,10 @@ public class ThreadDatabase extends Database {
}
public long getThreadIdIfExistsFor(Recipient recipient) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESS + " = ?";
String[] recipientsArg = new String[] {recipient.getAddress().serialize()};
Cursor cursor = null;
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = RECIPIENT_ID + " = ?";
String[] recipientsArg = new String[] {recipient.getId().serialize()};
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
@ -520,8 +517,8 @@ public class ThreadDatabase extends Database {
public long getThreadIdFor(Recipient recipient, int distributionType) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESS + " = ?";
String[] recipientsArg = new String[]{recipient.getAddress().serialize()};
String where = RECIPIENT_ID + " = ?";
String[] recipientsArg = new String[]{recipient.getId().serialize()};
Cursor cursor = null;
try {
@ -530,7 +527,7 @@ public class ThreadDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else {
return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType);
return createThreadForRecipient(recipient.getId(), recipient.isGroup(), distributionType);
}
} finally {
if (cursor != null)
@ -546,8 +543,8 @@ public class ThreadDatabase extends Database {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
return Recipient.from(context, address, false);
RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
return Recipient.resolved(id);
}
} finally {
if (cursor != null)
@ -664,9 +661,9 @@ public class ThreadDatabase extends Database {
String query =
"SELECT " + projection + " FROM " + TABLE_NAME +
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
" ON " + TABLE_NAME + "." + RECIPIENT_ID + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ID +
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.GROUP_ID +
" ON " + TABLE_NAME + "." + RECIPIENT_ID + " = " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.RECIPIENT_ID +
" WHERE " + where +
" ORDER BY " + TABLE_NAME + "." + DATE + " DESC";
@ -709,22 +706,11 @@ public class ThreadDatabase extends Database {
}
public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS)));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_ID)));
Optional<RecipientSettings> settings;
Optional<GroupRecord> groupRecord;
if (distributionType != DistributionTypes.ARCHIVE && distributionType != DistributionTypes.INBOX_ZERO) {
settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(cursor);
groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor);
} else {
settings = Optional.absent();
groupRecord = Optional.absent();
}
Recipient recipient = Recipient.from(context, address, settings, groupRecord, true);
Recipient recipient = Recipient.live(recipientId).get();
String body = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET));
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT));

View File

@ -1,5 +1,10 @@
package org.thoughtcrime.securesms.database.documents;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.logging.Log;
import com.fasterxml.jackson.annotation.JsonIgnore;
@ -13,20 +18,26 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import java.io.IOException;
import java.util.Objects;
public class IdentityKeyMismatch {
private static final String TAG = IdentityKeyMismatch.class.getSimpleName();
/** DEPRECATED */
@JsonProperty(value = "a")
private String address;
@JsonProperty(value = "r")
private String recipientId;
@JsonProperty(value = "k")
@JsonSerialize(using = IdentityKeySerializer.class)
@JsonDeserialize(using = IdentityKeyDeserializer.class)
@ -34,14 +45,19 @@ public class IdentityKeyMismatch {
public IdentityKeyMismatch() {}
public IdentityKeyMismatch(Address address, IdentityKey identityKey) {
this.address = address.serialize();
public IdentityKeyMismatch(RecipientId recipientId, IdentityKey identityKey) {
this.recipientId = recipientId.serialize();
this.address = "";
this.identityKey = identityKey;
}
@JsonIgnore
public Address getAddress() {
return Address.fromSerialized(address);
public RecipientId getRecipientId(@NonNull Context context) {
if (!TextUtils.isEmpty(recipientId)) {
return RecipientId.from(recipientId);
} else {
return Recipient.external(context, address).getId();
}
}
public IdentityKey getIdentityKey() {
@ -49,18 +65,18 @@ public class IdentityKeyMismatch {
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof IdentityKeyMismatch)) {
return false;
}
IdentityKeyMismatch that = (IdentityKeyMismatch)other;
return that.address.equals(this.address) && that.identityKey.equals(this.identityKey);
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdentityKeyMismatch that = (IdentityKeyMismatch) o;
return Objects.equals(address, that.address) &&
Objects.equals(recipientId, that.recipientId) &&
Objects.equals(identityKey, that.identityKey);
}
@Override
public int hashCode() {
return address.hashCode() ^ identityKey.hashCode();
return Objects.hash(address, recipientId, identityKey);
}
private static class IdentityKeySerializer extends JsonSerializer<IdentityKey> {

View File

@ -1,36 +1,56 @@
package org.thoughtcrime.securesms.database.documents;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.Objects;
public class NetworkFailure {
/** DEPRECATED */
@JsonProperty(value = "a")
private String address;
public NetworkFailure(Address address) {
this.address = address.serialize();
@JsonProperty(value = "r")
private String recipientId;
public NetworkFailure(@NonNull RecipientId recipientId) {
this.recipientId = recipientId.serialize();
this.address = "";
}
public NetworkFailure() {}
@JsonIgnore
public Address getAddress() {
return Address.fromSerialized(address);
public RecipientId getRecipientId(@NonNull Context context) {
if (!TextUtils.isEmpty(recipientId)) {
return RecipientId.from(recipientId);
} else {
return Recipient.external(context, address).getId();
}
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof NetworkFailure)) return false;
NetworkFailure that = (NetworkFailure)other;
return this.address.equals(that.address);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NetworkFailure that = (NetworkFailure) o;
return Objects.equals(address, that.address) &&
Objects.equals(recipientId, that.recipientId);
}
@Override
public int hashCode() {
return address.hashCode();
return Objects.hash(address, recipientId);
}
}

View File

@ -0,0 +1,282 @@
package org.thoughtcrime.securesms.database.helpers;
import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet;
import java.util.Set;
public class RecipientIdMigrationHelper {
private static final String TAG = Log.tag(RecipientIdMigrationHelper.class);
public static void execute(SQLiteDatabase db) {
Log.i(TAG, "Starting the recipient ID migration.");
long insertStart = System.currentTimeMillis();
Log.i(TAG, "Starting inserts for missing recipients.");
db.execSQL(buildInsertMissingRecipientStatement("identities", "address"));
db.execSQL(buildInsertMissingRecipientStatement("sessions", "address"));
db.execSQL(buildInsertMissingRecipientStatement("thread", "recipient_ids"));
db.execSQL(buildInsertMissingRecipientStatement("sms", "address"));
db.execSQL(buildInsertMissingRecipientStatement("mms", "address"));
db.execSQL(buildInsertMissingRecipientStatement("mms", "quote_author"));
db.execSQL(buildInsertMissingRecipientStatement("group_receipts", "address"));
db.execSQL(buildInsertMissingRecipientStatement("groups", "group_id"));
Log.i(TAG, "Finished inserts for missing recipients in " + (System.currentTimeMillis() - insertStart) + " ms.");
long updateMissingStart = System.currentTimeMillis();
Log.i(TAG, "Starting updates for invalid or missing addresses.");
db.execSQL(buildMissingAddressUpdateStatement("sms", "address"));
db.execSQL(buildMissingAddressUpdateStatement("mms", "address"));
db.execSQL(buildMissingAddressUpdateStatement("mms", "quote_author"));
Log.i(TAG, "Finished updates for invalid or missing addresses in " + (System.currentTimeMillis() - updateMissingStart) + " ms.");
db.execSQL("ALTER TABLE groups ADD COLUMN recipient_id INTEGER DEFAULT 0");
long updateStart = System.currentTimeMillis();
Log.i(TAG, "Starting recipient ID updates.");
db.execSQL(buildUpdateAddressToRecipientIdStatement("identities", "address"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("sessions", "address"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("thread", "recipient_ids"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("sms", "address"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("mms", "address"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("mms", "quote_author"));
db.execSQL(buildUpdateAddressToRecipientIdStatement("group_receipts", "address"));
db.execSQL("UPDATE groups SET recipient_id = (SELECT _id FROM recipient_preferences WHERE recipient_preferences.recipient_ids = groups.group_id)");
Log.i(TAG, "Finished recipient ID updates in " + (System.currentTimeMillis() - updateStart) + " ms.");
// NOTE: Because there's an open cursor on the same table, inserts and updates aren't visible
// until afterwards, which is why this group stuff is split into multiple loops
long findGroupStart = System.currentTimeMillis();
Log.i(TAG, "Starting to find missing group recipients.");
Set<String> missingGroupMembers = new HashSet<>();
try (Cursor cursor = db.rawQuery("SELECT members FROM groups", null)) {
while (cursor != null && cursor.moveToNext()) {
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow("members"));
String[] members = DelimiterUtil.split(serializedMembers, ',');
for (String rawMember : members) {
String member = DelimiterUtil.unescape(rawMember, ',');
if (!TextUtils.isEmpty(member) && !recipientExists(db, member)) {
missingGroupMembers.add(member);
}
}
}
}
Log.i(TAG, "Finished finding " + missingGroupMembers.size() + " missing group recipients in " + (System.currentTimeMillis() - findGroupStart) + " ms.");
long insertGroupStart = System.currentTimeMillis();
Log.i(TAG, "Starting the insert of missing group recipients.");
for (String member : missingGroupMembers) {
ContentValues values = new ContentValues();
values.put("recipient_ids", member);
db.insert("recipient_preferences", null, values);
}
Log.i(TAG, "Finished inserting missing group recipients in " + (System.currentTimeMillis() - insertGroupStart) + " ms.");
long updateGroupStart = System.currentTimeMillis();
Log.i(TAG, "Starting group recipient ID updates.");
try (Cursor cursor = db.rawQuery("SELECT _id, members FROM groups", null)) {
while (cursor != null && cursor.moveToNext()) {
long groupId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow("members"));
String[] members = DelimiterUtil.split(serializedMembers, ',');
long[] memberIds = new long[members.length];
for (int i = 0; i < members.length; i++) {
String member = DelimiterUtil.unescape(members[i], ',');
memberIds[i] = requireRecipientId(db, member);
}
String serializedMemberIds = Util.join(memberIds, ",");
db.execSQL("UPDATE groups SET members = ? WHERE _id = ?", new String[]{ serializedMemberIds, String.valueOf(groupId) });
}
}
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS group_recipient_id_index ON groups (recipient_id)");
Log.i(TAG, "Finished group recipient ID updates in " + (System.currentTimeMillis() - updateGroupStart) + " ms.");
long tableCopyStart = System.currentTimeMillis();
Log.i(TAG, "Starting to copy the recipient table.");
db.execSQL("CREATE TABLE recipient (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"uuid TEXT UNIQUE DEFAULT NULL, " +
"phone TEXT UNIQUE DEFAULT NULL, " +
"email TEXT UNIQUE DEFAULT NULL, " +
"group_id TEXT UNIQUE DEFAULT NULL, " +
"blocked INTEGER DEFAULT 0, " +
"message_ringtone TEXT DEFAULT NULL, " +
"message_vibrate INTEGER DEFAULT 0, " +
"call_ringtone TEXT DEFAULT NULL, " +
"call_vibrate INTEGER DEFAULT 0, " +
"notification_channel TEXT DEFAULT NULL, " +
"mute_until INTEGER DEFAULT 0, " +
"color TEXT DEFAULT NULL, " +
"seen_invite_reminder INTEGER DEFAULT 0, " +
"default_subscription_id INTEGER DEFAULT -1, " +
"message_expiration_time INTEGER DEFAULT 0, " +
"registered INTEGER DEFAULT 0, " +
"system_display_name TEXT DEFAULT NULL, " +
"system_photo_uri TEXT DEFAULT NULL, " +
"system_phone_label TEXT DEFAULT NULL, " +
"system_contact_uri TEXT DEFAULT NULL, " +
"profile_key TEXT DEFAULT NULL, " +
"signal_profile_name TEXT DEFAULT NULL, " +
"signal_profile_avatar TEXT DEFAULT NULL, " +
"profile_sharing INTEGER DEFAULT 0, " +
"unidentified_access_mode INTEGER DEFAULT 0, " +
"force_sms_selection INTEGER DEFAULT 0)");
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
boolean isGroup = GroupUtil.isEncodedGroup(address);
boolean isEmail = !isGroup && NumberUtil.isValidEmail(address);
boolean isPhone = !isGroup && !isEmail;
ContentValues values = new ContentValues();
values.put("_id", cursor.getLong(cursor.getColumnIndexOrThrow("_id")));
values.put("uuid", (String) null);
values.put("phone", isPhone ? address : null);
values.put("email", isEmail ? address : null);
values.put("group_id", isGroup ? address : null);
values.put("blocked", cursor.getInt(cursor.getColumnIndexOrThrow("block")));
values.put("message_ringtone", cursor.getString(cursor.getColumnIndexOrThrow("notification")));
values.put("message_vibrate", cursor.getString(cursor.getColumnIndexOrThrow("vibrate")));
values.put("call_ringtone", cursor.getString(cursor.getColumnIndexOrThrow("call_ringtone")));
values.put("call_vibrate", cursor.getString(cursor.getColumnIndexOrThrow("call_vibrate")));
values.put("notification_channel", cursor.getString(cursor.getColumnIndexOrThrow("notification_channel")));
values.put("mute_until", cursor.getLong(cursor.getColumnIndexOrThrow("mute_until")));
values.put("color", cursor.getString(cursor.getColumnIndexOrThrow("color")));
values.put("seen_invite_reminder", cursor.getInt(cursor.getColumnIndexOrThrow("seen_invite_reminder")));
values.put("default_subscription_id", cursor.getInt(cursor.getColumnIndexOrThrow("default_subscription_id")));
values.put("message_expiration_time", cursor.getInt(cursor.getColumnIndexOrThrow("expire_messages")));
values.put("registered", cursor.getInt(cursor.getColumnIndexOrThrow("registered")));
values.put("system_display_name", cursor.getString(cursor.getColumnIndexOrThrow("system_display_name")));
values.put("system_photo_uri", cursor.getString(cursor.getColumnIndexOrThrow("system_contact_photo")));
values.put("system_phone_label", cursor.getString(cursor.getColumnIndexOrThrow("system_phone_label")));
values.put("system_contact_uri", cursor.getString(cursor.getColumnIndexOrThrow("system_contact_uri")));
values.put("profile_key", cursor.getString(cursor.getColumnIndexOrThrow("profile_key")));
values.put("signal_profile_name", cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name")));
values.put("signal_profile_avatar", cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_avatar")));
values.put("profile_sharing", cursor.getInt(cursor.getColumnIndexOrThrow("profile_sharing_approval")));
values.put("unidentified_access_mode", cursor.getInt(cursor.getColumnIndexOrThrow("unidentified_access_mode")));
values.put("force_sms_selection", cursor.getInt(cursor.getColumnIndexOrThrow("force_sms_selection")));
db.insert("recipient", null, values);
}
}
db.execSQL("DROP TABLE recipient_preferences");
Log.i(TAG, "Finished copying the recipient table in " + (System.currentTimeMillis() - tableCopyStart) + " ms.");
long sanityCheckStart = System.currentTimeMillis();
Log.i(TAG, "Starting DB integrity sanity checks.");
assertEmptyQuery(db, "identities", buildSanityCheckQuery("identities", "address"));
assertEmptyQuery(db, "sessions", buildSanityCheckQuery("sessions", "address"));
assertEmptyQuery(db, "groups", buildSanityCheckQuery("groups", "recipient_id"));
assertEmptyQuery(db, "thread", buildSanityCheckQuery("thread", "recipient_ids"));
assertEmptyQuery(db, "sms", buildSanityCheckQuery("sms", "address"));
assertEmptyQuery(db, "mms -- address", buildSanityCheckQuery("mms", "address"));
assertEmptyQuery(db, "mms -- quote_author", buildSanityCheckQuery("mms", "quote_author"));
assertEmptyQuery(db, "group_receipts", buildSanityCheckQuery("group_receipts", "address"));
Log.i(TAG, "Finished DB integrity sanity checks in " + (System.currentTimeMillis() - sanityCheckStart) + " ms.");
Log.i(TAG, "Finished recipient ID migration in " + (System.currentTimeMillis() - insertStart) + " ms.");
}
private static String buildUpdateAddressToRecipientIdStatement(@NonNull String table, @NonNull String addressColumn) {
return "UPDATE " + table + " SET " + addressColumn + "=(SELECT _id " +
"FROM recipient_preferences " +
"WHERE recipient_preferences.recipient_ids = " + table + "." + addressColumn + ")";
}
private static String buildInsertMissingRecipientStatement(@NonNull String table, @NonNull String addressColumn) {
return "INSERT INTO recipient_preferences(recipient_ids) SELECT DISTINCT " + addressColumn + " " +
"FROM " + table + " " +
"WHERE " + addressColumn + " != '' AND " +
addressColumn + " != 'insert-address-column' AND " +
addressColumn + " NOT NULL AND " +
addressColumn + " NOT IN (SELECT recipient_ids FROM recipient_preferences)";
}
private static String buildMissingAddressUpdateStatement(@NonNull String table, @NonNull String addressColumn) {
return "UPDATE " + table + " SET " + addressColumn + " = -1 " +
"WHERE " + addressColumn + " = '' OR " +
addressColumn + " IS NULL OR " +
addressColumn + " = 'insert-address-token'";
}
private static boolean recipientExists(@NonNull SQLiteDatabase db, @NonNull String address) {
return getRecipientId(db, address) != null;
}
private static @Nullable Long getRecipientId(@NonNull SQLiteDatabase db, @NonNull String address) {
try (Cursor cursor = db.rawQuery("SELECT _id FROM recipient_preferences WHERE recipient_ids = ?", new String[]{ address })) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndex("_id"));
} else {
return null;
}
}
}
private static long requireRecipientId(@NonNull SQLiteDatabase db, @NonNull String address) {
Long id = getRecipientId(db, address);
if (id != null) {
return id;
} else {
throw new MissingRecipientError(address);
}
}
private static String buildSanityCheckQuery(@NonNull String table, @NonNull String idColumn) {
return "SELECT " + idColumn + " FROM " + table + " WHERE " + idColumn + " != -1 AND " + idColumn + " NOT IN (SELECT _id FROM recipient)";
}
private static void assertEmptyQuery(@NonNull SQLiteDatabase db, @NonNull String tag, @NonNull String query) {
try (Cursor cursor = db.rawQuery(query, null)) {
if (cursor != null && cursor.moveToFirst()) {
throw new FailedSanityCheckError(tag);
}
}
}
private static final class MissingRecipientError extends AssertionError {
MissingRecipientError(@NonNull String address) {
super("Could not find recipient with address " + address);
}
}
private static final class FailedSanityCheckError extends AssertionError {
FailedSanityCheckError(@NonNull String tableName) {
super("Sanity check failed for tag '" + tableName + "'");
}
}
}

View File

@ -7,6 +7,8 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import net.sqlcipher.database.SQLiteDatabase;
@ -36,8 +38,13 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import java.io.File;
@ -68,8 +75,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int STICKERS = 21;
private static final int REVEALABLE_MESSAGES = 22;
private static final int VIEW_ONCE_ONLY = 23;
private static final int RECIPIENT_IDS = 24;
private static final int DATABASE_VERSION = 23;
private static final int DATABASE_VERSION = 25;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@ -287,18 +295,18 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
try (Cursor cursor = db.rawQuery("SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0", null)) {
while (cursor != null && cursor.moveToNext()) {
String addressString = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Address address = Address.fromExternal(context, addressString);
String rawAddress = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
String address = PhoneNumberFormatter.get(context).format(rawAddress);
String systemName = cursor.getString(cursor.getColumnIndexOrThrow("system_display_name"));
String profileName = cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name"));
String messageSound = cursor.getString(cursor.getColumnIndexOrThrow("notification"));
Uri messageSoundUri = messageSound != null ? Uri.parse(messageSound) : null;
int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow("vibrate"));
String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, address);
String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, Address.fromSerialized(address));
boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1;
if (address.isGroup()) {
try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address.toGroupString() })) {
if (GroupUtil.isEncodedGroup(address)) {
try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address })) {
if (groupCursor != null && groupCursor.moveToFirst()) {
String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title"));
@ -309,11 +317,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
}
String channelId = NotificationChannels.createChannelFor(context, address, displayName, messageSoundUri, vibrateEnabled);
String channelId = NotificationChannels.createChannelFor(context, Address.fromSerialized(address), displayName, messageSoundUri, vibrateEnabled);
ContentValues values = new ContentValues(1);
values.put("notification_channel", channelId);
db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { addressString });
db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { rawAddress });
}
}
}
@ -478,6 +486,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL("UPDATE mms SET reveal_start_time = 0");
}
if (oldVersion < RECIPIENT_IDS) {
RecipientIdMigrationHelper.execute(db);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -79,7 +79,7 @@ class SessionStoreMigrationHelper {
ContentValues contentValues = new ContentValues();
contentValues.put(SessionDatabase.ADDRESS, address.serialize());
contentValues.put(SessionDatabase.RECIPIENT_ID, address.serialize());
contentValues.put(SessionDatabase.DEVICE, deviceId);
contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize());

View File

@ -76,7 +76,7 @@ public class IdentityRecordList {
for (IdentityRecord identityRecord : identityRecords) {
if (isUntrusted(identityRecord)) {
untrusted.add(Recipient.from(context, identityRecord.getAddress(), false));
untrusted.add(Recipient.resolved(identityRecord.getRecipientId()));
}
}
@ -100,7 +100,7 @@ public class IdentityRecordList {
for (IdentityRecord identityRecord : identityRecords) {
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
unverified.add(Recipient.from(context, identityRecord.getAddress(), false));
unverified.add(Recipient.resolved(identityRecord.getRecipientId()));
}
}

View File

@ -14,8 +14,7 @@ public class BlockedContactsLoader extends AbstractCursorLoader {
@Override
public Cursor getCursor() {
return DatabaseFactory.getRecipientDatabase(getContext())
.getBlocked();
return DatabaseFactory.getRecipientDatabase(getContext()).getBlocked();
}
}

View File

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -31,13 +32,13 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
@SuppressWarnings("unused")
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
private final Address address;
private final RecipientId recipientId;
private final ContentObserver observer;
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull Address address) {
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull RecipientId recipientId) {
super(context);
this.address = address;
this.observer = new ForceLoadContentObserver();
this.recipientId = recipientId;
this.observer = new ForceLoadContentObserver();
onContentChanged();
}
@ -62,7 +63,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
@Override
public BucketedThreadMedia loadInBackground() {
BucketedThreadMedia result = new BucketedThreadMedia(getContext());
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.resolved(recipientId));
DatabaseFactory.getMediaDatabase(getContext()).subscribeToMediaChanges(observer);
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {

View File

@ -9,6 +9,9 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
import java.util.LinkedList;
@ -42,7 +45,7 @@ public class ConversationListLoader extends AbstractCursorLoader {
if (archivedCount > 0) {
MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.ADDRESS, ThreadDatabase.SNIPPET, ThreadDatabase.READ, ThreadDatabase.UNREAD_COUNT,
ThreadDatabase.RECIPIENT_ID, ThreadDatabase.SNIPPET, ThreadDatabase.READ, ThreadDatabase.UNREAD_COUNT,
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
ThreadDatabase.SNIPPET_CONTENT_TYPE, ThreadDatabase.SNIPPET_EXTRAS,
ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.DELIVERY_RECEIPT_COUNT,
@ -70,13 +73,13 @@ public class ConversationListLoader extends AbstractCursorLoader {
}
private Cursor getFilteredConversationList(String filter) {
List<String> numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter);
List<Address> addresses = new LinkedList<>();
List<String> numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter);
List<RecipientId> recipientIds = new LinkedList<>();
for (String number : numbers) {
addresses.add(Address.fromExternal(context, number));
recipientIds.add(Recipient.external(context, number).getId());
}
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(addresses);
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(recipientIds);
}
}

View File

@ -8,29 +8,30 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
public class ThreadMediaLoader extends AbstractCursorLoader {
private final Address address;
private final boolean gallery;
private final RecipientId recipientId;
private final boolean gallery;
public ThreadMediaLoader(@NonNull Context context, @NonNull Address address, boolean gallery) {
public ThreadMediaLoader(@NonNull Context context, @NonNull RecipientId recipientId, boolean gallery) {
super(context);
this.address = address;
this.gallery = gallery;
this.recipientId = recipientId;
this.gallery = gallery;
}
@Override
public Cursor getCursor() {
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.resolved(recipientId));
if (gallery) return DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId);
else return DatabaseFactory.getMediaDatabase(getContext()).getDocumentMediaForThread(threadId);
}
public Address getAddress() {
return address;
public RecipientId getRecipientId() {
return recipientId;
}
}

View File

@ -6,16 +6,18 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
public class Quote {
private final long id;
private final Address author;
private final String text;
private final boolean missing;
private final SlideDeck attachment;
private final long id;
private final RecipientId author;
private final String text;
private final boolean missing;
private final SlideDeck attachment;
public Quote(long id, @NonNull Address author, @Nullable String text, boolean missing, @NonNull SlideDeck attachment) {
public Quote(long id, @NonNull RecipientId author, @Nullable String text, boolean missing, @NonNull SlideDeck attachment) {
this.id = id;
this.author = author;
this.text = text;
@ -27,7 +29,7 @@ public class Quote {
return id;
}
public @NonNull Address getAuthor() {
public @NonNull RecipientId getAuthor() {
return author;
}

View File

@ -29,7 +29,6 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase.Extra;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ExpirationUtil;
@ -111,7 +110,7 @@ public class ThreadRecord extends DisplayRecord {
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (SmsDatabase.Types.isIdentityUpdate(type)) {
if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
if (getRecipient().isGroup()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isIdentityVerified(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified));

View File

@ -1,13 +1,13 @@
package org.thoughtcrime.securesms.dependencies;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.IncomingMessageProcessor;
import org.thoughtcrime.securesms.gcm.MessageRetriever;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
@ -32,6 +32,7 @@ public class ApplicationDependencies {
private static SignalServiceMessageReceiver messageReceiver;
private static IncomingMessageProcessor incomingMessageProcessor;
private static MessageRetriever messageRetriever;
private static LiveRecipientCache recipientCache;
public static synchronized void init(@NonNull Application application, @NonNull Provider provider) {
if (ApplicationDependencies.application != null || ApplicationDependencies.provider != null) {
@ -105,6 +106,16 @@ public class ApplicationDependencies {
return messageRetriever;
}
public static synchronized @NonNull LiveRecipientCache getRecipientCache() {
assertInitialization();
if (recipientCache == null) {
recipientCache = provider.provideRecipientCache();
}
return recipientCache;
}
private static void assertInitialization() {
if (application == null || provider == null) {
throw new UninitializedException();
@ -118,6 +129,8 @@ public class ApplicationDependencies {
@NonNull SignalServiceNetworkAccess provideSignalServiceNetworkAccess();
@NonNull IncomingMessageProcessor provideIncomingMessageProcessor();
@NonNull MessageRetriever provideMessageRetriever();
@NonNull
LiveRecipientCache provideRecipientCache();
}
private static class UninitializedException extends IllegalStateException {

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.gcm.MessageRetriever;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.push.SecurityEventListener;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -85,6 +86,12 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
return new MessageRetriever();
}
@Override
public @NonNull
LiveRecipientCache provideRecipientCache() {
return new LiveRecipientCache(context);
}
private static class DynamicCredentialsProvider implements CredentialsProvider {
private final Context context;

View File

@ -118,6 +118,6 @@ public class WebRtcViewModel {
}
public @NonNull String toString() {
return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
return "[State: " + state + ", recipient: " + recipient.requireAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
}
}

View File

@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.Collection;
@ -22,7 +23,7 @@ public final class GroupManager {
@Nullable String name,
boolean mms)
{
Set<Address> addresses = getMemberAddresses(members);
Set<RecipientId> addresses = getMemberIds(members);
return V1GroupManager.createGroup(context, addresses, avatar, name, mms);
}
@ -34,15 +35,15 @@ public final class GroupManager {
@Nullable String name)
throws InvalidNumberException
{
Set<Address> addresses = getMemberAddresses(members);
Set<RecipientId> addresses = getMemberIds(members);
return V1GroupManager.updateGroup(context, groupId, addresses, avatar, name);
}
private static Set<Address> getMemberAddresses(Collection<Recipient> recipients) {
final Set<Address> results = new HashSet<>();
private static Set<RecipientId> getMemberIds(Collection<Recipient> recipients) {
final Set<RecipientId> results = new HashSet<>();
for (Recipient recipient : recipients) {
results.add(recipient.getAddress());
results.add(recipient.getId());
}
return results;

View File

@ -8,7 +8,6 @@ import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
@ -21,6 +20,7 @@ import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
@ -86,11 +86,11 @@ public class GroupMessageProcessor {
builder.setType(GroupContext.Type.UPDATE);
SignalServiceAttachment avatar = group.getAvatar().orNull();
List<Address> members = group.getMembers().isPresent() ? new LinkedList<Address>() : null;
List<RecipientId> members = new LinkedList<>();
if (group.getMembers().isPresent()) {
for (String member : group.getMembers().get()) {
members.add(Address.fromExternal(context, member));
members.add(Recipient.external(context, member).getId());
}
}
@ -110,31 +110,31 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
Set<Address> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<Address> messageMembers = new HashSet<>();
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>();
for (String messageMember : group.getMembers().get()) {
messageMembers.add(Address.fromExternal(context, messageMember));
messageMembers.add(Recipient.external(context, messageMember).getId());
}
Set<Address> addedMembers = new HashSet<>(messageMembers);
Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
addedMembers.removeAll(recordMembers);
Set<Address> missingMembers = new HashSet<>(recordMembers);
Set<RecipientId> missingMembers = new HashSet<>(recordMembers);
missingMembers.removeAll(messageMembers);
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
if (addedMembers.size() > 0) {
Set<Address> unionMembers = new HashSet<>(recordMembers);
Set<RecipientId> unionMembers = new HashSet<>(recordMembers);
unionMembers.addAll(messageMembers);
database.updateMembers(id, new LinkedList<>(unionMembers));
builder.clearMembers();
for (Address addedMember : addedMembers) {
builder.addMembers(addedMember.serialize());
for (RecipientId addedMember : addedMembers) {
builder.addMembers(Recipient.resolved(addedMember).requireAddress().serialize());
}
} else {
builder.clearMembers();
@ -163,10 +163,12 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record)
{
if (record.getMembers().contains(Address.fromExternal(context, content.getSender()))) {
Recipient sender = Recipient.external(context, content.getSender());
if (record.getMembers().contains(sender.getId())) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
.add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
}
return null;
@ -178,15 +180,15 @@ public class GroupMessageProcessor {
@NonNull GroupRecord record,
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
List<Address> members = record.getMembers();
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
List<RecipientId> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT);
if (members.contains(Address.fromExternal(context, content.getSender()))) {
database.remove(id, Address.fromExternal(context, content.getSender()));
if (members.contains(Recipient.external(context, content.getSender()).getId())) {
database.remove(id, Recipient.external(context, content.getSender()).getId());
if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing);
@ -210,8 +212,8 @@ public class GroupMessageProcessor {
try {
if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false));
Recipient recipient = Recipient.from(context, addres, false);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(group.getGroupId(), false));
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
@ -222,7 +224,7 @@ public class GroupMessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@ -10,7 +10,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -19,6 +18,7 @@ import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
@ -28,50 +28,49 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
final class V1GroupManager {
static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull Set<Address> memberAddresses,
@Nullable Bitmap avatar,
@Nullable String name,
boolean mms)
static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull Set<RecipientId> memberIds,
@Nullable Bitmap avatar,
@Nullable String name,
boolean mms)
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null);
memberIds.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
groupDatabase.create(groupId, name, new LinkedList<>(memberIds), null, null);
if (!mms) {
groupDatabase.updateAvatar(groupId, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient.getId(), true);
return sendGroupUpdate(context, groupId, memberIds, name, avatarBytes);
} else {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId);
}
}
static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<Address> memberAddresses,
@Nullable Bitmap avatar,
@Nullable String name)
static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<RecipientId> memberAddresses,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
memberAddresses.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
@ -79,27 +78,28 @@ final class V1GroupManager {
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
} else {
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
return new GroupActionResult(groupRecipient, threadId);
}
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<RecipientId> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
try {
Attachment avatarAttachment = null;
Address groupAddress = Address.fromSerialized(groupId);
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
Attachment avatarAttachment = null;
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
for (RecipientId member : members) {
numbers.add(Recipient.resolved(member).requireAddress().serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()

View File

@ -27,23 +27,23 @@ public abstract class JobMigration {
return endVersion;
}
protected static class JobData {
public static class JobData {
private final String factoryKey;
private final String queueKey;
private final Data data;
JobData(@NonNull String factoryKey, @Nullable String queueKey, @NonNull Data data) {
public JobData(@NonNull String factoryKey, @Nullable String queueKey, @NonNull Data data) {
this.factoryKey = factoryKey;
this.queueKey = queueKey;
this.data = data;
}
protected @NonNull JobData withQueueKey(@Nullable String newQueueKey) {
public @NonNull JobData withQueueKey(@Nullable String newQueueKey) {
return new JobData(factoryKey, newQueueKey, data);
}
protected @NonNull JobData withData(@NonNull Data newData) {
public @NonNull JobData withData(@NonNull Data newData) {
return new JobData(factoryKey, queueKey, newData);
}

View File

@ -0,0 +1,226 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import java.io.IOException;
import java.io.Serializable;
public class RecipientIdJobMigration extends JobMigration {
private final Application application;
public RecipientIdJobMigration(@NonNull Application application) {
super(2);
this.application = application;
}
@Override
protected @NonNull JobData migrate(@NonNull JobData jobData) {
switch(jobData.getFactoryKey()) {
case "MultiDeviceContactUpdateJob": return migrateMultiDeviceContactUpdateJob(jobData);
case "MultiDeviceRevealUpdateJob": return migrateMultiDeviceViewOnceOpenJob(jobData);
case "RequestGroupInfoJob": return migrateRequestGroupInfoJob(jobData);
case "SendDeliveryReceiptJob": return migrateSendDeliveryReceiptJob(jobData);
case "MultiDeviceVerifiedUpdateJob": return migrateMultiDeviceVerifiedUpdateJob(jobData);
case "RetrieveProfileJob": return migrateRetrieveProfileJob(jobData);
case "PushGroupSendJob": return migratePushGroupSendJob(jobData);
case "PushGroupUpdateJob": return migratePushGroupUpdateJob(jobData);
case "DirectoryRefreshJob": return migrateDirectoryRefreshJob(jobData);
case "RetrieveProfileAvatarJob": return migrateRetrieveProfileAvatarJob(jobData);
case "MultiDeviceReadUpdateJob": return migrateMultiDeviceReadUpdateJob(jobData);
case "PushTextSendJob": return migratePushTextSendJob(jobData);
case "PushMediaSendJob": return migratePushMediaSendJob(jobData);
case "SmsSendJob": return migrateSmsSendJob(jobData);
default: return jobData;
}
}
private @NonNull JobData migrateMultiDeviceContactUpdateJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("address");
Data updatedData = new Data.Builder().putString("recipient", Recipient.external(application, address).getId().serialize())
.putBoolean("force_sync", jobData.getData().getBoolean("force_sync"))
.build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateMultiDeviceViewOnceOpenJob(@NonNull JobData jobData) {
try {
String rawOld = jobData.getData().getString("message_id");
OldSerializableSyncMessageId old = JsonUtils.fromJson(rawOld, OldSerializableSyncMessageId.class);
Recipient recipient = Recipient.external(application, old.sender);
NewSerializableSyncMessageId updated = new NewSerializableSyncMessageId(recipient.getId().serialize(), old.timestamp);
String rawUpdated = JsonUtils.toJson(updated);
Data updatedData = new Data.Builder().putString("message_id", rawUpdated).build();
return jobData.withData(updatedData);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private @NonNull JobData migrateRequestGroupInfoJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("source");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("source", recipient.getId().serialize()).build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateSendDeliveryReceiptJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("address");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("recipient", recipient.getId().serialize()).build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateMultiDeviceVerifiedUpdateJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("destination");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("destination", recipient.getId().serialize())
.putString("identity_key", jobData.getData().getString("identity_key"))
.putInt("verified_status", jobData.getData().getInt("verified_status"))
.putLong("timestamp", jobData.getData().getLong("timestamp"))
.build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateRetrieveProfileJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("address");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("recipient", recipient.getId().serialize()).build();
return jobData.withData(updatedData);
}
private @NonNull JobData migratePushGroupSendJob(@NonNull JobData jobData) {
// noinspection ConstantConditions
Recipient queueRecipient = Recipient.external(application, jobData.getQueueKey());
String address = jobData.getData().getString("filter_address");
RecipientId recipientId = address != null ? Recipient.external(application, address).getId() : null;
Data updatedData = new Data.Builder().putString("filter_recipient", recipientId != null ? recipientId.serialize() : null)
.putLong("message_id", jobData.getData().getLong("message_id"))
.build();
return jobData.withQueueKey(queueRecipient.getId().toQueueKey())
.withData(updatedData);
}
private @NonNull JobData migratePushGroupUpdateJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("source");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("source", recipient.getId().serialize())
.putString("group_id", jobData.getData().getString("group_id"))
.build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateDirectoryRefreshJob(@NonNull JobData jobData) {
String address = jobData.getData().getString("address");
Recipient recipient = address != null ? Recipient.external(application, address) : null;
Data updatedData = new Data.Builder().putString("recipient", recipient != null ? recipient.getId().serialize() : null)
.putBoolean("notify_of_new_users", jobData.getData().getBoolean("notify_of_new_users"))
.build();
return jobData.withData(updatedData);
}
private @NonNull JobData migrateRetrieveProfileAvatarJob(@NonNull JobData jobData) {
//noinspection ConstantConditions
String queueAddress = jobData.getQueueKey().substring("RetrieveProfileAvatarJob".length());
Recipient queueRecipient = Recipient.external(application, queueAddress);
String address = jobData.getData().getString("address");
Recipient recipient = Recipient.external(application, address);
Data updatedData = new Data.Builder().putString("recipient", recipient.getId().serialize())
.putString("profile_avatar", jobData.getData().getString("profile_avatar"))
.build();
return jobData.withQueueKey("RetrieveProfileAvatarJob::" + queueRecipient.getId().toQueueKey())
.withData(updatedData);
}
private @NonNull JobData migrateMultiDeviceReadUpdateJob(@NonNull JobData jobData) {
try {
String[] rawOld = jobData.getData().getStringArray("message_ids");
String[] rawUpdated = new String[rawOld.length];
for (int i = 0; i < rawOld.length; i++) {
OldSerializableSyncMessageId old = JsonUtils.fromJson(rawOld[i], OldSerializableSyncMessageId.class);
Recipient recipient = Recipient.external(application, old.sender);
NewSerializableSyncMessageId updated = new NewSerializableSyncMessageId(recipient.getId().serialize(), old.timestamp);
rawUpdated[i] = JsonUtils.toJson(updated);
}
Data updatedData = new Data.Builder().putStringArray("message_ids", rawUpdated).build();
return jobData.withData(updatedData);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private @NonNull JobData migratePushTextSendJob(@NonNull JobData jobData) {
//noinspection ConstantConditions
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
return jobData.withQueueKey(recipient.getId().toQueueKey());
}
private @NonNull JobData migratePushMediaSendJob(@NonNull JobData jobData) {
//noinspection ConstantConditions
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
return jobData.withQueueKey(recipient.getId().toQueueKey());
}
private @NonNull JobData migrateSmsSendJob(@NonNull JobData jobData) {
//noinspection ConstantConditions
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
return jobData.withQueueKey(recipient.getId().toQueueKey());
}
@VisibleForTesting
static class OldSerializableSyncMessageId implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty
private final String sender;
@JsonProperty
private final long timestamp;
OldSerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
this.sender = sender;
this.timestamp = timestamp;
}
}
@VisibleForTesting
static class NewSerializableSyncMessageId implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty
private final String recipientId;
@JsonProperty
private final long timestamp;
NewSerializableSyncMessageId(@JsonProperty("recipientId") String recipientId, @JsonProperty("timestamp") long timestamp) {
this.recipientId = recipientId;
this.timestamp = timestamp;
}
}
}

View File

@ -1,16 +1,15 @@
package org.thoughtcrime.securesms.jobs;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -22,7 +21,7 @@ public class DirectoryRefreshJob extends BaseJob {
private static final String TAG = DirectoryRefreshJob.class.getSimpleName();
private static final String KEY_ADDRESS = "address";
private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_NOTIFY_OF_NEW_USERS = "notify_of_new_users";
@Nullable private Recipient recipient;
@ -53,7 +52,7 @@ public class DirectoryRefreshJob extends BaseJob {
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_ADDRESS, recipient != null ? recipient.getAddress().serialize() : null)
return new Data.Builder().putString(KEY_RECIPIENT, recipient != null ? recipient.getId().serialize() : null)
.putBoolean(KEY_NOTIFY_OF_NEW_USERS, notifyOfNewUsers)
.build();
}
@ -85,18 +84,11 @@ public class DirectoryRefreshJob extends BaseJob {
public static final class Factory implements Job.Factory<DirectoryRefreshJob> {
private final Application application;
public Factory(@NonNull Application application) {
this.application = application;
}
@Override
public @NonNull DirectoryRefreshJob create(@NonNull Parameters parameters, @NonNull Data data) {
String serializedAddress = data.getString(KEY_ADDRESS);
Address address = serializedAddress != null ? Address.fromSerialized(serializedAddress) : null;
Recipient recipient = address != null ? Recipient.from(application, address, true) : null;
boolean notifyOfNewUsers = data.getBoolean(KEY_NOTIFY_OF_NEW_USERS);
String serialized = data.getString(KEY_RECIPIENT);
Recipient recipient = serialized != null ? Recipient.resolved(RecipientId.from(serialized)) : null;
boolean notifyOfNewUsers = data.getBoolean(KEY_NOTIFY_OF_NEW_USERS);
return new DirectoryRefreshJob(parameters, recipient, notifyOfNewUsers);
}

View File

@ -14,12 +14,12 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -35,7 +35,7 @@ public final class JobManagerFactories {
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory(application));
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
put(FcmRefreshJob.KEY, new FcmRefreshJob.Factory());
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
@ -61,13 +61,13 @@ public final class JobManagerFactories {
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory(application));
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory());
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory());
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
put(ServiceOutageDetectionJob.KEY, new ServiceOutageDetectionJob.Factory());
put(SmsReceiveJob.KEY, new SmsReceiveJob.Factory());
put(SmsSendJob.KEY, new SmsSendJob.Factory());
@ -105,7 +105,7 @@ public final class JobManagerFactories {
new SqlCipherMigrationConstraintObserver());
}
public static List<JobMigration> getJobMigrations() {
return Collections.emptyList();
public static List<JobMigration> getJobMigrations(@NonNull Application application) {
return Arrays.asList(new RecipientIdJobMigration(application));
}
}

View File

@ -4,6 +4,7 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.android.mms.pdu_alt.PduBody;
@ -28,6 +29,8 @@ import org.thoughtcrime.securesms.mms.MmsRadioException;
import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -39,6 +42,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -155,18 +159,6 @@ public class MmsDownloadJob extends BaseJob {
handleDownloadError(messageId, threadId,
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
automatic);
} catch (DuplicateMessageException e) {
Log.w(TAG, e);
database.markAsDecryptDuplicate(messageId, threadId);
} catch (LegacyMessageException e) {
Log.w(TAG, e);
database.markAsLegacyVersion(messageId, threadId);
} catch (NoSessionException e) {
Log.w(TAG, e);
database.markAsNoSession(messageId, threadId);
} catch (InvalidMessageException e) {
Log.w(TAG, e);
database.markAsDecryptFailed(messageId, threadId);
}
}
@ -188,40 +180,39 @@ public class MmsDownloadJob extends BaseJob {
private void storeRetrievedMms(String contentLocation,
long messageId, long threadId, RetrieveConf retrieved,
int subscriptionId, @Nullable Address notificationFrom)
throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException,
LegacyMessageException
int subscriptionId, @Nullable RecipientId notificationFrom)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<Address> group = Optional.absent();
Set<Address> members = new HashSet<>();
String body = null;
List<Attachment> attachments = new LinkedList<>();
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<String> group = Optional.absent();
Set<RecipientId> members = new HashSet<>();
String body = null;
List<Attachment> attachments = new LinkedList<>();
Address from;
RecipientId from;
if (retrieved.getFrom() != null) {
from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString()));
from = Recipient.external(context, Util.toIsoString(retrieved.getFrom().getTextString())).getId();
} else if (notificationFrom != null) {
from = notificationFrom;
} else {
from = Address.UNKNOWN;
from = RecipientId.UNKNOWN;
}
if (retrieved.getTo() != null) {
for (EncodedStringValue toValue : retrieved.getTo()) {
members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString())));
members.add(Recipient.external(context, Util.toIsoString(toValue.getTextString())).getId());
}
}
if (retrieved.getCc() != null) {
for (EncodedStringValue ccValue : retrieved.getCc()) {
members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString())));
members.add(Recipient.external(context, Util.toIsoString(ccValue.getTextString())).getId());
}
}
members.add(from);
members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
members.add(Recipient.self().getId());
if (retrieved.getBody() != null) {
body = PartParser.getMessageText(retrieved.getBody());
@ -244,7 +235,8 @@ public class MmsDownloadJob extends BaseJob {
}
if (members.size() > 2) {
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true)));
List<RecipientId> recipients = new ArrayList<>(members);
group = Optional.of(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(recipients, true));
}
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false, false);

View File

@ -101,7 +101,7 @@ public class MmsReceiveJob extends BaseJob {
private boolean isBlocked(GenericPdu pdu) {
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
Recipient recipients = Recipient.from(context, Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false);
Recipient recipients = Recipient.external(context, Util.toIsoString(pdu.getFrom().getTextString()));
return recipients.isBlocked();
}

View File

@ -45,7 +45,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -221,7 +221,7 @@ public final class MmsSendJob extends SendJob {
{
SendReq req = new SendReq();
String lineNumber = getMyNumber(context);
Address destination = message.getRecipient().getAddress();
Address destination = message.getRecipient().requireAddress();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = message.getAttachments();
@ -236,9 +236,9 @@ public final class MmsSendJob extends SendJob {
for (Recipient member : members) {
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
req.addBcc(new EncodedStringValue(member.getAddress().serialize()));
req.addBcc(new EncodedStringValue(member.requireAddress().serialize()));
} else {
req.addTo(new EncodedStringValue(member.getAddress().serialize()));
req.addTo(new EncodedStringValue(member.requireAddress().serialize()));
}
}
} else {

View File

@ -73,10 +73,10 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob {
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
if (recipient.isGroupRecipient()) {
blockedGroups.add(GroupUtil.getDecodedId(recipient.getAddress().toGroupString()));
if (recipient.isGroup()) {
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireAddress().toGroupString()));
} else {
blockedIndividuals.add(recipient.getAddress().serialize());
blockedIndividuals.add(recipient.requireAddress().serialize());
}
}

View File

@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
@ -55,48 +56,46 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
private static final String KEY_ADDRESS = "address";
private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_FORCE_SYNC = "force_sync";
private @Nullable String address;
private @Nullable RecipientId recipientId;
private boolean forceSync;
public MultiDeviceContactUpdateJob(@NonNull Context context) {
this(context, false);
public MultiDeviceContactUpdateJob() {
this(false);
}
public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) {
this(context, null, forceSync);
public MultiDeviceContactUpdateJob(boolean forceSync) {
this(null, forceSync);
}
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) {
this(context, address, true);
public MultiDeviceContactUpdateJob(@Nullable RecipientId recipientId) {
this(recipientId, true);
}
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) {
public MultiDeviceContactUpdateJob(@Nullable RecipientId recipientId, boolean forceSync) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue("MultiDeviceContactUpdateJob")
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED)
.build(),
address,
recipientId,
forceSync);
}
private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) {
private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable RecipientId recipientId, boolean forceSync) {
super(parameters);
this.forceSync = forceSync;
if (address != null) this.address = address.serialize();
else this.address = null;
this.recipientId = recipientId;
this.forceSync = forceSync;
}
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_ADDRESS, address)
return new Data.Builder().putString(KEY_RECIPIENT, recipientId != null ? recipientId.serialize() : null)
.putBoolean(KEY_FORCE_SYNC, forceSync)
.build();
}
@ -115,22 +114,22 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
return;
}
if (address == null) generateFullContactUpdate();
else generateSingleContactUpdate(Address.fromSerialized(address));
if (recipientId == null) generateFullContactUpdate();
else generateSingleContactUpdate(recipientId);
}
private void generateSingleContactUpdate(@NonNull Address address)
private void generateSingleContactUpdate(@NonNull RecipientId recipientId)
throws IOException, UntrustedIdentityException, NetworkException
{
File contactDataFile = createTempFile("multidevice-contact-update");
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
Recipient recipient = Recipient.from(context, address, false);
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
Recipient recipient = Recipient.resolved(recipientId);
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
out.write(new DeviceContact(address.toPhoneString(),
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(),
Optional.fromNullable(recipient.getName()),
getAvatar(recipient.getContactUri()),
Optional.fromNullable(recipient.getColor().serialize()),
@ -181,9 +180,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
for (ContactData contactData : contacts) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id));
Address address = Address.fromExternal(context, contactData.numbers.get(0).number);
Recipient recipient = Recipient.from(context, address, false);
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
Recipient recipient = Recipient.external(context, contactData.numbers.get(0).number);
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
Optional<String> name = Optional.fromNullable(contactData.name);
Optional<String> color = Optional.of(recipient.getColor().serialize());
@ -191,11 +189,11 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
boolean blocked = recipient.isBlocked();
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
}
if (ProfileKeyUtil.hasProfileKey(context)) {
Recipient self = Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), false);
Recipient self = Recipient.self();
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
Optional.absent(), Optional.absent(),
Optional.of(self.getColor().serialize()), Optional.absent(),
@ -302,7 +300,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
if (!identity.isPresent()) return Optional.absent();
String destination = recipient.getAddress().toPhoneString();
String destination = recipient.requireAddress().toPhoneString();
IdentityKey identityKey = identity.get().getIdentityKey();
VerifiedMessage.VerifiedState state;
@ -334,8 +332,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
public static final class Factory implements Job.Factory<MultiDeviceContactUpdateJob> {
@Override
public @NonNull MultiDeviceContactUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
String serialized = data.getString(KEY_ADDRESS);
Address address = serialized != null ? Address.fromSerialized(serialized) : null;
String serialized = data.getString(KEY_RECIPIENT);
RecipientId address = serialized != null ? RecipientId.from(serialized) : null;
return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC));
}

View File

@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@ -13,6 +12,7 @@ import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -84,11 +84,12 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
if (!record.isMms()) {
List<String> members = new LinkedList<>();
for (Address member : record.getMembers()) {
members.add(member.serialize());
for (RecipientId member : record.getMembers()) {
members.add(Recipient.resolved(member).requireAddress().serialize());
}
Recipient recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(record.getId(), record.isMms())), false);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(record.getId(), record.isMms()));
Recipient recipient = Recipient.resolved(recipientId);
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()),

View File

@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@ -14,6 +13,8 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -53,7 +54,7 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
this.messageIds = new LinkedList<>();
for (SyncMessageId messageId : messageIds) {
this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp()));
this.messageIds.add(new SerializableSyncMessageId(messageId.getRecipientId().serialize(), messageId.getTimetamp()));
}
}
@ -87,7 +88,8 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
List<ReadMessage> readMessages = new LinkedList<>();
for (SerializableSyncMessageId messageId : messageIds) {
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
readMessages.add(new ReadMessage(recipient.requireAddress().serialize(), messageId.timestamp));
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
@ -109,14 +111,14 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
private static final long serialVersionUID = 1L;
@JsonProperty
private final String sender;
private final String recipientId;
@JsonProperty
private final long timestamp;
private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
this.sender = sender;
this.timestamp = timestamp;
private SerializableSyncMessageId(@JsonProperty("recipientId") String recipientId, @JsonProperty("timestamp") long timestamp) {
this.recipientId = recipientId;
this.timestamp = timestamp;
}
}
@ -131,11 +133,10 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
throw new AssertionError(e);
}
})
.map(id -> new SyncMessageId(Address.fromSerialized(id.sender), id.timestamp))
.map(id -> new SyncMessageId(RecipientId.from(id.recipientId), id.timestamp))
.toList();
return new MultiDeviceReadUpdateJob(parameters, ids);
}
}
}

View File

@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -37,12 +38,12 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
private static final String KEY_VERIFIED_STATUS = "verified_status";
private static final String KEY_TIMESTAMP = "timestamp";
private String destination;
private RecipientId destination;
private byte[] identityKey;
private VerifiedStatus verifiedStatus;
private long timestamp;
public MultiDeviceVerifiedUpdateJob(Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
public MultiDeviceVerifiedUpdateJob(@NonNull RecipientId destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue("__MULTI_DEVICE_VERIFIED_UPDATE__")
@ -56,14 +57,14 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
}
private MultiDeviceVerifiedUpdateJob(@NonNull Job.Parameters parameters,
@NonNull Address destination,
@NonNull RecipientId destination,
@NonNull byte[] identityKey,
@NonNull VerifiedStatus verifiedStatus,
long timestamp)
{
super(parameters);
this.destination = destination.serialize();
this.destination = destination;
this.identityKey = identityKey;
this.verifiedStatus = verifiedStatus;
this.timestamp = timestamp;
@ -71,7 +72,7 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
@Override
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_DESTINATION, destination)
return new Data.Builder().putString(KEY_DESTINATION, destination.serialize())
.putString(KEY_IDENTITY_KEY, Base64.encodeBytes(identityKey))
.putInt(KEY_VERIFIED_STATUS, verifiedStatus.toInt())
.putLong(KEY_TIMESTAMP, timestamp)
@ -97,12 +98,13 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Address canonicalDestination = Address.fromSerialized(destination);
Recipient recipient = Recipient.resolved(destination);
Address canonicalDestination = recipient.requireAddress();
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
UnidentifiedAccessUtil.getAccessFor(context, recipient));
} catch (InvalidKeyException e) {
throw new IOException(e);
}
@ -135,7 +137,7 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
@Override
public @NonNull MultiDeviceVerifiedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
try {
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
RecipientId destination = RecipientId.from(data.getString(KEY_DESTINATION));
VerifiedStatus verifiedStatus = VerifiedStatus.forState(data.getInt(KEY_VERIFIED_STATUS));
long timestamp = data.getLong(KEY_TIMESTAMP);
byte[] identityKey = Base64.decode(data.getString(KEY_IDENTITY_KEY));

View File

@ -5,13 +5,14 @@ import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -45,7 +46,7 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
private MultiDeviceViewOnceOpenJob(@NonNull Parameters parameters, @NonNull SyncMessageId syncMessageId) {
super(parameters);
this.messageId = new SerializableSyncMessageId(syncMessageId.getAddress().toPhoneString(), syncMessageId.getTimetamp());
this.messageId = new SerializableSyncMessageId(syncMessageId.getRecipientId().serialize(), syncMessageId.getTimetamp());
}
@Override
@ -74,7 +75,8 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(messageId.sender, messageId.timestamp);
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(recipient.requireAddress().serialize(), messageId.timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
}
@ -94,14 +96,14 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
private static final long serialVersionUID = 1L;
@JsonProperty
private final String sender;
private final String recipientId;
@JsonProperty
private final long timestamp;
private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
this.sender = sender;
this.timestamp = timestamp;
private SerializableSyncMessageId(@JsonProperty("recipientId") String recipientId, @JsonProperty("timestamp") long timestamp) {
this.recipientId = recipientId;
this.timestamp = timestamp;
}
}
@ -116,7 +118,7 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
throw new AssertionError(e);
}
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(messageId.sender), messageId.timestamp);
SyncMessageId syncMessageId = new SyncMessageId(RecipientId.from(messageId.recipientId), messageId.timestamp);
return new MultiDeviceViewOnceOpenJob(parameters, syncMessageId);
}

View File

@ -28,7 +28,6 @@ import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.signal.libsignal.metadata.SelfSendException;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.IncomingMessageProcessor;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
@ -81,6 +80,7 @@ import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
@ -287,7 +287,7 @@ public class PushDecryptJob extends BaseJob {
Log.w(TAG, "Got unrecognized message...");
}
resetRecipientToPush(Recipient.from(context, Address.fromExternal(context, content.getSender()), false));
resetRecipientToPush(Recipient.external(context, content.getSender()));
if (envelope.isPreKeySignalMessage()) {
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
@ -337,7 +337,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
@ -353,7 +353,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
context.startService(intent);
@ -367,7 +367,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
@ -387,7 +387,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
context.startService(intent);
}
@ -399,7 +399,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
context.startService(intent);
}
@ -408,7 +408,7 @@ public class PushDecryptJob extends BaseJob {
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
content.getSenderDevice(),
content.getTimestamp(),
"", Optional.absent(), 0,
@ -445,9 +445,9 @@ public class PushDecryptJob extends BaseJob {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
if (!recipient.isGroupRecipient()) {
if (!recipient.isGroup()) {
SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(recipient.getAddress().toPhoneString());
sessionStore.deleteAllSessions(recipient.requireAddress().toPhoneString());
SecurityEvent.broadcastSecurityUpdateEvent(context);
@ -481,7 +481,7 @@ public class PushDecryptJob extends BaseJob {
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RequestGroupInfoJob(content.getSender(), group.getGroupId()));
.add(new RequestGroupInfoJob(Recipient.external(context, content.getSender()).getId(), group.getGroupId()));
}
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
@ -492,7 +492,7 @@ public class PushDecryptJob extends BaseJob {
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(content, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(recipient.getId(),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
false,
@ -507,7 +507,7 @@ public class PushDecryptJob extends BaseJob {
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), message.getExpiresInSeconds());
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
@ -574,12 +574,12 @@ public class PushDecryptJob extends BaseJob {
if (message.getMessage().getProfileKey().isPresent()) {
Recipient recipient = null;
if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
if (message.getDestination().isPresent()) recipient = Recipient.external(context, message.getDestination().get());
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
}
}
@ -599,7 +599,7 @@ public class PushDecryptJob extends BaseJob {
if (message.isContactsRequest()) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(context, true));
.add(new MultiDeviceContactUpdateJob(true));
ApplicationContext.getInstance(context)
.getJobManager()
@ -635,8 +635,8 @@ public class PushDecryptJob extends BaseJob {
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
{
for (ReadMessage readMessage : readMessages) {
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
for (Pair<Long, Long> expiringMessage : expiringText) {
ApplicationContext.getInstance(context)
@ -657,7 +657,7 @@ public class PushDecryptJob extends BaseJob {
}
private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
Address author = Address.fromExternal(context, openMessage.getSender());
RecipientId author = Recipient.external(context, openMessage.getSender()).getId();
long timestamp = openMessage.getTimestamp();
MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author);
@ -687,7 +687,7 @@ public class PushDecryptJob extends BaseJob {
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.external(context, content.getSender()).getId(),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false,
message.isViewOnce(),
@ -749,7 +749,7 @@ public class PushDecryptJob extends BaseJob {
database.markAsSent(messageId, true);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getMessage().getExpiresInSeconds());
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), message.getMessage().getExpiresInSeconds());
return threadId;
}
@ -793,12 +793,12 @@ public class PushDecryptJob extends BaseJob {
try {
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
if (recipients.getAddress().isGroup()) {
updateGroupReceiptStatus(message, messageId, recipients.getAddress().toGroupString());
if (recipients.requireAddress().isGroup()) {
updateGroupReceiptStatus(message, messageId, recipients.requireAddress().toGroupString());
}
database.markAsSent(messageId, true);
database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize()));
database.markUnidentified(messageId, message.isUnidentified(recipients.requireAddress().serialize()));
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId);
List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
@ -822,7 +822,7 @@ public class PushDecryptJob extends BaseJob {
}
if (recipients.isLocalNumber()) {
SyncMessageId id = new SyncMessageId(recipients.getAddress(), message.getTimestamp());
SyncMessageId id = new SyncMessageId(recipients.getId(), message.getTimestamp());
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
}
@ -837,14 +837,13 @@ public class PushDecryptJob extends BaseJob {
private void handleGroupRecipientUpdate(@NonNull SentTranscriptMessage message) {
Recipient recipient = getSyncMessageDestination(message);
if (!recipient.isGroupRecipient()) {
if (!recipient.isGroup()) {
Log.w(TAG, "Got recipient update for a non-group message! Skipping.");
return;
}
MmsSmsDatabase database = DatabaseFactory.getMmsSmsDatabase(context);
MessageRecord record = database.getMessageFor(message.getTimestamp(),
Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
MessageRecord record = database.getMessageFor(message.getTimestamp(), Recipient.self().getId());
if (record == null) {
Log.w(TAG, "Got recipient update for non-existing message! Skipping.");
@ -856,26 +855,27 @@ public class PushDecryptJob extends BaseJob {
return;
}
updateGroupReceiptStatus(message, record.getId(), recipient.getAddress().toGroupString());
updateGroupReceiptStatus(message, record.getId(), recipient.requireAddress().toGroupString());
}
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull String groupString) {
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
Map<String, Integer> localReceipts = Stream.of(receiptDatabase.getGroupReceiptInfo(messageId))
.collect(Collectors.toMap(info -> info.getAddress().serialize(), GroupReceiptInfo::getStatus));
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.external(context, address)).toList();
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
Map<RecipientId, Integer> localReceipts = Stream.of(receiptDatabase.getGroupReceiptInfo(messageId))
.collect(Collectors.toMap(GroupReceiptInfo::getRecipientId, GroupReceiptInfo::getStatus));
for (String address : message.getRecipients()) {
for (Recipient messageRecipient : messageRecipients) {
//noinspection ConstantConditions
if (localReceipts.containsKey(address) && localReceipts.get(address) < GroupReceiptDatabase.STATUS_UNDELIVERED) {
receiptDatabase.update(Address.fromSerialized(address), messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getTimestamp());
} else if (!localReceipts.containsKey(address)) {
receiptDatabase.insert(Collections.singletonList(Address.fromSerialized(address)), messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getTimestamp());
if (localReceipts.containsKey(messageRecipient.getId()) && localReceipts.get(messageRecipient.getId()) < GroupReceiptDatabase.STATUS_UNDELIVERED) {
receiptDatabase.update(messageRecipient.getId(), messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getTimestamp());
} else if (!localReceipts.containsKey(messageRecipient.getId())) {
receiptDatabase.insert(Collections.singletonList(messageRecipient.getId()), messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getTimestamp());
}
}
for (Recipient member : members) {
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
receiptDatabase.setUnidentified(member.getId(), messageId, message.isUnidentified(member.requireAddress().serialize()));
}
}
@ -899,7 +899,7 @@ public class PushDecryptJob extends BaseJob {
} else {
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
content.getSenderDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
@ -932,7 +932,7 @@ public class PushDecryptJob extends BaseJob {
}
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
boolean isGroup = recipient.getAddress().isGroup();
boolean isGroup = recipient.requireAddress().isGroup();
MessagingDatabase database;
long messageId;
@ -944,13 +944,13 @@ public class PushDecryptJob extends BaseJob {
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
database = DatabaseFactory.getMmsDatabase(context);
updateGroupReceiptStatus(message, messageId, recipient.getAddress().toGroupString());
updateGroupReceiptStatus(message, messageId, recipient.requireAddress().toGroupString());
} else {
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database = DatabaseFactory.getSmsDatabase(context);
database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize()));
database.markUnidentified(messageId, message.isUnidentified(recipient.requireAddress().serialize()));
}
database.markAsSent(messageId, true);
@ -963,7 +963,7 @@ public class PushDecryptJob extends BaseJob {
}
if (recipient.isLocalNumber()) {
SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getTimestamp());
SyncMessageId id = new SyncMessageId(recipient.getId(), message.getTimestamp());
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
}
@ -1098,13 +1098,12 @@ public class PushDecryptJob extends BaseJob {
private void handleProfileKey(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message)
{
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Address sourceAddress = Address.fromExternal(context, content.getSender());
Recipient recipient = Recipient.from(context, sourceAddress, false);
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Recipient recipient = Recipient.external(context, content.getSender());
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
database.setProfileKey(recipient, message.getProfileKey().get());
database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN);
database.setProfileKey(recipient.getId(), message.getProfileKey().get());
database.setUnidentifiedAccessMode(recipient.getId(), RecipientDatabase.UnidentifiedAccessMode.UNKNOWN);
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileJob(recipient));
}
}
@ -1114,7 +1113,7 @@ public class PushDecryptJob extends BaseJob {
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SendDeliveryReceiptJob(Address.fromExternal(context, content.getSender()), message.getTimestamp()));
.add(new SendDeliveryReceiptJob(Recipient.external(context, content.getSender()).getId(), message.getTimestamp()));
}
@SuppressLint("DefaultLocale")
@ -1124,7 +1123,7 @@ public class PushDecryptJob extends BaseJob {
for (long timestamp : message.getTimestamps()) {
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), System.currentTimeMillis());
.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), System.currentTimeMillis());
}
}
@ -1137,7 +1136,7 @@ public class PushDecryptJob extends BaseJob {
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), content.getTimestamp());
.incrementReadReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), content.getTimestamp());
}
}
}
@ -1149,13 +1148,13 @@ public class PushDecryptJob extends BaseJob {
return;
}
Recipient author = Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
Recipient author = Recipient.external(context, content.getSender());
long threadId;
if (typingMessage.getGroupId().isPresent()) {
Address groupAddress = Address.fromExternal(context, GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false));
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false));
Recipient groupRecipient = Recipient.resolved(recipientId);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
} else {
@ -1200,7 +1199,7 @@ public class PushDecryptJob extends BaseJob {
return Optional.absent();
}
Address author = Address.fromExternal(context, quote.get().getAuthor().getNumber());
RecipientId author = Recipient.external(context, quote.get().getAuthor().getNumber()).getId();
MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author);
if (message != null) {
@ -1315,7 +1314,7 @@ public class PushDecryptJob extends BaseJob {
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp, Optional<SignalServiceGroup> group) {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender),
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(),
senderDevice, timestamp, "",
group, 0, false);
@ -1325,22 +1324,22 @@ public class PushDecryptJob extends BaseJob {
private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().getGroupInfo().isPresent()) {
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
return Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
} else {
return Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
return Recipient.external(context, message.getDestination().get());
}
}
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
return Recipient.external(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false));
} else {
return Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
return Recipient.external(context, content.getSender());
}
}
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
Recipient author = Recipient.from(context, Address.fromExternal(context, sender), false);
Recipient author = Recipient.external(context, sender);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
if (threadId > 0) {
@ -1355,15 +1354,15 @@ public class PushDecryptJob extends BaseJob {
return true;
}
Recipient sender = Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
Recipient sender = Recipient.external(context, content.getSender());
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
Recipient conversation = getMessageDestination(content, message);
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
if (conversation.isGroup() && conversation.isBlocked()) {
return true;
} else if (conversation.isGroupRecipient()) {
} else if (conversation.isGroup()) {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<String> groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))
: Optional.absent();
@ -1392,7 +1391,7 @@ public class PushDecryptJob extends BaseJob {
private void resetRecipientToPush(@NonNull Recipient recipient) {
if (recipient.isForceSmsSelection()) {
DatabaseFactory.getRecipientDatabase(context).setForceSmsSelection(recipient, false);
DatabaseFactory.getRecipientDatabase(context).setForceSmsSelection(recipient.getId(), false);
}
}

Some files were not shown because too many files have changed in this diff Show More