From 737810475e8596164a868f4081f2ef11e8d1aa8d Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 26 Jul 2017 09:59:15 -0700 Subject: [PATCH] Remove the Canonical Address Database This was a holdover from Signal's origins as a pure SMS app. It causes problems, depends on undefined device specific behavior, and should no longer be necessary now that we have all the information we need to E164 all numbers. // FREEBIE --- build.gradle | 2 +- .../securesms/BlockedContactsActivity.java | 11 +- .../securesms/ConfirmIdentityDialog.java | 36 +- .../securesms/ConversationActivity.java | 62 +-- .../securesms/ConversationFragment.java | 15 +- .../securesms/ConversationItem.java | 6 +- .../securesms/ConversationListActivity.java | 7 +- .../securesms/ConversationListAdapter.java | 5 +- .../ConversationListArchiveActivity.java | 2 +- .../securesms/ConversationPopupActivity.java | 2 +- .../securesms/ConversationTitleView.java | 4 +- .../securesms/ConversationUpdateItem.java | 2 +- .../securesms/DatabaseUpgradeActivity.java | 11 +- .../securesms/GroupCreateActivity.java | 42 +- .../securesms/GroupMembersDialog.java | 20 +- .../securesms/InviteActivity.java | 5 +- .../thoughtcrime/securesms/MediaAdapter.java | 13 +- .../securesms/MediaOverviewActivity.java | 31 +- .../securesms/MediaPreviewActivity.java | 9 +- .../securesms/MessageDetailsActivity.java | 9 +- .../MessageDetailsRecipientAdapter.java | 10 +- .../securesms/MessageRecipientListItem.java | 6 +- .../securesms/NewConversationActivity.java | 5 +- .../RecipientPreferenceActivity.java | 24 +- .../thoughtcrime/securesms/ShareActivity.java | 36 +- .../securesms/SmsSendtoActivity.java | 13 +- .../securesms/VerifyIdentityActivity.java | 77 +-- .../securesms/WebRtcCallActivity.java | 4 +- .../securesms/components/AvatarImageView.java | 6 +- .../components/PushRecipientsPanel.java | 69 ++- .../components/SingleRecipientPanel.java | 176 ------- .../identity/UntrustedSendDialog.java | 2 +- .../identity/UnverifiedSendDialog.java | 2 +- .../components/webrtc/WebRtcCallScreen.java | 4 +- .../contacts/ContactSelectionListItem.java | 8 +- .../contacts/ContactsCursorLoader.java | 5 +- .../securesms/contacts/RecipientsEditor.java | 10 +- .../securesms/crypto/SessionUtil.java | 7 +- .../storage/TextSecureIdentityKeyStore.java | 22 +- .../storage/TextSecureSessionStore.java | 26 +- .../securesms/database/Address.java | 156 ++++++ .../database/CanonicalAddressDatabase.java | 261 ---------- .../database/CanonicalSessionMigrator.java | 70 --- .../securesms/database/DatabaseFactory.java | 476 +++++++++++++++++- .../securesms/database/EarlyReceiptCache.java | 8 +- .../securesms/database/GroupDatabase.java | 52 +- .../securesms/database/IdentityDatabase.java | 50 +- .../securesms/database/MediaDatabase.java | 15 +- .../securesms/database/MessagingDatabase.java | 19 +- .../database/MmsAddressDatabase.java | 35 +- .../securesms/database/MmsAddresses.java | 32 +- .../securesms/database/MmsDatabase.java | 180 +++---- .../database/PlaintextBackupExporter.java | 2 +- .../database/PlaintextBackupImporter.java | 2 +- .../database/RecipientPreferenceDatabase.java | 32 +- .../securesms/database/SmsDatabase.java | 179 +++---- .../securesms/database/SmsMigrator.java | 21 +- .../database/TextSecureDirectory.java | 107 ++-- .../securesms/database/ThreadDatabase.java | 118 ++--- .../documents/IdentityKeyMismatch.java | 17 +- .../documents/IdentityKeyMismatchList.java | 2 + .../database/documents/NetworkFailure.java | 18 +- .../database/identity/IdentityRecordList.java | 4 +- .../loaders/ConversationListLoader.java | 11 +- .../securesms/events/WebRtcViewModel.java | 2 +- .../securesms/groups/GroupManager.java | 50 +- .../groups/GroupMessageProcessor.java | 48 +- .../securesms/jobs/MmsDownloadJob.java | 5 +- .../securesms/jobs/MmsReceiveJob.java | 3 +- .../securesms/jobs/MmsSendJob.java | 12 +- .../jobs/MultiDeviceBlockedUpdateJob.java | 9 +- .../jobs/MultiDeviceContactUpdateJob.java | 48 +- .../jobs/MultiDeviceGroupUpdateJob.java | 11 +- .../jobs/MultiDeviceReadUpdateJob.java | 2 +- .../jobs/MultiDeviceVerifiedUpdateJob.java | 13 +- .../securesms/jobs/PushDecryptJob.java | 61 ++- .../securesms/jobs/PushGroupSendJob.java | 50 +- .../securesms/jobs/PushGroupUpdateJob.java | 10 +- .../securesms/jobs/PushMediaSendJob.java | 23 +- .../securesms/jobs/PushReceivedJob.java | 22 +- .../securesms/jobs/PushSendJob.java | 14 +- .../securesms/jobs/PushTextSendJob.java | 15 +- .../securesms/jobs/RetrieveProfileJob.java | 14 +- .../securesms/jobs/SmsReceiveJob.java | 5 +- .../securesms/jobs/SmsSendJob.java | 2 +- .../securesms/jobs/SmsSentJob.java | 5 +- .../securesms/mms/IncomingMediaMessage.java | 35 +- .../AndroidAutoReplyReceiver.java | 20 +- .../notifications/MessageNotifier.java | 2 +- .../notifications/NotificationItem.java | 2 +- .../notifications/NotificationState.java | 6 +- .../notifications/RemoteReplyReceiver.java | 13 +- .../SingleRecipientNotificationBuilder.java | 10 +- .../securesms/recipients/Recipient.java | 37 +- .../recipients/RecipientFactory.java | 104 +--- .../recipients/RecipientProvider.java | 114 ++--- .../securesms/recipients/Recipients.java | 69 +-- .../securesms/service/DirectShareService.java | 10 +- .../service/QuickResponseService.java | 15 +- .../service/RegistrationService.java | 7 +- .../securesms/service/WebRtcCallService.java | 39 +- .../securesms/sms/IncomingJoinedMessage.java | 3 +- .../securesms/sms/IncomingTextMessage.java | 31 +- .../securesms/sms/MessageSender.java | 77 ++- .../securesms/util/DirectoryHelper.java | 75 ++- .../securesms/util/GroupUtil.java | 11 +- .../securesms/util/IdentityUtil.java | 42 +- .../securesms/util/NumberUtil.java | 42 +- .../util/SelectedRecipientsAdapter.java | 2 +- src/org/thoughtcrime/securesms/util/Util.java | 41 +- .../securesms/util/VerifySpan.java | 11 +- .../securesms/webrtc/VoiceCallShare.java | 6 +- .../CanonicalAddressDatabaseTest.java | 238 ++++----- 113 files changed, 2029 insertions(+), 2130 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/components/SingleRecipientPanel.java create mode 100644 src/org/thoughtcrime/securesms/database/Address.java delete mode 100644 src/org/thoughtcrime/securesms/database/CanonicalAddressDatabase.java delete mode 100644 src/org/thoughtcrime/securesms/database/CanonicalSessionMigrator.java diff --git a/build.gradle b/build.gradle index 1ee0f3fd4b..38e7a2c88f 100644 --- a/build.gradle +++ b/build.gradle @@ -203,7 +203,7 @@ android { } defaultConfig { - versionCode 275 + versionCode 276 versionName "4.8.1" minSdkVersion 9 diff --git a/src/org/thoughtcrime/securesms/BlockedContactsActivity.java b/src/org/thoughtcrime/securesms/BlockedContactsActivity.java index e9f4f491bc..c391c879c8 100644 --- a/src/org/thoughtcrime/securesms/BlockedContactsActivity.java +++ b/src/org/thoughtcrime/securesms/BlockedContactsActivity.java @@ -17,6 +17,7 @@ import android.widget.AdapterView; import android.widget.ListView; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader; import org.thoughtcrime.securesms.preferences.BlockedContactListItem; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -24,6 +25,8 @@ import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; +import java.util.List; + public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity { private final DynamicTheme dynamicTheme = new DynamicTheme(); @@ -105,7 +108,7 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity public void onItemClick(AdapterView parent, View view, int position, long id) { Recipients recipients = ((BlockedContactListItem)view).getRecipients(); Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class); - intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses()); startActivity(intent); } @@ -124,8 +127,10 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity @Override public void bindView(View view, Context context, Cursor cursor) { - String recipientIds = cursor.getString(1); - Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, true); + String addressesConcat = cursor.getString(1); + List
addresses = Address.fromSerializedList(addressesConcat, " "); + + Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), true); ((BlockedContactListItem) view).set(recipients); } diff --git a/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java b/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java index a926447723..c33a6c4ff5 100644 --- a/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java +++ b/src/org/thoughtcrime/securesms/ConfirmIdentityDialog.java @@ -8,12 +8,11 @@ import android.support.v7.app.AlertDialog; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; -import android.util.Log; import android.widget.TextView; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsAddressDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; @@ -28,12 +27,9 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.VerifySpan; import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.IdentityKeyStore; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.IOException; @@ -53,10 +49,8 @@ public class ConfirmIdentityDialog extends AlertDialog { { super(context); - try { - Recipient recipient = RecipientFactory.getRecipientForId(context, mismatch.getRecipientId(), false); + Recipient recipient = RecipientFactory.getRecipientFor(context, mismatch.getAddress(), false); String name = recipient.toShortString(); - String number = Util.canonicalizeNumber(context, recipient.getNumber()); String introduction = String.format(context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed), name, name); SpannableString spannableString = new SpannableString(introduction + " " + context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact)); @@ -68,11 +62,8 @@ public class ConfirmIdentityDialog extends AlertDialog { setTitle(name); setMessage(spannableString); - setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, number)); + setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, recipient.getAddress())); setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener()); - } catch (InvalidNumberException e) { - throw new AssertionError(e); - } } @Override @@ -91,13 +82,13 @@ public class ConfirmIdentityDialog extends AlertDialog { private final MasterSecret masterSecret; private final MessageRecord messageRecord; private final IdentityKeyMismatch mismatch; - private final String number; + private final Address address; - private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, String number) { + private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) { this.masterSecret = masterSecret; this.messageRecord = messageRecord; this.mismatch = mismatch; - this.number = number; + this.address = address; } @Override @@ -107,7 +98,7 @@ public class ConfirmIdentityDialog extends AlertDialog { @Override protected Void doInBackground(Void... params) { synchronized (SESSION_LOCK) { - SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(number, 1); + SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1); TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext()); identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true); @@ -151,16 +142,16 @@ public class ConfirmIdentityDialog extends AlertDialog { if (messageRecord.isMms()) { mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getRecipientId(), + mismatch.getAddress(), mismatch.getIdentityKey()); Recipients recipients = mmsAddressDatabase.getRecipientsForId(messageRecord.getId()); - if (recipients.isGroupRecipient()) MessageSender.resendGroupMessage(getContext(), masterSecret, messageRecord, mismatch.getRecipientId()); + if (recipients.isGroupRecipient()) MessageSender.resendGroupMessage(getContext(), masterSecret, messageRecord, mismatch.getAddress()); else MessageSender.resend(getContext(), masterSecret, messageRecord); } else { smsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getRecipientId(), + mismatch.getAddress(), mismatch.getIdentityKey()); MessageSender.resend(getContext(), masterSecret, messageRecord); @@ -173,13 +164,13 @@ public class ConfirmIdentityDialog extends AlertDialog { SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); smsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getRecipientId(), + mismatch.getAddress(), mismatch.getIdentityKey()); boolean legacy = !messageRecord.isContentBundleKeyExchange(); SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE, - messageRecord.getIndividualRecipient().getNumber(), + messageRecord.getIndividualRecipient().getAddress().toPhoneString(), messageRecord.getRecipientDeviceId(), "", messageRecord.getDateSent(), legacy ? Base64.decode(messageRecord.getBody().getBody()) : null, @@ -189,8 +180,7 @@ public class ConfirmIdentityDialog extends AlertDialog { ApplicationContext.getInstance(getContext()) .getJobManager() - .add(new PushDecryptJob(getContext(), pushId, messageRecord.getId(), - messageRecord.getIndividualRecipient().getNumber())); + .add(new PushDecryptJob(getContext(), pushId, messageRecord.getId())); } catch (IOException e) { throw new AssertionError(e); } diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 065f03af6e..68ea836ee2 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -97,6 +97,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterSecret; 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; @@ -164,7 +165,7 @@ import org.whispersystems.libsignal.util.guava.Optional; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.LinkedList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; @@ -191,7 +192,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity { private static final String TAG = ConversationActivity.class.getSimpleName(); - public static final String RECIPIENTS_EXTRA = "recipients"; + public static final String ADDRESSES_EXTRA = "addresses"; 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"; @@ -409,7 +410,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity addAttachmentContactInfo(data.getData()); break; case GROUP_EDIT: - recipients = RecipientFactory.getRecipientsForIds(this, data.getLongArrayExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA), true); + recipients = RecipientFactory.getRecipientsFor(this, RecipientFactory.getRecipientFor(this, (Address)data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true), true); recipients.addListener(this); titleView.setTitle(recipients); setBlockedUserState(recipients, isSecureText, isDefaultSms); @@ -421,7 +422,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } break; case ADD_CONTACT: - recipients = RecipientFactory.getRecipientsForIds(ConversationActivity.this, recipients.getIds(), true); + recipients = RecipientFactory.getRecipientsFor(this, recipients.getAddresses(), true); recipients.addListener(this); fragment.reloadList(); break; @@ -679,7 +680,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity composeText.appendInvite(inviteText); } else { Intent intent = new Intent(Intent.ACTION_SENDTO); - intent.setData(Uri.parse("smsto:" + recipients.getPrimaryRecipient().getNumber())); + intent.setData(Uri.parse("smsto:" + recipients.getPrimaryRecipient().getAddress().serialize())); intent.putExtra("sms_body", inviteText); intent.putExtra(Intent.EXTRA_TEXT, inviteText); startActivity(intent); @@ -725,7 +726,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleViewMedia() { Intent intent = new Intent(this, MediaOverviewActivity.class); intent.putExtra(MediaOverviewActivity.THREAD_ID_EXTRA, threadId); - intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId()); + intent.putExtra(MediaOverviewActivity.ADDRESSES_EXTRA, recipients.getAddresses()); startActivity(intent); } @@ -746,7 +747,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public void onClick(DialogInterface dialog, int which) { Context self = ConversationActivity.this; try { - byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getNumber()); + byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString()); DatabaseFactory.getGroupDatabase(self).setActive(groupId, false); GroupContext context = GroupContext.newBuilder() @@ -756,7 +757,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis(), 0); MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null); - DatabaseFactory.getGroupDatabase(self).remove(groupId, TextSecurePreferences.getLocalNumber(self)); + DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self))); initializeEnabledCheck(); } catch (IOException e) { Log.w(TAG, e); @@ -771,7 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleEditPushGroup() { Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class); - intent.putExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId()); + intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress()); startActivityForResult(intent, GROUP_EDIT); } @@ -813,7 +814,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (isSecureText) { Intent intent = new Intent(this, WebRtcCallService.class); intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, recipient.getNumber()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress()); startService(intent); Intent activityIntent = new Intent(this, WebRtcCallActivity.class); @@ -822,7 +823,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else { try { Intent dialIntent = new Intent(Intent.ACTION_DIAL, - Uri.parse("tel:" + recipient.getNumber())); + Uri.parse("tel:" + recipient.getAddress().serialize())); startActivity(dialIntent); } catch (ActivityNotFoundException anfe) { Log.w(TAG, anfe); @@ -838,9 +839,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleAddToContacts() { + if (recipients.getPrimaryRecipient().getAddress().isGroup()) return; + try { final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getNumber()); + if (recipients.getPrimaryRecipient().getAddress().isEmail()) { + intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipients.getPrimaryRecipient().getAddress().toEmailString()); + } else { + intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getAddress().toPhoneString()); + } intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); startActivityForResult(intent, ADD_CONTACT); } catch (ActivityNotFoundException e) { @@ -1103,12 +1110,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (recipients.isGroupRecipient()) { recipients = DatabaseFactory.getGroupDatabase(ConversationActivity.this) - .getGroupMembers(GroupUtil.getDecodedId(recipients.getPrimaryRecipient().getNumber()), false); + .getGroupMembers(GroupUtil.getDecodedId(recipients.getPrimaryRecipient().getAddress().toGroupString()), false); } - for (long recipientId : recipients.getIds()) { - Log.w(TAG, "Loading identity for: " + recipientId); - identityRecordList.add(identityDatabase.getIdentity(recipientId)); + for (Address recipientAddress : recipients.getAddresses()) { + Log.w(TAG, "Loading identity for: " + recipientAddress); + identityRecordList.add(identityDatabase.getIdentity(recipientAddress)); } String message = null; @@ -1206,7 +1213,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onClick(View v) { Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class); - intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, isSecureText && !isSelfConversation()); @@ -1253,7 +1260,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void initializeResources() { if (recipients != null) recipients.removeListener(this); - recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getLongArrayExtra(RECIPIENTS_EXTRA), true); + recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false); distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); @@ -1295,7 +1302,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Subscribe(threadMode = ThreadMode.MAIN) public void onRecipientPreferenceUpdate(final RecipientPreferenceEvent event) { - if (event.getRecipients().getSortedIdsString().equals(this.recipients.getSortedIdsString())) { + if (Arrays.equals(event.getRecipients().getAddresses(), this.recipients.getAddresses())) { new RecipientPreferencesTask().execute(this.recipients); } } @@ -1319,9 +1326,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public void onReceive(Context context, Intent intent) { Log.w(TAG, "Group update received..."); if (recipients != null) { - long[] ids = recipients.getIds(); Log.w(TAG, "Looking up new recipients..."); - recipients = RecipientFactory.getRecipientsForIds(context, ids, true); + recipients = RecipientFactory.getRecipientsFor(context, recipients.getAddresses(), true); recipients.addListener(ConversationActivity.this); onModified(recipients); fragment.reloadList(); @@ -1501,7 +1507,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (!isGroupConversation()) return false; try { - byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getNumber()); + byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString()); GroupRecord record = DatabaseFactory.getGroupDatabase(this).getGroup(groupId); return record != null && record.isActive(); @@ -1516,7 +1522,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (!recipients.isSingleRecipient()) return false; if (recipients.getPrimaryRecipient().isGroupRecipient()) return false; - return Util.isOwnNumber(this, recipients.getPrimaryRecipient().getNumber()); + return Util.isOwnNumber(this, recipients.getPrimaryRecipient().getAddress()); } private boolean isGroupConversation() { @@ -2000,7 +2006,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } Optional prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) - .getRecipientsPreferences(recipients[0].getIds()); + .getRecipientsPreferences(recipients[0].getAddresses()); return new Pair<>(recipients[0], prefs.orNull()); } @@ -2023,7 +2029,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity protected Void doInBackground(Void... params) { synchronized (SESSION_LOCK) { for (IdentityRecord identityRecord : unverifiedIdentities) { - identityDatabase.setVerified(identityRecord.getRecipientId(), + identityDatabase.setVerified(identityRecord.getAddress(), identityRecord.getIdentityKey(), VerifiedStatus.DEFAULT); } @@ -2046,7 +2052,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Log.w(TAG, "onClicked: " + unverifiedIdentities.size()); if (unverifiedIdentities.size() == 1) { Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, unverifiedIdentities.get(0).getRecipientId()); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(0).getAddress()); intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(0).getIdentityKey())); intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); @@ -2055,7 +2061,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity String[] unverifiedNames = new String[unverifiedIdentities.size()]; for (int i=0;i= VERSION_CODES.JELLY_BEAN) { diff --git a/src/org/thoughtcrime/securesms/ConversationTitleView.java b/src/org/thoughtcrime/securesms/ConversationTitleView.java index 93676a00bd..86a35278ba 100644 --- a/src/org/thoughtcrime/securesms/ConversationTitleView.java +++ b/src/org/thoughtcrime/securesms/ConversationTitleView.java @@ -69,14 +69,14 @@ public class ConversationTitleView extends LinearLayout { private void setRecipientTitle(Recipient recipient) { if (!recipient.isGroupRecipient()) { if (TextUtils.isEmpty(recipient.getName())) { - this.title.setText(recipient.getNumber()); + this.title.setText(recipient.getAddress().serialize()); this.subtitle.setText(null); this.subtitle.setVisibility(View.GONE); } else { this.title.setText(recipient.getName()); if (recipient.getCustomLabel() != null) this.subtitle.setText(recipient.getCustomLabel()); - else this.subtitle.setText(recipient.getNumber()); + else this.subtitle.setText(recipient.getAddress().serialize()); this.subtitle.setVisibility(View.VISIBLE); } diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java index cbf321f3cc..ab5e8a6a26 100644 --- a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java +++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java @@ -221,7 +221,7 @@ public class ConversationUpdateItem extends LinearLayout public void onSuccess(Optional result) { if (result.isPresent()) { Intent intent = new Intent(getContext(), VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, sender.getRecipientId()); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress()); intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey())); intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); diff --git a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java index d3b21ffc80..e03dece50a 100644 --- a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java +++ b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java @@ -68,7 +68,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { public static final int CONTACTS_ACCOUNT_VERSION = 136; public static final int MEDIA_DOWNLOAD_CONTROLS_VERSION = 151; public static final int REDPHONE_SUPPORT_VERSION = 157; -// public static final int FINGERPRINTS_NON_BLOCKING_VESRION = 212; + public static final int NO_MORE_CANONICAL_DB_VERSION = 276; private static final SortedSet UPGRADE_VERSIONS = new TreeSet() {{ add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); @@ -82,7 +82,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { add(MIGRATE_SESSION_PLAINTEXT); add(MEDIA_DOWNLOAD_CONTROLS_VERSION); add(REDPHONE_SUPPORT_VERSION); -// add(FINGERPRINTS_NON_BLOCKING_VESRION); + add(NO_MORE_CANONICAL_DB_VERSION); }}; private MasterSecret masterSecret; @@ -233,10 +233,6 @@ public class DatabaseUpgradeActivity extends BaseActivity { .add(new DirectoryRefreshJob(getApplicationContext())); } -// if (params[0] < FINGERPRINTS_NON_BLOCKING_VESRION) { -// TextSecurePreferences.setBlockingIdentityUpdates(getApplicationContext(), true); -// } - return null; } @@ -274,8 +270,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { ApplicationContext.getInstance(getApplicationContext()) .getJobManager() .add(new PushDecryptJob(getApplicationContext(), - pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)), - pushReader.getString(pushReader.getColumnIndexOrThrow(PushDatabase.SOURCE)))); + pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)))); } } finally { if (pushReader != null) diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index ee4f07b823..82e29e0538 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.contacts.RecipientsEditor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; @@ -66,12 +67,11 @@ import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter; import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -95,8 +95,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity private final static String TAG = GroupCreateActivity.class.getSimpleName(); - public static final String GROUP_RECIPIENT_EXTRA = "group_recipient"; - public static final String GROUP_THREAD_EXTRA = "group_thread"; + public static final String GROUP_ADDRESS_EXTRA = "group_recipient"; + public static final String GROUP_THREAD_EXTRA = "group_thread"; private final DynamicTheme dynamicTheme = new DynamicTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -174,9 +174,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity private static boolean isActiveInDirectory(Context context, Recipient recipient) { try { - return TextSecureDirectory.getInstance(context) - .isSecureTextSupported(Util.canonicalizeNumber(context, recipient.getNumber())); - } catch (NotInDirectoryException | InvalidNumberException e) { + return TextSecureDirectory.getInstance(context).isSecureTextSupported(recipient.getAddress()); + } catch (NotInDirectoryException e) { return false; } } @@ -213,11 +212,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity } private void initializeExistingGroup() { - final String encodedGroupId = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(GROUP_RECIPIENT_EXTRA, -1), true) - .getNumber(); + final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA); + byte[] groupId; + try { - groupId = GroupUtil.getDecodedId(encodedGroupId); + if (groupAddress != null) groupId = GroupUtil.getDecodedId(groupAddress.toGroupString()); + else groupId = null; } catch (IOException ioe) { Log.w(TAG, "Couldn't decode the encoded groupId passed in via intent", ioe); groupId = null; @@ -286,7 +287,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.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); startActivity(intent); finish(); } @@ -310,9 +311,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity switch (reqCode) { case PICK_CONTACT: List selected = data.getStringArrayListExtra("contacts"); + for (String contact : selected) { - final Recipient recipient = RecipientFactory.getRecipientsFromString(this, contact, false).getPrimaryRecipient(); - if (recipient != null) addSelectedContacts(recipient); + Address address = Address.fromExternal(this, contact); + Recipient recipient = RecipientFactory.getRecipientFor(this, address, false); + + addSelectedContacts(recipient); } break; @@ -470,7 +474,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity if (!activity.isFinishing()) { Intent intent = activity.getIntent(); intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId()); - intent.putExtra(GROUP_RECIPIENT_EXTRA, result.get().getGroupRecipient().getIds()); + intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getPrimaryRecipient().getAddress()); activity.setResult(RESULT_OK, intent); activity.finish(); } @@ -508,16 +512,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity final List results = new LinkedList<>(); for (Recipient recipient : recipients) { - boolean isPush = isActiveInDirectory(activity, recipient); - String recipientE164 = null; - try { - recipientE164 = Util.canonicalizeNumber(activity, recipient.getNumber()); - } catch (InvalidNumberException ine) { /* do nothing */ } + boolean isPush = isActiveInDirectory(activity, recipient); if (failIfNotPush && !isPush) { results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group, - recipient.getNumber()))); - } else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipientE164)) { + recipient.toShortString()))); + } else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().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)); diff --git a/src/org/thoughtcrime/securesms/GroupMembersDialog.java b/src/org/thoughtcrime/securesms/GroupMembersDialog.java index be6859d617..8c90e3981a 100644 --- a/src/org/thoughtcrime/securesms/GroupMembersDialog.java +++ b/src/org/thoughtcrime/securesms/GroupMembersDialog.java @@ -14,9 +14,7 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.util.LinkedList; @@ -40,7 +38,7 @@ public class GroupMembersDialog extends AsyncTask { @Override protected Recipients doInBackground(Void... params) { try { - String groupId = recipients.getPrimaryRecipient().getNumber(); + String groupId = recipients.getPrimaryRecipient().getAddress().toGroupString(); return DatabaseFactory.getGroupDatabase(context) .getGroupMembers(GroupUtil.getDecodedId(groupId), true); } catch (IOException e) { @@ -85,7 +83,11 @@ public class GroupMembersDialog extends AsyncTask { ContactsContract.QuickContact.MODE_LARGE, null); } else { final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getNumber()); + if (recipient.getAddress().isEmail()) { + intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString()); + } else { + intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString()); + } intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); context.startActivity(intent); } @@ -134,15 +136,7 @@ public class GroupMembersDialog extends AsyncTask { } private boolean isLocalNumber(Recipient recipient) { - try { - String localNumber = TextSecurePreferences.getLocalNumber(context); - String e164Number = Util.canonicalizeNumber(context, recipient.getNumber()); - - return e164Number != null && e164Number.equals(localNumber); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - return false; - } + return Util.isOwnNumber(context, recipient.getAddress()); } } } diff --git a/src/org/thoughtcrime/securesms/InviteActivity.java b/src/org/thoughtcrime/securesms/InviteActivity.java index c54badf51f..d0b4beded5 100644 --- a/src/org/thoughtcrime/securesms/InviteActivity.java +++ b/src/org/thoughtcrime/securesms/InviteActivity.java @@ -27,6 +27,7 @@ import android.widget.Toast; import org.thoughtcrime.securesms.components.ContactFilterToolbar; import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -230,10 +231,10 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen if (context == null) return null; for (String number : numbers) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, number)}, false); if (recipients.getPrimaryRecipient() != null) { - Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds()); + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getAddresses()); int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message, subscriptionId), -1L, true, null); diff --git a/src/org/thoughtcrime/securesms/MediaAdapter.java b/src/org/thoughtcrime/securesms/MediaAdapter.java index 83769b1319..b18d561619 100644 --- a/src/org/thoughtcrime/securesms/MediaAdapter.java +++ b/src/org/thoughtcrime/securesms/MediaAdapter.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.database.Cursor; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; -import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -33,8 +32,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.MediaUtil; public class MediaAdapter extends CursorRecyclerViewAdapter { @@ -93,14 +90,10 @@ public class MediaAdapter extends CursorRecyclerViewAdapter { intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize()); intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, threadId); - if (!TextUtils.isEmpty(mediaRecord.getAddress())) { - Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), - mediaRecord.getAddress(), - true); - if (recipients != null && recipients.getPrimaryRecipient() != null) { - intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId()); - } + if (mediaRecord.getAddress() != null) { + intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, mediaRecord.getAddress()); } + intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType()); getContext().startActivity(intent); } diff --git a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java index 2552e08c16..8673fbb616 100644 --- a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -38,12 +38,12 @@ import android.view.WindowManager; import android.widget.TextView; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientFactory; +import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.AbstractCursorLoader; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.SaveAttachmentTask; @@ -58,7 +58,7 @@ import java.util.List; public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks { private final static String TAG = MediaOverviewActivity.class.getSimpleName(); - public static final String RECIPIENT_EXTRA = "recipient"; + public static final String ADDRESSES_EXTRA = "addresses"; public static final String THREAD_ID_EXTRA = "thread_id"; private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -68,7 +68,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i private RecyclerView gridView; private GridLayoutManager gridManager; private TextView noImages; - private Recipient recipient; + private Recipients recipients; private long threadId; @Override @@ -113,9 +113,9 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i } private void initializeActionBar() { - getSupportActionBar().setTitle(recipient == null + getSupportActionBar().setTitle(recipients == null ? getString(R.string.AndroidManifest__all_media) - : getString(R.string.AndroidManifest__all_media_named, recipient.toShortString())); + : getString(R.string.AndroidManifest__all_media_named, recipients.toShortString())); } @Override @@ -132,19 +132,20 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i gridView.setLayoutManager(gridManager); gridView.setHasFixedSize(true); - final long recipientId = getIntent().getLongExtra(RECIPIENT_EXTRA, -1); - if (recipientId > -1) { - recipient = RecipientFactory.getRecipientForId(this, recipientId, true); - } else if (threadId > -1){ - recipient = DatabaseFactory.getThreadDatabase(this).getRecipientsForThreadId(threadId).getPrimaryRecipient(); + Address[] addresses = Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)); + + if (addresses != null) { + recipients = RecipientFactory.getRecipientsFor(this, addresses, true); + } else if (threadId > -1) { + recipients = DatabaseFactory.getThreadDatabase(this).getRecipientsForThreadId(threadId); } else { - recipient = null; + recipients = null; } - if (recipient != null) { - recipient.addListener(new RecipientModifiedListener() { + if (recipients != null) { + recipients.addListener(new Recipients.RecipientsModifiedListener() { @Override - public void onModified(Recipient recipient) { + public void onModified(Recipients recipients) { initializeActionBar(); } }); diff --git a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java index f56673fb92..be35e09c7e 100644 --- a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -34,6 +34,7 @@ import android.widget.Toast; import org.thoughtcrime.securesms.components.ZoomingImageView; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; @@ -52,7 +53,7 @@ import java.io.IOException; public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener { private final static String TAG = MediaPreviewActivity.class.getSimpleName(); - public static final String RECIPIENT_EXTRA = "recipient"; + public static final String ADDRESS_EXTRA = "address"; public static final String THREAD_ID_EXTRA = "thread_id"; public static final String DATE_EXTRA = "date"; public static final String SIZE_EXTRA = "size"; @@ -143,7 +144,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im } private void initializeResources() { - final long recipientId = getIntent().getLongExtra(RECIPIENT_EXTRA, -1); + Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA); mediaUri = getIntent().getData(); mediaType = getIntent().getType(); @@ -151,8 +152,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im size = getIntent().getLongExtra(SIZE_EXTRA, 0); threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); - if (recipientId > -1) { - recipient = RecipientFactory.getRecipientForId(this, recipientId, true); + if (address != null) { + recipient = RecipientFactory.getRecipientFor(this, address, true); recipient.addListener(this); } else { recipient = null; diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java index 8e96c42463..b518ab9f0d 100644 --- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -36,6 +36,7 @@ import android.widget.TextView; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; @@ -73,7 +74,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity 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 RECIPIENTS_IDS_EXTRA = "recipients_ids"; + public final static String ADDRESSES_EXTRA = "addresses"; private MasterSecret masterSecret; private long threadId; @@ -138,7 +139,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity private void initializeActionBar() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); - Recipients recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getLongArrayExtra(RECIPIENTS_IDS_EXTRA), true); + Recipients recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); recipients.addListener(this); setActionBarColor(recipients.getColor()); @@ -355,9 +356,9 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity recipients = intermediaryRecipients; } else { try { - String groupId = intermediaryRecipients.getPrimaryRecipient().getNumber(); + Address groupId = intermediaryRecipients.getPrimaryRecipient().getAddress(); recipients = DatabaseFactory.getGroupDatabase(context) - .getGroupMembers(GroupUtil.getDecodedId(groupId), false); + .getGroupMembers(GroupUtil.getDecodedId(groupId.toGroupString()), false); } catch (IOException e) { Log.w(TAG, e); recipients = RecipientFactory.getRecipientsFor(MessageDetailsActivity.this, new LinkedList(), false); diff --git a/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java b/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java index a908c8295b..a0e39aa0e7 100644 --- a/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java +++ b/src/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java @@ -11,6 +11,10 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.util.Conversions; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener { @@ -43,7 +47,11 @@ public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsLi @Override public long getItemId(int position) { - return recipients.getRecipientsList().get(position).getRecipientId(); + try { + return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(recipients.getRecipientsList().get(position).getAddress().serialize().getBytes())); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } } @Override diff --git a/src/org/thoughtcrime/securesms/MessageRecipientListItem.java b/src/org/thoughtcrime/securesms/MessageRecipientListItem.java index c6b80dfcf2..8d2d5715d7 100644 --- a/src/org/thoughtcrime/securesms/MessageRecipientListItem.java +++ b/src/org/thoughtcrime/securesms/MessageRecipientListItem.java @@ -137,7 +137,7 @@ public class MessageRecipientListItem extends RelativeLayout private NetworkFailure getNetworkFailure(final MessageRecord record) { if (record.hasNetworkFailures()) { for (final NetworkFailure failure : record.getNetworkFailures()) { - if (failure.getRecipientId() == recipient.getRecipientId()) { + if (failure.getAddress().equals(recipient.getAddress())) { return failure; } } @@ -148,7 +148,7 @@ public class MessageRecipientListItem extends RelativeLayout private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) { if (record.isIdentityMismatchFailure()) { for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) { - if (mismatch.getRecipientId() == recipient.getRecipientId()) { + if (mismatch.getAddress().equals(recipient.getAddress())) { return mismatch; } } @@ -188,7 +188,7 @@ public class MessageRecipientListItem extends RelativeLayout mmsDatabase.removeFailure(record.getId(), failure); if (record.getRecipients().isGroupRecipient()) { - MessageSender.resendGroupMessage(getContext(), masterSecret, record, failure.getRecipientId()); + MessageSender.resendGroupMessage(getContext(), masterSecret, record, failure.getAddress()); } else { MessageSender.resend(getContext(), masterSecret, record); } diff --git a/src/org/thoughtcrime/securesms/NewConversationActivity.java b/src/org/thoughtcrime/securesms/NewConversationActivity.java index f9c72c615f..2d43a2631d 100644 --- a/src/org/thoughtcrime/securesms/NewConversationActivity.java +++ b/src/org/thoughtcrime/securesms/NewConversationActivity.java @@ -25,6 +25,7 @@ import android.view.MenuItem; import android.view.View; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -49,10 +50,10 @@ public class NewConversationActivity extends ContactSelectionActivity { @Override public void onContactSelected(String number) { - Recipients recipients = RecipientFactory.getRecipientsFromString(this, number, true); + Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, number)}, true); Intent intent = new Intent(this, ConversationActivity.class); - intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)); intent.setDataAndType(getIntent().getData(), getIntent().getType()); diff --git a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java index 7b844b0f0b..849891afb7 100644 --- a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java +++ b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java @@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.color.MaterialColors; import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase; @@ -48,7 +49,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.util.guava.Optional; import java.util.concurrent.ExecutionException; @@ -57,7 +57,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi { private static final String TAG = RecipientPreferenceActivity.class.getSimpleName(); - public static final String RECIPIENTS_EXTRA = "recipient_ids"; + public static final String ADDRESSES_EXTRA = "recipient_addresses"; public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number"; private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; @@ -86,8 +86,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi public void onCreate(Bundle instanceState, @NonNull MasterSecret masterSecret) { setContentView(R.layout.recipient_preference_activity); - long[] recipientIds = getIntent().getLongArrayExtra(RECIPIENTS_EXTRA); - Recipients recipients = RecipientFactory.getRecipientsForIds(this, recipientIds, true); + Address[] addresses = Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)); + Recipients recipients = RecipientFactory.getRecipientsFor(this, addresses, true); initializeToolbar(); initializeReceivers(); @@ -95,7 +95,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi recipients.addListener(this); Bundle bundle = new Bundle(); - bundle.putLongArray(RECIPIENTS_EXTRA, recipientIds); + bundle.putParcelableArray(ADDRESSES_EXTRA, addresses); initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), masterSecret, null, bundle); } @@ -149,7 +149,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi this.staleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - Recipients recipients = RecipientFactory.getRecipientsForIds(context, getIntent().getLongArrayExtra(RECIPIENTS_EXTRA), true); + Recipients recipients = RecipientFactory.getRecipientsFor(context, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); recipients.addListener(RecipientPreferenceActivity.this); onModified(recipients); } @@ -234,9 +234,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi } private void initializeRecipients() { - this.recipients = RecipientFactory.getRecipientsForIds(getActivity(), - getArguments().getLongArray(RECIPIENTS_EXTRA), - true); + this.recipients = RecipientFactory.getRecipientsFor(getActivity(), + Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)), + true); this.recipients.addListener(this); @@ -244,7 +244,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi @Override public void onReceive(Context context, Intent intent) { recipients.removeListener(RecipientPreferenceFragment.this); - recipients = RecipientFactory.getRecipientsForIds(getActivity(), getArguments().getLongArray(RECIPIENTS_EXTRA), true); + recipients = RecipientFactory.getRecipientsFor(getActivity(), Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)), true); onModified(recipients); } }; @@ -411,7 +411,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi { ApplicationContext.getInstance(context) .getJobManager() - .add(new MultiDeviceContactUpdateJob(context, recipients.getPrimaryRecipient().getRecipientId())); + .add(new MultiDeviceContactUpdateJob(context, recipients.getPrimaryRecipient().getAddress())); } return null; } @@ -471,7 +471,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi @Override public boolean onPreferenceClick(Preference preference) { Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class); - verifyIdentityIntent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, recipients.getPrimaryRecipient().getRecipientId()); + verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress()); verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey())); verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); startActivity(verifyIdentityIntent); diff --git a/src/org/thoughtcrime/securesms/ShareActivity.java b/src/org/thoughtcrime/securesms/ShareActivity.java index 0b8d2517cc..29466bd2de 100644 --- a/src/org/thoughtcrime/securesms/ShareActivity.java +++ b/src/org/thoughtcrime/securesms/ShareActivity.java @@ -23,6 +23,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Parcel; import android.os.Process; import android.provider.OpenableColumns; import android.support.annotation.NonNull; @@ -35,9 +36,9 @@ import android.view.View; import android.view.ViewGroup; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.providers.PersistentBlobProvider; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; @@ -59,9 +60,9 @@ 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_RECIPIENT_IDS = "recipient_ids"; - public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type"; + public static final String EXTRA_THREAD_ID = "thread_id"; + public static final String EXTRA_ADDRESSES_MARSHALLED = "addresses"; + public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type"; private final DynamicTheme dynamicTheme = new DynamicTheme (); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -93,6 +94,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity @Override protected void onNewIntent(Intent intent) { + Log.w(TAG, "onNewIntent()"); super.onNewIntent(intent); setIntent(intent); initializeMedia(); @@ -100,6 +102,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity @Override public void onResume() { + Log.w(TAG, "onResume()"); super.onResume(); dynamicTheme.onResume(this); dynamicLanguage.onResume(this); @@ -163,15 +166,24 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity @Override public void onCreateConversation(long threadId, Recipients recipients, int distributionType) { - createConversation(threadId, recipients, distributionType); + createConversation(threadId, recipients.getAddresses(), distributionType); } private void handleResolvedMedia(Intent intent, boolean animate) { - long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1); - long[] recipientIds = intent.getLongArrayExtra(EXTRA_RECIPIENT_IDS); - int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1); + long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1); + int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1); + Address[] addresses = null; - boolean hasResolvedDestination = threadId != -1 && recipientIds != null && distributionType != -1; + if (intent.hasExtra(EXTRA_ADDRESSES_MARSHALLED)) { + Parcel parcel = Parcel.obtain(); + byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESSES_MARSHALLED); + parcel.unmarshall(marshalled, 0, marshalled.length); + parcel.setDataPosition(0); + addresses = parcel.createTypedArray(Address.CREATOR); + parcel.recycle(); + } + + boolean hasResolvedDestination = threadId != -1 && addresses != null && distributionType != -1; if (!hasResolvedDestination && animate) { ViewUtil.fadeIn(fragmentContainer, 300); @@ -180,13 +192,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity fragmentContainer.setVisibility(View.VISIBLE); progressWheel.setVisibility(View.GONE); } else { - createConversation(threadId, RecipientFactory.getRecipientsForIds(this, recipientIds, true), distributionType); + createConversation(threadId, addresses, distributionType); } } - private void createConversation(long threadId, Recipients recipients, int distributionType) { + private void createConversation(long threadId, Address[] addresses, int distributionType) { final Intent intent = getBaseShareIntent(ConversationActivity.class); - intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, addresses); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); diff --git a/src/org/thoughtcrime/securesms/SmsSendtoActivity.java b/src/org/thoughtcrime/securesms/SmsSendtoActivity.java index 55bc8f5179..68fbcbd010 100644 --- a/src/org/thoughtcrime/securesms/SmsSendtoActivity.java +++ b/src/org/thoughtcrime/securesms/SmsSendtoActivity.java @@ -6,9 +6,11 @@ import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract; import android.support.annotation.NonNull; +import android.text.TextUtils; import android.util.Log; import android.widget.Toast; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; @@ -38,19 +40,20 @@ public class SmsSendtoActivity extends Activity { destination = getDestinationForView(original); } - Recipients recipients = RecipientFactory.getRecipientsFromString(this, destination.getDestination(), true); - long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); - final Intent nextIntent; - if (recipients.isEmpty()) { + + if (TextUtils.isEmpty(destination.destination)) { nextIntent = new Intent(this, NewConversationActivity.class); nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody()); Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show(); } else { + Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, destination.getDestination())}, true); + long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); + nextIntent = new Intent(this, ConversationActivity.class); nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody()); nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); - nextIntent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); + nextIntent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); } return nextIntent; } diff --git a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java index f13bf30871..f367d57518 100644 --- a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java +++ b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java @@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; +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; @@ -84,7 +85,6 @@ import org.whispersystems.libsignal.fingerprint.Fingerprint; import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; @@ -100,9 +100,9 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity private static final String TAG = VerifyIdentityActivity.class.getSimpleName(); - public static final String RECIPIENT_ID_EXTRA = "recipient_id"; - public static final String IDENTITY_EXTRA = "recipient_identity"; - public static final String VERIFIED_EXTRA = "verified_state"; + public static final String ADDRESS_EXTRA = "address"; + 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(); @@ -118,31 +118,26 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity @Override protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) { - try { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number); - Recipient recipient = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(RECIPIENT_ID_EXTRA, -1), true); - recipient.addListener(this); + Recipient recipient = RecipientFactory.getRecipientFor(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true); + recipient.addListener(this); - setActionBarNotificationBarColor(recipient.getColor()); + setActionBarNotificationBarColor(recipient.getColor()); - Bundle extras = new Bundle(); - extras.putLong(VerifyDisplayFragment.REMOTE_RECIPIENT_ID, getIntent().getLongExtra(RECIPIENT_ID_EXTRA, -1)); - extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA)); - extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, Util.canonicalizeNumber(this, recipient.getNumber())); - 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)); + Bundle extras = new Bundle(); + extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_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)); - scanFragment.setScanListener(this); - displayFragment.setClickListener(this); + scanFragment.setScanListener(this); + displayFragment.setClickListener(this); - initFragment(android.R.id.content, displayFragment, masterSecret, dynamicLanguage.getCurrentLocale(), extras); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - finish(); - } + initFragment(android.R.id.content, displayFragment, masterSecret, dynamicLanguage.getCurrentLocale(), extras); } @Override @@ -198,12 +193,12 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity public static class VerifyDisplayFragment extends Fragment implements Recipient.RecipientModifiedListener, CompoundButton.OnCheckedChangeListener { - public static final String REMOTE_RECIPIENT_ID = "remote_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"; + public static final String REMOTE_ADDRESS = "remote_address"; + 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 MasterSecret masterSecret; private Recipient recipient; @@ -263,12 +258,20 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity public void onCreate(Bundle bundle) { super.onCreate(bundle); + Address address = getArguments().getParcelable(REMOTE_ADDRESS); + IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY); + IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY); + + if (address == null) throw new AssertionError("Address required"); + if (localIdentityParcelable == null) throw new AssertionError("local identity required"); + if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required"); + this.masterSecret = getArguments().getParcelable("master_secret"); this.localNumber = getArguments().getString(LOCAL_NUMBER); - this.localIdentity = ((IdentityKeyParcelable)getArguments().getParcelable(LOCAL_IDENTITY)).get(); + this.localIdentity = localIdentityParcelable.get(); this.remoteNumber = getArguments().getString(REMOTE_NUMBER); - this.recipient = RecipientFactory.getRecipientForId(getActivity(), getArguments().getLong(REMOTE_RECIPIENT_ID), true); - this.remoteIdentity = ((IdentityKeyParcelable)getArguments().getParcelable(REMOTE_IDENTITY)).get(); + this.recipient = RecipientFactory.getRecipientFor(getActivity(), address, true); + this.remoteIdentity = remoteIdentityParcelable.get(); this.recipient.addListener(this); @@ -589,15 +592,15 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity protected Void doInBackground(Recipient... params) { synchronized (SESSION_LOCK) { if (isChecked) { - Log.w(TAG, "Saving identity: " + params[0].getRecipientId()); + Log.w(TAG, "Saving identity: " + params[0].getAddress()); DatabaseFactory.getIdentityDatabase(getActivity()) - .saveIdentity(params[0].getRecipientId(), + .saveIdentity(params[0].getAddress(), remoteIdentity, VerifiedStatus.VERIFIED, false, System.currentTimeMillis(), true); } else { DatabaseFactory.getIdentityDatabase(getActivity()) - .setVerified(params[0].getRecipientId(), + .setVerified(params[0].getAddress(), remoteIdentity, VerifiedStatus.DEFAULT); } @@ -605,7 +608,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity ApplicationContext.getInstance(getActivity()) .getJobManager() .add(new MultiDeviceVerifiedUpdateJob(getActivity(), - recipient.getNumber(), + recipient.getAddress(), remoteIdentity, isChecked ? VerifiedStatus.VERIFIED : VerifiedStatus.DEFAULT)); diff --git a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java index 55d0bb7189..0546205c9a 100644 --- a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -258,11 +258,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.getNumber(), 1), theirIdentity, true); + identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true); } Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, recipient.getNumber()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress()); intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL); startService(intent); } diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java index 6fc4ea17ee..230b535134 100644 --- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java +++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java @@ -63,7 +63,11 @@ public class AvatarImageView extends ImageView { ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); } else if (recipient != null) { final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getNumber()); + if (recipient.getAddress().isEmail()) { + intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString()); + } else { + intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString()); + } intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); getContext().startActivity(intent); } diff --git a/src/org/thoughtcrime/securesms/components/PushRecipientsPanel.java b/src/org/thoughtcrime/securesms/components/PushRecipientsPanel.java index 30ff3dbd9e..c49c16d320 100644 --- a/src/org/thoughtcrime/securesms/components/PushRecipientsPanel.java +++ b/src/org/thoughtcrime/securesms/components/PushRecipientsPanel.java @@ -18,31 +18,28 @@ package org.thoughtcrime.securesms.components; import android.content.Context; import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; -import android.widget.AutoCompleteTextView; import android.widget.RelativeLayout; -import android.widget.TextView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.ContactAccessor; 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.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Set; +import java.util.StringTokenizer; /** * Panel component combining both an editable field with a button for @@ -74,26 +71,11 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod initialize(); } - public void addRecipient(String name, String number) { - if (name != null) recipientsText.append(name + "< " + number + ">, "); - else recipientsText.append(number + ", "); - } - - public void addRecipients(Recipients recipients) { - List recipientList = recipients.getRecipientsList(); - Iterator iterator = recipientList.iterator(); - - while (iterator.hasNext()) { - Recipient recipient = iterator.next(); - addRecipient(recipient.getName(), recipient.getNumber()); - } - } - public Recipients getRecipients() throws RecipientFormattingException { - String rawText = recipientsText.getText().toString(); - Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, true); + String rawText = recipientsText.getText().toString(); + Recipients recipients = getRecipientsFromString(getContext(), rawText, true); - if (recipients.isEmpty()) + if (recipients == null || recipients.isEmpty()) throw new RecipientFormattingException("Recipient List Is Empty!"); return recipients; @@ -151,7 +133,40 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod }); } - @Override public void onModified(Recipients recipients) { + private @Nullable Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) { + StringTokenizer tokenizer = new StringTokenizer(rawText, ","); + List
addresses = new LinkedList<>(); + + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken().trim(); + + if (!TextUtils.isEmpty(token)) { + if (hasBracketedNumber(token)) addresses.add(Address.fromExternal(context, parseBracketedNumber(token))); + else addresses.add(Address.fromExternal(context, token)); + } + } + + if (addresses.size() == 0) return null; + else return RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), asynchronous); + } + + private boolean hasBracketedNumber(String recipient) { + int openBracketIndex = recipient.indexOf('<'); + + return (openBracketIndex != -1) && + (recipient.indexOf('>', openBracketIndex) != -1); + } + + private String parseBracketedNumber(String recipient) { + int begin = recipient.indexOf('<'); + int end = recipient.indexOf('>', begin); + String value = recipient.substring(begin + 1, end); + + return value; + } + + @Override + public void onModified(Recipients recipients) { recipientsText.populate(recipients); } diff --git a/src/org/thoughtcrime/securesms/components/SingleRecipientPanel.java b/src/org/thoughtcrime/securesms/components/SingleRecipientPanel.java deleted file mode 100644 index 1685ad75c3..0000000000 --- a/src/org/thoughtcrime/securesms/components/SingleRecipientPanel.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.components; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AdapterView; -import android.widget.RelativeLayout; - -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.ContactAccessor; -import org.thoughtcrime.securesms.contacts.RecipientsAdapter; -import org.thoughtcrime.securesms.contacts.RecipientsEditor; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.RecipientFormattingException; -import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -/** - * Panel component combining both an editable field with a button for - * a list-based contact selector. - * - * @author Moxie Marlinspike - */ -public class SingleRecipientPanel extends RelativeLayout implements RecipientsModifiedListener { - private final String TAG = SingleRecipientPanel.class.getSimpleName(); - private RecipientsPanelChangedListener panelChangeListener; - - private RecipientsEditor recipientsText; - private View panel; - - private static final int RECIPIENTS_MAX_LENGTH = 312; - - public SingleRecipientPanel(Context context) { - super(context); - initialize(); - } - - public SingleRecipientPanel(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(); - } - - public SingleRecipientPanel(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initialize(); - } - - public void addRecipient(String name, String number) { - Log.i(TAG, "addRecipient for " + name + "/" + number); - if (name != null) recipientsText.append(name + "< " + number + ">, "); - else recipientsText.append(number + ", "); - } - - public void addRecipients(Recipients recipients) { - List recipientList = recipients.getRecipientsList(); - Iterator iterator = recipientList.iterator(); - - while (iterator.hasNext()) { - Recipient recipient = iterator.next(); - addRecipient(recipient.getName(), recipient.getNumber()); - } - } - - public void addContacts(List contacts) { - for (ContactAccessor.ContactData contact : contacts) { - for (ContactAccessor.NumberData number : contact.numbers) { - addRecipient(contact.name, number.number); - } - } - } - - public Recipients getRecipients() throws RecipientFormattingException { - String rawText = recipientsText.getText().toString(); - Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, true); - - if (recipients.isEmpty()) - throw new RecipientFormattingException("Recipient List Is Empty!"); - - return recipients; - } - - public void disable() { - clear(); - panel.setVisibility(View.GONE); - } - - public void clear() { - recipientsText.setText(""); - } - - public void setPanelChangeListener(RecipientsPanelChangedListener panelChangeListener) { - this.panelChangeListener = panelChangeListener; - } - - private void initialize() { - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.single_recipient_panel, this, true); - - panel = findViewById(R.id.recipients_panel); - initRecipientsEditor(); - } - - private void initRecipientsEditor() { - Recipients recipients; - recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text); - - try { - recipients = getRecipients(); - } catch (RecipientFormattingException e) { - recipients = RecipientFactory.getRecipientsFor(getContext(), new LinkedList(), true); - } - recipients.addListener(this); - - recipientsText.setAdapter(new RecipientsAdapter(this.getContext())); - recipientsText.populate(recipients); - - recipientsText.setOnFocusChangeListener(new FocusChangedListener()); - recipientsText.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if (panelChangeListener != null) { - try { - panelChangeListener.onRecipientsPanelUpdate(getRecipients()); - } catch (RecipientFormattingException rfe) { - panelChangeListener.onRecipientsPanelUpdate(null); - } - } - recipientsText.setText(""); - } - }); - } - - @Override public void onModified(Recipients recipients) { - recipientsText.populate(recipients); - } - - private class FocusChangedListener implements OnFocusChangeListener { - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus && (panelChangeListener != null)) { - try { - panelChangeListener.onRecipientsPanelUpdate(getRecipients()); - } catch (RecipientFormattingException rfe) { - panelChangeListener.onRecipientsPanelUpdate(null); - } - } - } - } - - public interface RecipientsPanelChangedListener { - public void onRecipientsPanelUpdate(Recipients recipients); - } - -} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java b/src/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java index 0bd6268a4b..45ac6472ba 100644 --- a/src/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java +++ b/src/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java @@ -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.getRecipientId(), true); + identityDatabase.setApproval(identityRecord.getAddress(), true); } } diff --git a/src/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java b/src/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java index bf1e0cf300..9bf6e3cc27 100644 --- a/src/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java +++ b/src/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java @@ -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.getRecipientId(), + identityDatabase.setVerified(identityRecord.getAddress(), identityRecord.getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT); } diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java index 35db803b2b..5de9769da7 100644 --- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java +++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java @@ -120,7 +120,7 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient 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.getRecipientId(), untrustedIdentity), + spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity), introduction.length()+1, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -302,7 +302,7 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient }.execute(); this.name.setText(recipient.getName()); - this.phoneNumber.setText(recipient.getNumber()); + this.phoneNumber.setText(recipient.getAddress().serialize()); } private void setCard(Recipient recipient, String status) { diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java index 0790bb6402..34ab6014de 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java @@ -10,9 +10,10 @@ import android.widget.TextView; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.AvatarImageView; -import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.recipients.RecipientsFormatter; import org.thoughtcrime.securesms.util.ViewUtil; public class ContactSelectionListItem extends LinearLayout implements Recipients.RecipientsModifiedListener { @@ -53,9 +54,10 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients if (type == ContactsDatabase.NEW_TYPE) { this.recipients = null; - this.contactPhotoImage.setAvatar(Recipient.getUnknownRecipient(), false); + this.contactPhotoImage.setAvatar(RecipientFactory.getRecipientFor(getContext(), Address.UNKNOWN, true), false); } else if (!TextUtils.isEmpty(number)) { - this.recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true); + Address address = Address.fromExternal(getContext(), number); + this.recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[] {address}, true); if (this.recipients.getPrimaryRecipient() != null && this.recipients.getPrimaryRecipient().getName() != null) diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java b/src/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java index 4554cb3ede..59a5f8ada4 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java @@ -27,6 +27,7 @@ import android.text.TextUtils; import android.util.Log; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; @@ -102,8 +103,8 @@ public class ContactsCursorLoader extends CursorLoader { ContactsDatabase.LABEL_COLUMN, ContactsDatabase.CONTACT_TYPE_COLUMN}); while (cursor.moveToNext()) { - final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); - final Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true); + final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); + final Recipients recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[]{Address.fromExternal(getContext(), number)}, true); if (DirectoryHelper.getUserCapabilities(getContext(), recipients) .getTextCapability() != Capability.SUPPORTED) diff --git a/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java b/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java index deea9f924c..23c6c39f50 100644 --- a/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java +++ b/src/org/thoughtcrime/securesms/contacts/RecipientsEditor.java @@ -128,9 +128,9 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView { return mTokenizer.getNumbers(); } - public Recipients constructContactsFromInput() { - return RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false); - } +// public Recipients constructContactsFromInput() { +// return RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false); +// } private boolean isValidAddress(String number, boolean isMms) { /*if (isMms) { @@ -191,7 +191,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView { public static CharSequence contactToToken(Recipient c) { String name = c.getName(); - String number = c.getNumber(); + String number = c.getAddress().serialize(); SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number)); int len = s.length(); @@ -199,7 +199,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView { return s; } - s.setSpan(new Annotation("number", c.getNumber()), 0, len, + s.setSpan(new Annotation("number", c.getAddress().serialize()), 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return s; diff --git a/src/org/thoughtcrime/securesms/crypto/SessionUtil.java b/src/org/thoughtcrime/securesms/crypto/SessionUtil.java index 86fbc4565a..8d4a616492 100644 --- a/src/org/thoughtcrime/securesms/crypto/SessionUtil.java +++ b/src/org/thoughtcrime/securesms/crypto/SessionUtil.java @@ -4,6 +4,7 @@ import android.content.Context; import android.support.annotation.NonNull; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.recipients.Recipient; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.SessionRecord; @@ -15,12 +16,12 @@ import java.util.List; public class SessionUtil { public static boolean hasSession(Context context, MasterSecret masterSecret, Recipient recipient) { - return hasSession(context, masterSecret, recipient.getNumber()); + return hasSession(context, masterSecret, recipient.getAddress()); } - public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull String number) { + public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull Address address) { SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); - SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(number, SignalServiceAddress.DEFAULT_DEVICE_ID); + SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID); return sessionStore.containsSession(axolotlAddress); } diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java index 68f1dbfa9f..1279349b64 100644 --- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java +++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java @@ -5,12 +5,12 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.database.Address; 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.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.libsignal.IdentityKey; @@ -47,13 +47,12 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore { public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) { synchronized (LOCK) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, address.getName(), true); - long recipientId = recipients.getPrimaryRecipient().getRecipientId(); - Optional identityRecord = identityDatabase.getIdentity(recipientId); + Address signalAddress = Address.fromExternal(context, address.getName()); + Optional identityRecord = identityDatabase.getIdentity(signalAddress); if (!identityRecord.isPresent()) { Log.w(TAG, "Saving new identity..."); - identityDatabase.saveIdentity(recipientId, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval); + identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval); return false; } @@ -69,15 +68,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore { verifiedStatus = VerifiedStatus.DEFAULT; } - identityDatabase.saveIdentity(recipientId, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval); - IdentityUtil.markIdentityUpdate(context, recipients.getPrimaryRecipient()); + identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval); + IdentityUtil.markIdentityUpdate(context, RecipientFactory.getRecipientFor(context, signalAddress, true)); SessionUtil.archiveSiblingSessions(context, address); return true; } if (isNonBlockingApprovalRequired(identityRecord.get())) { Log.w(TAG, "Setting approval status..."); - identityDatabase.setApproval(recipientId, nonBlockingApproval); + identityDatabase.setApproval(signalAddress, nonBlockingApproval); return false; } @@ -94,16 +93,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore { public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { synchronized (LOCK) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - long recipientId = RecipientFactory.getRecipientsFromString(context, address.getName(), true).getPrimaryRecipient().getRecipientId(); String ourNumber = TextSecurePreferences.getLocalNumber(context); - long ourRecipientId = RecipientFactory.getRecipientsFromString(context, ourNumber, true).getPrimaryRecipient().getRecipientId(); + Address theirAddress = Address.fromExternal(context, address.getName()); - if (ourRecipientId == recipientId || ourNumber.equals(address.getName())) { + if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) { return identityKey.equals(IdentityKeyUtil.getIdentityKey(context)); } switch (direction) { - case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(recipientId)); + case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress)); case RECEIVING: return true; default: throw new AssertionError("Unknown direction: " + direction); } diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index 93eb7a037b..389869e3c2 100644 --- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -7,11 +7,9 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.util.Conversions; -import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SessionState; @@ -135,7 +133,6 @@ public class TextSecureSessionStore implements SessionStore { @Override public List getSubDeviceSessions(String name) { - long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId(); List results = new LinkedList<>(); File parent = getSessionDirectory(); String[] children = parent.list(); @@ -144,10 +141,10 @@ public class TextSecureSessionStore implements SessionStore { for (String child : children) { try { - String[] parts = child.split("[.]", 2); - long sessionRecipientId = Long.parseLong(parts[0]); + String[] parts = child.split("[.]", 2); + String sessionName = parts[0]; - if (sessionRecipientId == recipientId && parts.length > 1) { + if (sessionName.equals(name) && parts.length > 1) { results.add(Integer.parseInt(parts[1])); } } catch (NumberFormatException e) { @@ -209,26 +206,21 @@ public class TextSecureSessionStore implements SessionStore { return directory; } - private String getSessionName(SignalProtocolAddress axolotlAddress) { - Recipient recipient = RecipientFactory.getRecipientsFromString(context, axolotlAddress.getName(), true) - .getPrimaryRecipient(); - long recipientId = recipient.getRecipientId(); - int deviceId = axolotlAddress.getDeviceId(); - - return recipientId + (deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId); + private String getSessionName(SignalProtocolAddress address) { + int deviceId = address.getDeviceId(); + return address.getName() + (deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId); } private @Nullable SignalProtocolAddress getAddressName(File sessionFile) { try { - String[] parts = sessionFile.getName().split("[.]"); - Recipient recipient = RecipientFactory.getRecipientForId(context, Integer.valueOf(parts[0]), true); + String[] parts = sessionFile.getName().split("[.]"); int deviceId; if (parts.length > 1) deviceId = Integer.parseInt(parts[1]); else deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - return new SignalProtocolAddress(recipient.getNumber(), deviceId); + return new SignalProtocolAddress(parts[0], deviceId); } catch (NumberFormatException e) { Log.w(TAG, e); return null; diff --git a/src/org/thoughtcrime/securesms/database/Address.java b/src/org/thoughtcrime/securesms/database/Address.java new file mode 100644 index 0000000000..69d49a39fa --- /dev/null +++ b/src/org/thoughtcrime/securesms/database/Address.java @@ -0,0 +1,156 @@ +package org.thoughtcrime.securesms.database; + + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; + +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.NumberUtil; +import org.thoughtcrime.securesms.util.ShortCodeUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; + +import java.util.LinkedList; +import java.util.List; + +public class Address implements Parcelable, Comparable
{ + + public static final Parcelable.Creator
CREATOR = new Parcelable.Creator
() { + public Address createFromParcel(Parcel in) { + return new Address(in); + } + + public Address[] newArray(int size) { + return new Address[size]; + } + }; + + public static final Address UNKNOWN = new Address("Unknown"); + + private static final String TAG = Address.class.getSimpleName(); + + private final String address; + + private Address(@NonNull String address) { + if (address == null) throw new AssertionError(address); + this.address = address; + } + + public Address(Parcel in) { + this(in.readString()); + } + + public static Address fromSerialized(@NonNull String serialized) { + return new Address(serialized); + } + + public static List
fromSerializedList(@NonNull String serialized, @NonNull String delimiter) { + List elements = Util.split(serialized, delimiter); + List
addresses = new LinkedList<>(); + + for (String element : elements) { + addresses.add(Address.fromSerialized(element)); + } + + return addresses; + } + + public static Address fromExternal(@NonNull Context context, @Nullable String external) + { + if (external == null) return new Address("Unknown"); + + try { + String localNumber = TextSecurePreferences.getLocalNumber(context); + + if (GroupUtil.isEncodedGroup(external)) return new Address(external); + else if (NumberUtil.isValidEmail(external)) return new Address(external); + else if (ShortCodeUtil.isShortCode(localNumber, external)) return new Address(external.replaceAll("[^0-9+]", "")); + + return new Address(PhoneNumberFormatter.formatNumber(external, localNumber)); + } catch (InvalidNumberException e) { + Log.w(TAG, e); + if (TextUtils.isEmpty(external.trim())) return new Address("Unknown"); + else return new Address(external.trim()); + } + } + + public static Address[] fromParcelable(Parcelable[] parcelables) { + Address[] addresses = new Address[parcelables.length]; + + for (int i=0;i. - */ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDatabase.CursorFactory; -import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; -import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; -import android.util.Log; - -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.LRUCache; -import org.thoughtcrime.securesms.util.ShortCodeUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class CanonicalAddressDatabase { - - private static final String TAG = CanonicalAddressDatabase.class.getSimpleName(); - - private static final int DATABASE_VERSION = 1; - private static final String DATABASE_NAME = "canonical_address.db"; - private static final String TABLE = "canonical_addresses"; - private static final String ID_COLUMN = "_id"; - private static final String ADDRESS_COLUMN = "address"; - - private static final String DATABASE_CREATE = "CREATE TABLE " + TABLE + " (" + ID_COLUMN + " integer PRIMARY KEY, " + ADDRESS_COLUMN + " TEXT NOT NULL);"; - private static final String SELECTION_NUMBER = "PHONE_NUMBERS_EQUAL(" + ADDRESS_COLUMN + ", ?)"; - private static final String SELECTION_OTHER = ADDRESS_COLUMN + " = ? COLLATE NOCASE"; - - private static CanonicalAddressDatabase instance; - private DatabaseHelper databaseHelper; - private final Context context; - - private final Map addressCache = new ConcurrentHashMap<>(); - private final Map idCache = new ConcurrentHashMap<>(); - private final Map formattedAddressCache = Collections.synchronizedMap(new LRUCache(100)); - - public synchronized static CanonicalAddressDatabase getInstance(Context context) { - if (instance == null) - instance = new CanonicalAddressDatabase(context.getApplicationContext()); - - return instance; - } - - private CanonicalAddressDatabase(Context context) { - this.context = context; - this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); - - fillCache(); - } - - public void reset(Context context) { - DatabaseHelper old = this.databaseHelper; - this.databaseHelper = new DatabaseHelper(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION); - old.close(); - fillCache(); - } - - private void fillCache() { - Cursor cursor = null; - - try { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - cursor = db.query(TABLE, null, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN)); - - if (address == null || address.trim().length() == 0) - address = "Anonymous"; - - idCache.put(id, address); - addressCache.put(address, id); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public @NonNull String getAddressFromId(long id) { - String cachedAddress = idCache.get(id); - - if (cachedAddress != null) - return cachedAddress; - - Cursor cursor = null; - - try { - Log.w(TAG, "Hitting DB on query [ID]."); - - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - cursor = db.query(TABLE, null, ID_COLUMN + " = ?", new String[] {id+""}, null, null, null); - - if (!cursor.moveToFirst()) - return "Anonymous"; - - String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN)); - - if (address == null || address.trim().equals("")) { - return "Anonymous"; - } else { - idCache.put(id, address); - return address; - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public void close() { - databaseHelper.close(); - instance = null; - } - - public long getCanonicalAddressId(@NonNull String address) { - try { - long canonicalAddressId; - String formattedAddress; - - if ((formattedAddress = formattedAddressCache.get(address)) == null) { - String localNumber = TextSecurePreferences.getLocalNumber(context); - - if (!isNumberAddress(address) || - !TextSecurePreferences.isPushRegistered(context) || - ShortCodeUtil.isShortCode(localNumber, address)) - { - formattedAddress = address; - } else { - formattedAddress = PhoneNumberFormatter.formatNumber(address, localNumber); - } - - formattedAddressCache.put(address, formattedAddress); - } - - if ((canonicalAddressId = getCanonicalAddressFromCache(formattedAddress)) == -1) { - canonicalAddressId = getCanonicalAddressIdFromDatabase(formattedAddress); - } - - idCache.put(canonicalAddressId, formattedAddress); - addressCache.put(formattedAddress, canonicalAddressId); - - return canonicalAddressId; - } catch (InvalidNumberException e) { - throw new AssertionError(e); - } - } - - public @NonNull List getCanonicalAddressIds(@NonNull List addresses) { - List addressList = new LinkedList<>(); - - for (String address : addresses) { - addressList.add(getCanonicalAddressId(address)); - } - - return addressList; - } - - private long getCanonicalAddressFromCache(String address) { - Long cachedAddress = addressCache.get(address); - return cachedAddress == null ? -1L : cachedAddress; - } - - private long getCanonicalAddressIdFromDatabase(@NonNull String address) { - Log.w(TAG, "Hitting DB on query [ADDRESS]"); - - Cursor cursor = null; - - try { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - String[] selectionArguments = new String[]{address}; - boolean isNumber = isNumberAddress(address); - - cursor = db.query(TABLE, null, isNumber ? SELECTION_NUMBER : SELECTION_OTHER, - selectionArguments, null, null, null); - - if (cursor.getCount() == 0 || !cursor.moveToFirst()) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(ADDRESS_COLUMN, address); - return db.insert(TABLE, ADDRESS_COLUMN, contentValues); - } else { - long canonicalId = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN)); - String oldAddress = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN)); - - if (!address.equals(oldAddress)) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(ADDRESS_COLUMN, address); - db.update(TABLE, contentValues, ID_COLUMN + " = ?", new String[]{canonicalId+""}); - - addressCache.remove(oldAddress); - } - - return canonicalId; - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - - @VisibleForTesting - static boolean isNumberAddress(@NonNull String number) { - if (number.contains("@")) return false; - if (GroupUtil.isEncodedGroup(number)) return false; - - final String networkNumber = PhoneNumberUtils.extractNetworkPortion(number); - - if (TextUtils.isEmpty(networkNumber)) return false; - if (networkNumber.length() < 3) return false; - - return PhoneNumberUtils.isWellFormedSmsAddress(number); - } - - private static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { - super(context, name, factory, version); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(DATABASE_CREATE); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - } - - } -} diff --git a/src/org/thoughtcrime/securesms/database/CanonicalSessionMigrator.java b/src/org/thoughtcrime/securesms/database/CanonicalSessionMigrator.java deleted file mode 100644 index 587ca26546..0000000000 --- a/src/org/thoughtcrime/securesms/database/CanonicalSessionMigrator.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.database; - -import android.content.Context; -import android.util.Log; - -import java.io.File; - -public class CanonicalSessionMigrator { - - private static void migrateSession(File sessionFile, File sessionsDirectory, long canonicalAddress) { - File canonicalSessionFile = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress); - sessionFile.renameTo(canonicalSessionFile); - Log.w("CanonicalSessionMigrator", "Moving: " + sessionFile.toString() + " to " + canonicalSessionFile.toString()); - - File canonicalSessionFileLocal = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress + "-local"); - File localFile = new File(sessionFile.getAbsolutePath() + "-local"); - if (localFile.exists()) - localFile.renameTo(canonicalSessionFileLocal); - - Log.w("CanonicalSessionMigrator", "Moving " + localFile + " to " + canonicalSessionFileLocal); - - File canonicalSessionFileRemote = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress + "-remote"); - File remoteFile = new File(sessionFile.getAbsolutePath() + "-remote"); - if (remoteFile.exists()) - remoteFile.renameTo(canonicalSessionFileRemote); - - Log.w("CanonicalSessionMigrator", "Moving " + remoteFile + " to " + canonicalSessionFileRemote); - - } - - public static void migrateSessions(Context context) { - if (context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).getBoolean("canonicalized", false)) - return; - - CanonicalAddressDatabase canonicalDb = CanonicalAddressDatabase.getInstance(context); - File rootDirectory = context.getFilesDir(); - File sessionsDirectory = new File(rootDirectory.getAbsolutePath() + File.separatorChar + "sessions"); - sessionsDirectory.mkdir(); - - String[] files = rootDirectory.list(); - - for (int i=0;i newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); + db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + if (cursor != null) cursor.close(); + + // Migrate MMS mismatched identities + cursor = db.query("mms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + String document = cursor.getString(1); + + if (!TextUtils.isEmpty(document)) { + try { + PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class); + List newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); + db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + if (cursor != null) cursor.close(); + + // Migrate MMS network failures + cursor = db.query("mms", new String[] {"_id", "network_failures"}, "network_failures IS NOT NULL", null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + String document = cursor.getString(1); + + if (!TextUtils.isEmpty(document)) { + try { + PreCanonicalAddressNetworkFailureList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressNetworkFailureList.class); + List newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressNetworkFailureDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressNetworkFailureDocument(numberMigrator.migrate(address))); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("network_failures", JsonUtils.toJson(new PostCanonicalAddressNetworkFailureList(newDocumentList))); + db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + // Migrate sessions + File sessionsDirectory = new File(context.getFilesDir(), "sessions-v2"); + + if (sessionsDirectory.exists() && sessionsDirectory.isDirectory()) { + File[] sessions = sessionsDirectory.listFiles(); + + for (File session : sessions) { + try { + String[] sessionParts = session.getName().split("[.]"); + long recipientId = Long.parseLong(sessionParts[0]); + + int deviceId; + + if (sessionParts.length > 1) deviceId = Integer.parseInt(sessionParts[1]); + else deviceId = 1; + + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToNext()) { + String address = resolved.getString(0); + File destination = new File(session.getParentFile(), address + (deviceId != 1 ? "." + deviceId : "")); + + if (!session.renameTo(destination)) { + Log.w(TAG, "Session rename failed: " + destination); + } + } + + if (resolved != null) resolved.close(); + } catch (NumberFormatException e) { + Log.w(TAG, e); + } + } + } + + } + db.setTransactionSuccessful(); db.endTransaction(); } @@ -893,4 +1202,153 @@ public class DatabaseFactory { } } + + private static class PreCanonicalAddressIdentityMismatchList { + @JsonProperty(value = "m") + private List list; + } + + private static class PostCanonicalAddressIdentityMismatchList { + @JsonProperty(value = "m") + private List list; + + public PostCanonicalAddressIdentityMismatchList(List list) { + this.list = list; + } + } + + private static class PreCanonicalAddressIdentityMismatchDocument { + @JsonProperty(value = "r") + private long recipientId; + + @JsonProperty(value = "k") + private String identityKey; + } + + private static class PostCanonicalAddressIdentityMismatchDocument { + @JsonProperty(value = "a") + private String address; + + @JsonProperty(value = "k") + private String identityKey; + + public PostCanonicalAddressIdentityMismatchDocument() {} + + public PostCanonicalAddressIdentityMismatchDocument(String address, String identityKey) { + this.address = address; + this.identityKey = identityKey; + } + } + + private static class PreCanonicalAddressNetworkFailureList { + @JsonProperty(value = "l") + private List list; + } + + private static class PostCanonicalAddressNetworkFailureList { + @JsonProperty(value = "l") + private List list; + + public PostCanonicalAddressNetworkFailureList(List list) { + this.list = list; + } + } + + private static class PreCanonicalAddressNetworkFailureDocument { + @JsonProperty(value = "r") + private long recipientId; + } + + private static class PostCanonicalAddressNetworkFailureDocument { + @JsonProperty(value = "a") + private String address; + + public PostCanonicalAddressNetworkFailureDocument() {} + + public PostCanonicalAddressNetworkFailureDocument(String address) { + this.address = address; + } + } + + private static class NumberMigrator { + + private static final String TAG = NumberMigrator.class.getSimpleName(); + + private static final Set SHORT_COUNTRIES = new HashSet() {{ + add("NU"); + add("TK"); + add("NC"); + add("AC"); + }}; + + private final Phonenumber.PhoneNumber localNumber; + private final String localNumberString; + private final String localCountryCode; + + private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + + public NumberMigrator(String localNumber) { + try { + this.localNumberString = localNumber; + this.localNumber = phoneNumberUtil.parse(localNumber, null); + this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(this.localNumber); + } catch (NumberParseException e) { + throw new AssertionError(e); + } + } + + public String migrate(@Nullable String number) { + if (number == null) return "Unknown"; + if (number.startsWith("__textsecure_group__!")) return number; + if (android.util.Patterns.EMAIL_ADDRESS.matcher(number).matches()) return number; + + String bareNumber = number.replaceAll("[^0-9+]", ""); + + if (bareNumber.length() == 0) { + if (TextUtils.isEmpty(number.trim())) 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; + } + + try { + Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(bareNumber, localCountryCode); + + if (ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(parsedNumber, localCountryCode)) { + return bareNumber; + } + + return phoneNumberUtil.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + } catch (NumberParseException e) { + Log.w(TAG, e); + if (bareNumber.charAt(0) == '+') + return bareNumber; + + String localNumberImprecise = localNumberString; + + if (localNumberImprecise.charAt(0) == '+') + localNumberImprecise = localNumberImprecise.substring(1); + + if (localNumberImprecise.length() == number.length() || number.length() > localNumberImprecise.length()) + return "+" + number; + + int difference = localNumberImprecise.length() - number.length(); + + return "+" + localNumberImprecise.substring(0, difference) + number; + } + } + + + + } + + } diff --git a/src/org/thoughtcrime/securesms/database/EarlyReceiptCache.java b/src/org/thoughtcrime/securesms/database/EarlyReceiptCache.java index b23301e0ca..f1648397a2 100644 --- a/src/org/thoughtcrime/securesms/database/EarlyReceiptCache.java +++ b/src/org/thoughtcrime/securesms/database/EarlyReceiptCache.java @@ -11,7 +11,7 @@ public class EarlyReceiptCache { private final LRUCache cache = new LRUCache<>(100); - public synchronized void increment(long timestamp, String address) { + public synchronized void increment(long timestamp, Address address) { Log.w(TAG, this+""); Log.w(TAG, String.format("Early receipt: %d,%s", timestamp, address)); Placeholder tuple = new Placeholder(timestamp, address); @@ -24,7 +24,7 @@ public class EarlyReceiptCache { } } - public synchronized long remove(long timestamp, String address) { + public synchronized long remove(long timestamp, Address address) { Long count = cache.remove(new Placeholder(timestamp, address)); Log.w(TAG, this+""); Log.w(TAG, String.format("Checking early receipts (%d, %s): %d", timestamp, address, count)); @@ -34,9 +34,9 @@ public class EarlyReceiptCache { private class Placeholder { private final long timestamp; - private final @NonNull String address; + private final @NonNull Address address; - private Placeholder(long timestamp, @NonNull String address) { + private Placeholder(long timestamp, @NonNull Address address) { this.timestamp = timestamp; this.address = address; } diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java index b64ce90c4e..bad4ccdbc4 100644 --- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -101,22 +101,20 @@ public class GroupDatabase extends Database { } public @NonNull Recipients getGroupMembers(byte[] groupId, boolean includeSelf) { - String localNumber = TextSecurePreferences.getLocalNumber(context); - List members = getCurrentMembers(groupId); + List
members = getCurrentMembers(groupId); List recipients = new LinkedList<>(); - for (String member : members) { - if (!includeSelf && member.equals(localNumber)) + for (Address member : members) { + if (!includeSelf && Util.isOwnNumber(context, member)) continue; - recipients.addAll(RecipientFactory.getRecipientsFromString(context, member, false) - .getRecipientsList()); + recipients.add(RecipientFactory.getRecipientFor(context, member, false)); } return RecipientFactory.getRecipientsFor(context, recipients, false); } - public void create(byte[] groupId, String title, List members, + public void create(byte[] groupId, String title, List
members, SignalServiceAttachmentPointer avatar, String relay) { ContentValues contentValues = new ContentValues(); @@ -185,7 +183,7 @@ public class GroupDatabase extends Database { notifyDatabaseListeners(); } - public void updateMembers(byte[] id, List members) { + public void updateMembers(byte[] id, List
members) { ContentValues contents = new ContentValues(); contents.put(MEMBERS, Util.join(members, ",")); contents.put(ACTIVE, 1); @@ -194,8 +192,8 @@ public class GroupDatabase extends Database { new String[] {GroupUtil.getEncodedId(id)}); } - public void remove(byte[] id, String source) { - List currentMembers = getCurrentMembers(id); + public void remove(byte[] id, Address source) { + List
currentMembers = getCurrentMembers(id); currentMembers.remove(source); ContentValues contents = new ContentValues(); @@ -205,7 +203,7 @@ public class GroupDatabase extends Database { new String[] {GroupUtil.getEncodedId(id)}); } - private List getCurrentMembers(byte[] id) { + private List
getCurrentMembers(byte[] id) { Cursor cursor = null; try { @@ -215,7 +213,13 @@ public class GroupDatabase extends Database { null, null, null); if (cursor != null && cursor.moveToFirst()) { - return Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ","); + List
results = new LinkedList<>(); + + for (String member : Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ",")) { + results.add(Address.fromSerialized(member)); + } + + return results; } return new LinkedList<>(); @@ -286,16 +290,16 @@ public class GroupDatabase extends Database { public static class GroupRecord { - private final String id; - private final String title; - private final List 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 String id; + private final String title; + private final List
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; public GroupRecord(String id, String title, String members, byte[] avatar, long avatarId, byte[] avatarKey, String avatarContentType, @@ -303,7 +307,7 @@ public class GroupDatabase extends Database { { this.id = id; this.title = title; - this.members = Util.split(members, ","); + this.members = Address.fromSerializedList(members, ","); this.avatar = avatar; this.avatarId = avatarId; this.avatarKey = avatarKey; @@ -329,7 +333,7 @@ public class GroupDatabase extends Database { return title; } - public List getMembers() { + public List
getMembers() { return members; } diff --git a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java index 13b2293aa9..715fb14fdd 100644 --- a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java +++ b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java @@ -38,7 +38,7 @@ public class IdentityDatabase extends Database { private static final String TABLE_NAME = "identities"; private static final String ID = "_id"; - private static final String RECIPIENT = "recipient"; + private static final String ADDRESS = "address"; private static final String IDENTITY_KEY = "key"; private static final String TIMESTAMP = "timestamp"; private static final String FIRST_USE = "first_use"; @@ -47,7 +47,7 @@ public class IdentityDatabase extends Database { public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + - RECIPIENT + " INTEGER UNIQUE, " + + ADDRESS + " TEXT UNIQUE, " + IDENTITY_KEY + " TEXT, " + FIRST_USE + " INTEGER DEFAULT 0, " + TIMESTAMP + " INTEGER DEFAULT 0, " + @@ -65,8 +65,8 @@ public class IdentityDatabase extends Database { } public static VerifiedStatus forState(int state) { - if (state == 0) return DEFAULT; - else if (state == 1) return VERIFIED; + if (state == 0) return DEFAULT; + else if (state == 1) return VERIFIED; else if (state == 2) return UNVERIFIED; else throw new AssertionError("No such state: " + state); } @@ -86,13 +86,13 @@ public class IdentityDatabase extends Database { return new IdentityReader(cursor); } - public Optional getIdentity(long recipientId) { + public Optional getIdentity(Address address) { SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, null, RECIPIENT + " = ?", - new String[] {recipientId + ""}, null, null, null); + cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", + new String[] {address.serialize()}, null, null, null); if (cursor != null && cursor.moveToFirst()) { return Optional.of(getIdentityRecord(cursor)); @@ -106,14 +106,14 @@ public class IdentityDatabase extends Database { return Optional.absent(); } - public void saveIdentity(long recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus, + public void saveIdentity(Address address, 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(RECIPIENT, recipientId); + contentValues.put(ADDRESS, address.serialize()); contentValues.put(IDENTITY_KEY, identityKeyString); contentValues.put(TIMESTAMP, timestamp); contentValues.put(VERIFIED, verifiedStatus.toInt()); @@ -122,38 +122,36 @@ public class IdentityDatabase extends Database { database.replace(TABLE_NAME, null, contentValues); - EventBus.getDefault().post(new IdentityRecord(recipientId, identityKey, verifiedStatus, + EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus, firstUse, timestamp, nonBlockingApproval)); } - public void setApproval(long recipientId, boolean nonBlockingApproval) { + public void setApproval(Address address, boolean nonBlockingApproval) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(2); contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval); - database.update(TABLE_NAME, contentValues, RECIPIENT + " = ?", - new String[] {String.valueOf(recipientId)}); + database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()}); } - public void setVerified(long recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus) { + public void setVerified(Address address, 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, RECIPIENT + " = ? AND " + IDENTITY_KEY + " = ?", - new String[] {String.valueOf(recipientId), - Base64.encodeBytes(identityKey.serialize())}); + int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?", + new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())}); if (updated > 0) { - Optional record = getIdentity(recipientId); + Optional record = getIdentity(address); if (record.isPresent()) EventBus.getDefault().post(record.get()); } } private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException { - long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT)); + String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED)); @@ -161,23 +159,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(recipientId, identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval); + return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval); } public static class IdentityRecord { - private final long recipientId; + private final Address address; private final IdentityKey identitykey; private final VerifiedStatus verifiedStatus; private final boolean firstUse; private final long timestamp; private final boolean nonblockingApproval; - private IdentityRecord(long recipientId, + private IdentityRecord(Address address, IdentityKey identitykey, VerifiedStatus verifiedStatus, boolean firstUse, long timestamp, boolean nonblockingApproval) { - this.recipientId = recipientId; + this.address = address; this.identitykey = identitykey; this.verifiedStatus = verifiedStatus; this.firstUse = firstUse; @@ -185,8 +183,8 @@ public class IdentityDatabase extends Database { this.nonblockingApproval = nonblockingApproval; } - public long getRecipientId() { - return recipientId; + public Address getAddress() { + return address; } public IdentityKey getIdentityKey() { @@ -211,7 +209,7 @@ public class IdentityDatabase extends Database { @Override public String toString() { - return "{recipientId: " + recipientId + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}"; + return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}"; } } diff --git a/src/org/thoughtcrime/securesms/database/MediaDatabase.java b/src/org/thoughtcrime/securesms/database/MediaDatabase.java index cdc56342fd..4a5e0181b8 100644 --- a/src/org/thoughtcrime/securesms/database/MediaDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MediaDatabase.java @@ -5,9 +5,9 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.crypto.MasterSecret; @@ -57,10 +57,10 @@ public class MediaDatabase extends Database { public static class MediaRecord { private final DatabaseAttachment attachment; - private final String address; + private final Address address; private final long date; - private MediaRecord(DatabaseAttachment attachment, String address, long date) { + private MediaRecord(DatabaseAttachment attachment, @Nullable Address address, long date) { this.attachment = attachment; this.address = address; this.date = date; @@ -69,7 +69,12 @@ public class MediaDatabase extends Database { public static MediaRecord from(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Cursor cursor) { AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); DatabaseAttachment attachment = attachmentDatabase.getAttachment(masterSecret, cursor); - String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); + String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); + Address address = null; + + if (serializedAddress != null) { + address = Address.fromSerialized(serializedAddress); + } long date; @@ -90,7 +95,7 @@ public class MediaDatabase extends Database { return attachment.getContentType(); } - public String getAddress() { + public @Nullable Address getAddress() { return address; } diff --git a/src/org/thoughtcrime/securesms/database/MessagingDatabase.java b/src/org/thoughtcrime/securesms/database/MessagingDatabase.java index 6a660aa604..775a7c2339 100644 --- a/src/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -17,7 +17,6 @@ import org.whispersystems.libsignal.IdentityKey; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; public abstract class MessagingDatabase extends Database implements MmsSmsColumns { @@ -30,9 +29,9 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn protected abstract String getTableName(); - public void setMismatchedIdentity(long messageId, final long recipientId, final IdentityKey identityKey) { + public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) { List items = new ArrayList() {{ - add(new IdentityKeyMismatch(recipientId, identityKey)); + add(new IdentityKeyMismatch(address, identityKey)); }}; IdentityKeyMismatchList document = new IdentityKeyMismatchList(items); @@ -51,20 +50,20 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn } } - public void addMismatchedIdentity(long messageId, long recipientId, IdentityKey identityKey) { + public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { try { addToDocument(messageId, MISMATCHED_IDENTITIES, - new IdentityKeyMismatch(recipientId, identityKey), + new IdentityKeyMismatch(address, identityKey), IdentityKeyMismatchList.class); } catch (IOException e) { Log.w(TAG, e); } } - public void removeMismatchedIdentity(long messageId, long recipientId, IdentityKey identityKey) { + public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { try { removeFromDocument(messageId, MISMATCHED_IDENTITIES, - new IdentityKeyMismatch(recipientId, identityKey), + new IdentityKeyMismatch(address, identityKey), IdentityKeyMismatchList.class); } catch (IOException e) { Log.w(TAG, e); @@ -168,15 +167,15 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn public static class SyncMessageId { - private final String address; + private final Address address; private final long timetamp; - public SyncMessageId(String address, long timetamp) { + public SyncMessageId(Address address, long timetamp) { this.address = address; this.timetamp = timetamp; } - public String getAddress() { + public Address getAddress() { return address; } diff --git a/src/org/thoughtcrime/securesms/database/MmsAddressDatabase.java b/src/org/thoughtcrime/securesms/database/MmsAddressDatabase.java index 6753799d9b..d31deed4dd 100644 --- a/src/org/thoughtcrime/securesms/database/MmsAddressDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsAddressDatabase.java @@ -55,18 +55,18 @@ public class MmsAddressDatabase extends Database { super(context, databaseHelper); } - private void insertAddress(long messageId, int type, @NonNull String value) { + private void insertAddress(long messageId, int type, @NonNull Address value) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put(MMS_ID, messageId); contentValues.put(TYPE, type); - contentValues.put(ADDRESS, value); + contentValues.put(ADDRESS, value.serialize()); contentValues.put(ADDRESS_CHARSET, "UTF-8"); database.insert(TABLE_NAME, null, contentValues); } - private void insertAddress(long messageId, int type, @NonNull List addresses) { - for (String address : addresses) { + private void insertAddress(long messageId, int type, @NonNull List
addresses) { + for (Address address : addresses) { insertAddress(messageId, type, address); } } @@ -84,17 +84,17 @@ public class MmsAddressDatabase extends Database { public MmsAddresses getAddressesForId(long messageId) { SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; - String from = null; - List to = new LinkedList<>(); - List cc = new LinkedList<>(); - List bcc = new LinkedList<>(); + Address from = null; + List
to = new LinkedList<>(); + List
cc = new LinkedList<>(); + List
bcc = new LinkedList<>(); try { cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {messageId+""}, null, null, null); while (cursor != null && cursor.moveToNext()) { - long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); + long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)); + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); if (type == PduHeaders.FROM) from = address; if (type == PduHeaders.TO) to.add(address); @@ -109,8 +109,8 @@ public class MmsAddressDatabase extends Database { return new MmsAddresses(from, to, cc, bcc); } - public List getAddressesListForId(long messageId) { - List results = new LinkedList<>(); + public List
getAddressesListForId(long messageId) { + List
results = new LinkedList<>(); MmsAddresses addresses = getAddressesForId(messageId); if (addresses.getFrom() != null) { @@ -125,13 +125,12 @@ public class MmsAddressDatabase extends Database { } public Recipients getRecipientsForId(long messageId) { - List numbers = getAddressesListForId(messageId); - List results = new LinkedList<>(); + List
addresses = getAddressesListForId(messageId); + List results = new LinkedList<>(); - for (String number : numbers) { - if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(number)) { - results.add(RecipientFactory.getRecipientsFromString(context, number, false) - .getPrimaryRecipient()); + for (Address address : addresses) { + if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(address.serialize())) { + results.add(RecipientFactory.getRecipientFor(context, address, false)); } } diff --git a/src/org/thoughtcrime/securesms/database/MmsAddresses.java b/src/org/thoughtcrime/securesms/database/MmsAddresses.java index 5913ae978a..21302a222b 100644 --- a/src/org/thoughtcrime/securesms/database/MmsAddresses.java +++ b/src/org/thoughtcrime/securesms/database/MmsAddresses.java @@ -8,13 +8,13 @@ import java.util.List; public class MmsAddresses { - private final @Nullable String from; - private final @NonNull List to; - private final @NonNull List cc; - private final @NonNull List bcc; + private final @Nullable Address from; + private final @NonNull List
to; + private final @NonNull List
cc; + private final @NonNull List
bcc; - public MmsAddresses(@Nullable String from, @NonNull List to, - @NonNull List cc, @NonNull List bcc) + public MmsAddresses(@Nullable Address from, @NonNull List
to, + @NonNull List
cc, @NonNull List
bcc) { this.from = from; this.to = to; @@ -23,34 +23,34 @@ public class MmsAddresses { } @NonNull - public List getTo() { + public List
getTo() { return to; } @NonNull - public List getCc() { + public List
getCc() { return cc; } @NonNull - public List getBcc() { + public List
getBcc() { return bcc; } @Nullable - public String getFrom() { + public Address getFrom() { return from; } - public static MmsAddresses forTo(@NonNull List to) { - return new MmsAddresses(null, to, new LinkedList(), new LinkedList()); + public static MmsAddresses forTo(@NonNull List
to) { + return new MmsAddresses(null, to, new LinkedList
(), new LinkedList
()); } - public static MmsAddresses forBcc(@NonNull List bcc) { - return new MmsAddresses(null, new LinkedList(), new LinkedList(), bcc); + public static MmsAddresses forBcc(@NonNull List
bcc) { + return new MmsAddresses(null, new LinkedList
(), new LinkedList
(), bcc); } - public static MmsAddresses forFrom(@NonNull String from) { - return new MmsAddresses(from, new LinkedList(), new LinkedList(), new LinkedList()); + public static MmsAddresses forFrom(@NonNull Address from) { + return new MmsAddresses(from, new LinkedList
(), new LinkedList
(), new LinkedList
()); } } diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index 302d8c43a9..90889b2986 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -51,16 +51,15 @@ import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord; import org.thoughtcrime.securesms.jobs.TrimThreadJob; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -68,7 +67,6 @@ import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobManager; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -78,11 +76,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import org.thoughtcrime.securesms.mms.MmsException; - -import static org.thoughtcrime.securesms.util.Util.canonicalizeNumber; -import static org.thoughtcrime.securesms.util.Util.canonicalizeNumberOrGroup; - public class MmsDatabase extends MessagingDatabase { private static final String TAG = MmsDatabase.class.getSimpleName(); @@ -212,39 +205,30 @@ public class MmsDatabase extends MessagingDatabase { while (cursor.moveToNext()) { if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { - List addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); + List
addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); - for (String storedAddress : addresses) { - try { - String ourAddress = canonicalizeNumber(context, messageId.getAddress()); - String theirAddress = canonicalizeNumberOrGroup(context, storedAddress); + for (Address theirAddress : addresses) { + Address ourAddress = messageId.getAddress(); - if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - found = true; + found = true; - database.execSQL("UPDATE " + TABLE_NAME + " SET " + - RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?", - new String[] {String.valueOf(id)}); + database.execSQL("UPDATE " + TABLE_NAME + " SET " + + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?", + new String[] {String.valueOf(id)}); - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - } - } catch (InvalidNumberException e) { - Log.w("MmsDatabase", e); + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); } } } } if (!found) { - try { - earlyReceiptCache.increment(messageId.getTimetamp(), canonicalizeNumber(context, messageId.getAddress())); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } + earlyReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); } } finally { if (cursor != null) @@ -273,12 +257,12 @@ public class MmsDatabase extends MessagingDatabase { private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException { if (retrieved.getGroupId() != null) { - Recipients groupRecipients = RecipientFactory.getRecipientsFromString(context, retrieved.getGroupId(), true); + Recipients groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {retrieved.getGroupId()}, true); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients); } - String localNumber; - Set group = new HashSet<>(); + String localNumber; + Set
group = new HashSet<>(); if (retrieved.getAddresses().getFrom() == null) { throw new MmsException("FROM value in PduHeaders did not exist."); @@ -292,11 +276,11 @@ public class MmsDatabase extends MessagingDatabase { localNumber = ServiceUtil.getTelephonyManager(context).getLine1Number(); } - for (String cc : retrieved.getAddresses().getCc()) { + for (Address cc : retrieved.getAddresses().getCc()) { PhoneNumberUtil.MatchType match; if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH; - else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc); + else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc.toPhoneString()); if (match == PhoneNumberUtil.MatchType.NO_MATCH || match == PhoneNumberUtil.MatchType.NOT_A_NUMBER) @@ -307,11 +291,11 @@ public class MmsDatabase extends MessagingDatabase { if (retrieved.getAddresses().getTo().size() > 1) { - for (String to : retrieved.getAddresses().getTo()) { + for (Address to : retrieved.getAddresses().getTo()) { PhoneNumberUtil.MatchType match; if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH; - else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to); + else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to.toPhoneString()); if (match == PhoneNumberUtil.MatchType.NO_MATCH || match == PhoneNumberUtil.MatchType.NOT_A_NUMBER) @@ -322,8 +306,7 @@ public class MmsDatabase extends MessagingDatabase { } } - String recipientsList = Util.join(group, ","); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, recipientsList, false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, group.toArray(new Address[0]), false); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); } @@ -332,8 +315,7 @@ public class MmsDatabase extends MessagingDatabase { String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null ? Util.toIsoString(notification.getFrom().getTextString()) : ""; - Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString, false); - if (recipients.isEmpty()) recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, fromString)}, false); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); } @@ -486,7 +468,7 @@ public class MmsDatabase extends MessagingDatabase { while(cursor != null && cursor.moveToNext()) { if (Types.isSecureType(cursor.getLong(3))) { - SyncMessageId syncMessageId = new SyncMessageId(cursor.getString(1), cursor.getLong(2)); + SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2)); ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true); result.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); @@ -516,34 +498,29 @@ public class MmsDatabase extends MessagingDatabase { cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); while (cursor.moveToNext()) { - List addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); + List
addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); - for (String storedAddress : addresses) { - try { - String ourAddress = canonicalizeNumber(context, messageId.getAddress()); - String theirAddress = canonicalizeNumberOrGroup(context, storedAddress); + for (Address theirAddress : addresses) { + Address ourAddress = messageId.getAddress(); - if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); + if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); - ContentValues values = new ContentValues(); - values.put(READ, 1); + ContentValues values = new ContentValues(); + values.put(READ, 1); - if (expiresIn > 0) { - values.put(EXPIRE_STARTED, expireStarted); - expiring.add(new Pair<>(id, expiresIn)); - } - - database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)}); - - DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - notifyConversationListeners(threadId); + if (expiresIn > 0) { + values.put(EXPIRE_STARTED, expireStarted); + expiring.add(new Pair<>(id, expiresIn)); } - } catch (InvalidNumberException e) { - Log.w("MmsDatabase", e); + + database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)}); + + DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + notifyConversationListeners(threadId); } } } @@ -630,14 +607,14 @@ public class MmsDatabase extends MessagingDatabase { long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); List attachments = new LinkedList(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId)); MmsAddresses addresses = addr.getAddressesForId(messageId); - List destinations = new LinkedList<>(); + List
destinations = new LinkedList<>(); String body = getDecryptedBody(masterSecret, messageText, outboxType); destinations.addAll(addresses.getBcc()); destinations.addAll(addresses.getCc()); destinations.addAll(addresses.getTo()); - Recipients recipients = RecipientFactory.getRecipientsFromStrings(context, destinations, false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, destinations.toArray(new Address[0]), false); if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { return new OutgoingGroupMediaMessage(recipients, body, attachments, timestamp, 0); @@ -668,7 +645,7 @@ public class MmsDatabase extends MessagingDatabase { try { OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId); ContentValues contentValues = new ContentValues(); - contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getNumber()); + contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getAddress().serialize()); contentValues.put(DATE_SENT, request.getSentTimeMillis()); contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.ENCRYPTION_SYMMETRIC_BIT); contentValues.put(THREAD_ID, getThreadIdForMessage(messageId)); @@ -697,7 +674,7 @@ public class MmsDatabase extends MessagingDatabase { } return insertMediaMessage(new MasterSecretUnion(masterSecret), - MmsAddresses.forTo(request.getRecipients().toNumberStringList(false)), + MmsAddresses.forTo(request.getRecipients().getAddressesList()), request.getBody(), attachments, contentValues, @@ -726,7 +703,7 @@ public class MmsDatabase extends MessagingDatabase { ContentValues contentValues = new ContentValues(); contentValues.put(DATE_SENT, retrieved.getSentTimeMillis()); - contentValues.put(ADDRESS, retrieved.getAddresses().getFrom()); + contentValues.put(ADDRESS, retrieved.getAddresses().getFrom().serialize()); contentValues.put(MESSAGE_BOX, mailbox); contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); @@ -847,7 +824,7 @@ public class MmsDatabase extends MessagingDatabase { long messageId = db.insert(TABLE_NAME, null, contentValues); if (notification.getFrom() != null) { - addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Util.toIsoString(notification.getFrom().getTextString()))); + addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())))); } return new Pair<>(messageId, threadId); @@ -887,7 +864,7 @@ public class MmsDatabase extends MessagingDatabase { type |= Types.EXPIRATION_TIMER_UPDATE_BIT; } - List recipientNumbers = message.getRecipients().toNumberStringList(true); + List
recipientNumbers = message.getRecipients().getAddressesList(); MmsAddresses addresses; @@ -911,12 +888,7 @@ public class MmsDatabase extends MessagingDatabase { contentValues.put(EXPIRES_IN, message.getExpiresIn()); if (message.getRecipients().isSingleRecipient()) { - try { - contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), - canonicalizeNumber(context, message.getRecipients().getPrimaryRecipient().getNumber()))); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } + contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipients().getPrimaryRecipient().getAddress())); } contentValues.remove(ADDRESS); @@ -1021,7 +993,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 + " = ?", - new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom(), String.valueOf(threadId)}, + new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom().serialize(), String.valueOf(threadId)}, null, null, null, "1"); try { @@ -1249,21 +1221,21 @@ public class MmsDatabase extends MessagingDatabase { } private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); - 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)); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); - int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT)); - DisplayRecord.Body body = getBody(cursor); - int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); - String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); - String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); + 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)); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); + int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT)); + DisplayRecord.Body body = getBody(cursor); + int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); + String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); + String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); Recipients recipients = getRecipientsFor(address); List mismatches = getMismatchedIdentities(mismatchDocument); @@ -1276,18 +1248,16 @@ public class MmsDatabase extends MessagingDatabase { networkFailures, subscriptionId, expiresIn, expireStarted); } - private Recipients getRecipientsFor(String address) { - if (TextUtils.isEmpty(address) || address.equals("insert-address-token")) { - return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true); + private Recipients getRecipientsFor(String serialized) { + Address address; + + if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) { + address = Address.UNKNOWN; + } else { + address = Address.fromSerialized(serialized); + } - - Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, true); - - if (recipients == null || recipients.isEmpty()) { - return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true); - } - - return recipients; + return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true); } private List getMismatchedIdentities(String document) { diff --git a/src/org/thoughtcrime/securesms/database/PlaintextBackupExporter.java b/src/org/thoughtcrime/securesms/database/PlaintextBackupExporter.java index 46e019f58a..717f4f5026 100644 --- a/src/org/thoughtcrime/securesms/database/PlaintextBackupExporter.java +++ b/src/org/thoughtcrime/securesms/database/PlaintextBackupExporter.java @@ -44,7 +44,7 @@ public class PlaintextBackupExporter { while ((record = reader.getNext()) != null) { XmlBackup.XmlBackupItem item = - new XmlBackup.XmlBackupItem(0, record.getIndividualRecipient().getNumber(), + new XmlBackup.XmlBackupItem(0, record.getIndividualRecipient().getAddress().serialize(), record.getIndividualRecipient().getName(), record.getDateReceived(), MmsSmsColumns.Types.translateToSystemBaseType(record.getType()), diff --git a/src/org/thoughtcrime/securesms/database/PlaintextBackupImporter.java b/src/org/thoughtcrime/securesms/database/PlaintextBackupImporter.java index 30b03ab484..f48bcde277 100644 --- a/src/org/thoughtcrime/securesms/database/PlaintextBackupImporter.java +++ b/src/org/thoughtcrime/securesms/database/PlaintextBackupImporter.java @@ -34,7 +34,7 @@ public class PlaintextBackupImporter { XmlBackup.XmlBackupItem item; while ((item = backup.getNext()) != null) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, item.getAddress(), false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, item.getAddress())}, false); long threadId = threads.getThreadIdFor(recipients); SQLiteStatement statement = db.createInsertStatement(transaction); diff --git a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java index 1db7cd083a..5ab76b329a 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java @@ -19,7 +19,6 @@ import org.whispersystems.libsignal.util.guava.Optional; import java.util.Arrays; - public class RecipientPreferenceDatabase extends Database { private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName(); @@ -27,7 +26,7 @@ public class RecipientPreferenceDatabase extends Database { private static final String TABLE_NAME = "recipient_preferences"; private static final String ID = "_id"; - private static final String RECIPIENT_IDS = "recipient_ids"; + private static final String ADDRESSES = "recipient_ids"; private static final String BLOCK = "block"; private static final String NOTIFICATION = "notification"; private static final String VIBRATE = "vibrate"; @@ -58,7 +57,7 @@ public class RecipientPreferenceDatabase extends Database { public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + - RECIPIENT_IDS + " TEXT UNIQUE, " + + ADDRESSES + " TEXT UNIQUE, " + BLOCK + " INTEGER DEFAULT 0," + NOTIFICATION + " TEXT DEFAULT NULL, " + VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + @@ -75,7 +74,7 @@ public class RecipientPreferenceDatabase extends Database { public Cursor getBlocked() { SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_IDS}, BLOCK + " = 1", + Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESSES}, BLOCK + " = 1", null, null, null, null, null); cursor.setNotificationUri(context.getContentResolver(), Uri.parse(RECIPIENT_PREFERENCES_URI)); @@ -86,15 +85,15 @@ public class RecipientPreferenceDatabase extends Database { return new BlockedReader(context, cursor); } - public Optional getRecipientsPreferences(@NonNull long[] recipients) { - Arrays.sort(recipients); + public Optional getRecipientsPreferences(@NonNull Address[] addresses) { + Arrays.sort(addresses); SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; try { - cursor = database.query(TABLE_NAME, null, RECIPIENT_IDS + " = ?", - new String[] {Util.join(recipients, " ")}, + cursor = database.query(TABLE_NAME, null, ADDRESSES + " = ?", + new String[] {Util.join(addresses, " ")}, null, null, null); if (cursor != null && cursor.moveToNext()) { @@ -188,11 +187,11 @@ public class RecipientPreferenceDatabase extends Database { database.beginTransaction(); - int updated = database.update(TABLE_NAME, contentValues, RECIPIENT_IDS + " = ?", - new String[] {String.valueOf(recipients.getSortedIdsString())}); + int updated = database.update(TABLE_NAME, contentValues, ADDRESSES + " = ?", + new String[] {Util.join(recipients.getAddresses(), " ")}); if (updated < 1) { - contentValues.put(RECIPIENT_IDS, recipients.getSortedIdsString()); + contentValues.put(ADDRESSES, Util.join(recipients.getAddresses(), " ")); database.insert(TABLE_NAME, null, contentValues); } @@ -274,8 +273,15 @@ public class RecipientPreferenceDatabase extends Database { } public @NonNull Recipients getCurrent() { - String recipientIds = cursor.getString(cursor.getColumnIndexOrThrow(RECIPIENT_IDS)); - return RecipientFactory.getRecipientsForIds(context, recipientIds, false); + String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES)); + String[] addresses = serialized.split(" "); + Address[] addressList = new Address[addresses.length]; + + for (int i=0;i 0) { - contentValues.put(EXPIRE_STARTED, expireStarted); - expiring.add(new Pair<>(id, expiresIn)); - } - - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""}); - - DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - notifyConversationListeners(threadId); + if (expiresIn > 0) { + contentValues.put(EXPIRE_STARTED, expireStarted); + expiring.add(new Pair<>(id, expiresIn)); } - } catch (InvalidNumberException e) { - Log.w(TAG, e); + + database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""}); + + DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + notifyConversationListeners(threadId); } } } finally { @@ -440,7 +422,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().getNumber()); + contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize()); contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId()); contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); contentValues.put(DATE_SENT, record.getDateSent()); @@ -462,24 +444,24 @@ public class SmsDatabase extends MessagingDatabase { return new Pair<>(newMessageId, record.getThreadId()); } - public @NonNull Pair insertReceivedCall(@NonNull String number) { - return insertCallLog(number, Types.INCOMING_CALL_TYPE, false); + public @NonNull Pair insertReceivedCall(@NonNull Address address) { + return insertCallLog(address, Types.INCOMING_CALL_TYPE, false); } - public @NonNull Pair insertOutgoingCall(@NonNull String number) { - return insertCallLog(number, Types.OUTGOING_CALL_TYPE, false); + public @NonNull Pair insertOutgoingCall(@NonNull Address address) { + return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false); } - public @NonNull Pair insertMissedCall(@NonNull String number) { - return insertCallLog(number, Types.MISSED_CALL_TYPE, true); + public @NonNull Pair insertMissedCall(@NonNull Address address) { + return insertCallLog(address, Types.MISSED_CALL_TYPE, true); } - private @NonNull Pair insertCallLog(@NonNull String number, long type, boolean unread) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, true); + private @NonNull Pair insertCallLog(@NonNull Address address, long type, boolean unread) { + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {address}, true); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); ContentValues values = new ContentValues(6); - values.put(ADDRESS, number); + values.put(ADDRESS, address.serialize()); values.put(ADDRESS_DEVICE_ID, 1); values.put(DATE_RECEIVED, System.currentTimeMillis()); values.put(DATE_SENT, System.currentTimeMillis()); @@ -524,21 +506,14 @@ 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; - Recipients recipients; - - if (message.getSender() != null) { - recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), true); - } else { - Log.w(TAG, "Sender is null, returning unknown recipient"); - recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), false); - } + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, true); Recipients groupRecipients; if (message.getGroupId() == null) { groupRecipients = null; } else { - groupRecipients = RecipientFactory.getRecipientsFromString(context, message.getGroupId(), true); + groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getGroupId()}, true); } boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) || @@ -551,7 +526,7 @@ public class SmsDatabase extends MessagingDatabase { else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients); ContentValues values = new ContentValues(6); - values.put(ADDRESS, message.getSender()); + values.put(ADDRESS, message.getSender().serialize()); values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId()); values.put(DATE_RECEIVED, System.currentTimeMillis()); values.put(DATE_SENT, message.getSentTimestampMillis()); @@ -614,10 +589,10 @@ 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; - String address = message.getRecipients().getPrimaryRecipient().getNumber(); + Address address = message.getRecipients().getPrimaryRecipient().getAddress(); ContentValues contentValues = new ContentValues(6); - contentValues.put(ADDRESS, PhoneNumberUtils.formatNumber(address)); + contentValues.put(ADDRESS, address.serialize()); contentValues.put(THREAD_ID, threadId); contentValues.put(BODY, message.getMessageBody()); contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); @@ -626,12 +601,7 @@ public class SmsDatabase extends MessagingDatabase { contentValues.put(TYPE, type); contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); contentValues.put(EXPIRES_IN, message.getExpiresIn()); - - try { - contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, canonicalizeNumber(context, address))); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } + contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, address)); SQLiteDatabase db = databaseHelper.getWritableDatabase(); long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues); @@ -671,13 +641,13 @@ public class SmsDatabase extends MessagingDatabase { return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null); } - public Cursor getEncryptedRogueMessages(Recipient recipient) { - String selection = TYPE + " & " + Types.ENCRYPTION_REMOTE_NO_SESSION_BIT + " != 0" + - " AND PHONE_NUMBERS_EQUAL(" + ADDRESS + ", ?)"; - String[] args = {recipient.getNumber()}; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - return db.query(TABLE_NAME, MESSAGE_PROJECTION, selection, args, null, null, null); - } +// public Cursor getEncryptedRogueMessages(Recipient recipient) { +// String selection = TYPE + " & " + Types.ENCRYPTION_REMOTE_NO_SESSION_BIT + " != 0" + +// " AND PHONE_NUMBERS_EQUAL(" + ADDRESS + ", ?)"; +// String[] args = {recipient.getNumber()}; +// SQLiteDatabase db = databaseHelper.getReadableDatabase(); +// return db.query(TABLE_NAME, MESSAGE_PROJECTION, selection, args, null, null, null); +// } public Cursor getExpirationStartedMessages() { String where = EXPIRE_STARTED + " > 0"; @@ -706,7 +676,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 + " = ?", - new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender(), String.valueOf(threadId)}, + new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender().serialize(), String.valueOf(threadId)}, null, null, null, "1"); try { @@ -843,19 +813,19 @@ public class SmsDatabase extends MessagingDatabase { } public SmsMessageRecord getCurrent() { - long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)); - 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)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); - int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); - int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT)); - String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); + long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS))); + 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)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); + int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); + int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT)); + String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); List mismatches = getMismatches(mismatchDocument); Recipients recipients = getRecipientsFor(address); @@ -869,19 +839,8 @@ public class SmsDatabase extends MessagingDatabase { expiresIn, expireStarted); } - private Recipients getRecipientsFor(String address) { - if (address != null) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, true); - - if (recipients == null || recipients.isEmpty()) { - return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true); - } - - return recipients; - } else { - Log.w(TAG, "getRecipientsFor() address is null"); - return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true); - } + private Recipients getRecipientsFor(Address address) { + return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true); } private List getMismatches(String document) { diff --git a/src/org/thoughtcrime/securesms/database/SmsMigrator.java b/src/org/thoughtcrime/securesms/database/SmsMigrator.java index f78318b7b0..2fc899142f 100644 --- a/src/org/thoughtcrime/securesms/database/SmsMigrator.java +++ b/src/org/thoughtcrime/securesms/database/SmsMigrator.java @@ -27,9 +27,10 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; +import java.util.LinkedList; +import java.util.List; import java.util.StringTokenizer; public class SmsMigrator { @@ -137,24 +138,20 @@ public class SmsMigrator { } private static Recipients getOurRecipients(Context context, String theirRecipients) { - StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " "); - StringBuilder sb = new StringBuilder(); + StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " "); + List
addressList = new LinkedList<>(); while (tokenizer.hasMoreTokens()) { String theirRecipientId = tokenizer.nextToken(); String address = getTheirCanonicalAddress(context, theirRecipientId); - if (address == null) - continue; - - if (sb.length() != 0) - sb.append(','); - - sb.append(address); + if (address != null) { + addressList.add(Address.fromExternal(context, address)); + } } - if (sb.length() == 0) return null; - else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true); + if (addressList.isEmpty()) return null; + else return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), true); } private static String encrypt(MasterSecret masterSecret, String body) diff --git a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java index f7e1275041..a43849fee7 100644 --- a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java +++ b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java @@ -7,7 +7,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.text.TextUtils; +import android.support.annotation.NonNull; import android.util.Log; import org.whispersystems.signalservice.api.push.ContactTokenDetails; @@ -69,10 +69,9 @@ public class TextSecureDirectory { this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); } - public boolean isSecureTextSupported(String e164number) throws NotInDirectoryException { - if (e164number == null || e164number.length() == 0) { - return false; - } + public boolean isSecureTextSupported(@NonNull Address address) throws NotInDirectoryException { + if (address.isEmail()) return false; + if (address.isGroup()) return true; SQLiteDatabase db = databaseHelper.getReadableDatabase(); Cursor cursor = null; @@ -80,7 +79,7 @@ public class TextSecureDirectory { try { cursor = db.query(TABLE_NAME, new String[]{REGISTERED}, NUMBER + " = ?", - new String[] {e164number}, null, null, null); + new String[] {address.serialize()}, null, null, null); if (cursor != null && cursor.moveToFirst()) { return cursor.getInt(0) == 1; @@ -94,55 +93,55 @@ public class TextSecureDirectory { } } - public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException { - if (TextUtils.isEmpty(e164number)) { - return false; - } +// public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException { +// if (TextUtils.isEmpty(e164number)) { +// return false; +// } +// +// SQLiteDatabase db = databaseHelper.getReadableDatabase(); +// Cursor cursor = null; +// +// try { +// cursor = db.query(TABLE_NAME, +// new String[]{VOICE}, NUMBER + " = ?", +// new String[] {e164number}, null, null, null); +// +// if (cursor != null && cursor.moveToFirst()) { +// return cursor.getInt(0) == 1; +// } else { +// throw new NotInDirectoryException(); +// } +// +// } finally { +// if (cursor != null) +// cursor.close(); +// } +// } - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, - new String[]{VOICE}, NUMBER + " = ?", - new String[] {e164number}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getInt(0) == 1; - } else { - throw new NotInDirectoryException(); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - - public boolean isSecureVideoSupported(String e164number) throws NotInDirectoryException { - if (TextUtils.isEmpty(e164number)) { - return false; - } - - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, - new String[]{VIDEO}, NUMBER + " = ?", - new String[] {e164number}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getInt(0) == 1; - } else { - throw new NotInDirectoryException(); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } +// public boolean isSecureVideoSupported(String e164number) throws NotInDirectoryException { +// if (TextUtils.isEmpty(e164number)) { +// return false; +// } +// +// SQLiteDatabase db = databaseHelper.getReadableDatabase(); +// Cursor cursor = null; +// +// try { +// cursor = db.query(TABLE_NAME, +// new String[]{VIDEO}, NUMBER + " = ?", +// new String[] {e164number}, null, null, null); +// +// if (cursor != null && cursor.moveToFirst()) { +// return cursor.getInt(0) == 1; +// } else { +// throw new NotInDirectoryException(); +// } +// +// } finally { +// if (cursor != null) +// cursor.close(); +// } +// } public String getRelay(String e164number) { SQLiteDatabase database = databaseHelper.getReadableDatabase(); diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 667d485e46..0340b85c57 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -23,6 +23,7 @@ import android.database.MergeCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -30,21 +31,17 @@ import android.util.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.InvalidMessageException; -import java.util.Arrays; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -57,7 +54,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 RECIPIENT_IDS = "recipient_ids"; + public static final String ADDRESSES = "recipient_ids"; public static final String SNIPPET = "snippet"; private static final String SNIPPET_CHARSET = "snippet_cs"; public static final String READ = "read"; @@ -73,7 +70,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, " + RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESSES + " TEXT, " + 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, " + @@ -82,7 +79,7 @@ public class ThreadDatabase extends Database { LAST_SEEN + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { - "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");", + "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESSES + ");", "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", }; @@ -90,42 +87,12 @@ public class ThreadDatabase extends Database { super(context, databaseHelper); } - private long[] getRecipientIds(Recipients recipients) { - Set recipientSet = new HashSet<>(); - List recipientList = recipients.getRecipientsList(); - - for (Recipient recipient : recipientList) { - recipientSet.add(recipient.getRecipientId()); - } - - long[] recipientArray = new long[recipientSet.size()]; - int i = 0; - - for (Long recipientId : recipientSet) { - recipientArray[i++] = recipientId; - } - - Arrays.sort(recipientArray); - - return recipientArray; - } - - private String getRecipientsAsString(long[] recipientIds) { - StringBuilder sb = new StringBuilder(); - for (int i=0;i 1) contentValues.put(TYPE, distributionType); @@ -304,29 +271,24 @@ public class ThreadDatabase extends Database { notifyConversationListListeners(); } - public Cursor getFilteredConversationList(List filter) { + public Cursor getFilteredConversationList(List
filter) { if (filter == null || filter.size() == 0) return null; - List rawRecipientIds = DatabaseFactory.getAddressDatabase(context).getCanonicalAddressIds(filter); + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + List> partitionedAddresses = Util.partition(filter, 900); + List cursors = new LinkedList<>(); - if (rawRecipientIds == null || rawRecipientIds.size() == 0) - return null; + for (List
addresses : partitionedAddresses) { + String selection = ADDRESSES + " = ?"; + String[] selectionArgs = new String[addresses.size()]; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - List> partitionedRecipientIds = Util.partition(rawRecipientIds, 900); - List cursors = new LinkedList<>(); - - for (List recipientIds : partitionedRecipientIds) { - String selection = RECIPIENT_IDS + " = ?"; - String[] selectionArgs = new String[recipientIds.size()]; - - for (int i=0;i { diff --git a/src/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java b/src/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java index 843ce9d449..eaceb4d93f 100644 --- a/src/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java +++ b/src/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.database.documents; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.LinkedList; @@ -25,6 +26,7 @@ public class IdentityKeyMismatchList implements Document { } @Override + @JsonIgnore public List getList() { return mismatches; } diff --git a/src/org/thoughtcrime/securesms/database/documents/NetworkFailure.java b/src/org/thoughtcrime/securesms/database/documents/NetworkFailure.java index faa3d20af1..6dc87ca794 100644 --- a/src/org/thoughtcrime/securesms/database/documents/NetworkFailure.java +++ b/src/org/thoughtcrime/securesms/database/documents/NetworkFailure.java @@ -2,19 +2,21 @@ package org.thoughtcrime.securesms.database.documents; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thoughtcrime.securesms.database.Address; + public class NetworkFailure { - @JsonProperty(value = "r") - private long recipientId; + @JsonProperty(value = "a") + private String address; - public NetworkFailure(long recipientId) { - this.recipientId = recipientId; + public NetworkFailure(Address address) { + this.address = address.serialize(); } public NetworkFailure() {} - public long getRecipientId() { - return recipientId; + public Address getAddress() { + return Address.fromSerialized(address); } @Override @@ -22,11 +24,11 @@ public class NetworkFailure { if (other == null || !(other instanceof NetworkFailure)) return false; NetworkFailure that = (NetworkFailure)other; - return this.recipientId == that.recipientId; + return this.address.equals(that.address); } @Override public int hashCode() { - return (int)recipientId; + return address.hashCode(); } } diff --git a/src/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java b/src/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java index ad7679c350..4ddc76e333 100644 --- a/src/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java +++ b/src/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java @@ -77,7 +77,7 @@ public class IdentityRecordList { for (IdentityRecord identityRecord : identityRecords) { if (isUntrusted(identityRecord)) { - untrusted.add(RecipientFactory.getRecipientForId(context, identityRecord.getRecipientId(), false)); + untrusted.add(RecipientFactory.getRecipientFor(context, identityRecord.getAddress(), false)); } } @@ -101,7 +101,7 @@ public class IdentityRecordList { for (IdentityRecord identityRecord : identityRecords) { if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { - unverified.add(RecipientFactory.getRecipientForId(context, identityRecord.getRecipientId(), false)); + unverified.add(RecipientFactory.getRecipientFor(context, identityRecord.getAddress(), false)); } } diff --git a/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java index d0ef1de46a..4ffe26a39d 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java @@ -6,6 +6,7 @@ import android.database.MatrixCursor; import android.database.MergeCursor; 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.util.AbstractCursorLoader; @@ -41,7 +42,7 @@ public class ConversationListLoader extends AbstractCursorLoader { if (archivedCount > 0) { MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] { ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT, - ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ, + ThreadDatabase.ADDRESSES, ThreadDatabase.SNIPPET, ThreadDatabase.READ, ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI, ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT, ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN}, 1); @@ -62,6 +63,12 @@ public class ConversationListLoader extends AbstractCursorLoader { private Cursor getFilteredConversationList(String filter) { List numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter); - return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(numbers); + List
addresses = new LinkedList<>(); + + for (String number : numbers) { + addresses.add(Address.fromExternal(context, number)); + } + + return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(addresses); } } diff --git a/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java index 15787d7ce9..325a6730e1 100644 --- a/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java +++ b/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java @@ -88,6 +88,6 @@ public class WebRtcViewModel { } public String toString() { - return "[State: " + state + ", recipient: " + recipient.getNumber() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localVideoEnabled + "]"; + return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localVideoEnabled + "]"; } } diff --git a/src/org/thoughtcrime/securesms/groups/GroupManager.java b/src/org/thoughtcrime/securesms/groups/GroupManager.java index a0fb5aec89..73740ec445 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/src/org/thoughtcrime/securesms/groups/GroupManager.java @@ -11,6 +11,7 @@ import com.google.protobuf.ByteString; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; @@ -24,16 +25,17 @@ import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Set; public class GroupManager { + public static @NonNull GroupActionResult createGroup(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Set members, @@ -41,23 +43,23 @@ public class GroupManager { @Nullable String name) throws InvalidNumberException { - final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final byte[] groupId = groupDatabase.allocateGroupId(); - final Set memberE164Numbers = getE164Numbers(context, members); + final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final byte[] groupId = groupDatabase.allocateGroupId(); + final Set
memberAddresses = getMemberAddresses(context, members); - memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context)); - groupDatabase.create(groupId, name, new LinkedList<>(memberE164Numbers), null, null); + memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null); groupDatabase.updateAvatar(groupId, avatarBytes); - return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes); + return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); } - private static Set getE164Numbers(Context context, Collection recipients) + private static Set
getMemberAddresses(Context context, Collection recipients) throws InvalidNumberException { - final Set results = new HashSet<>(); + final Set
results = new HashSet<>(); for (Recipient recipient : recipients) { - results.add(Util.canonicalizeNumber(context, recipient.getNumber())); + results.add(recipient.getAddress()); } return results; @@ -71,33 +73,39 @@ public class GroupManager { @Nullable String name) throws InvalidNumberException { - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final Set memberE164Numbers = getE164Numbers(context, members); - final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final Set
memberAddresses = getMemberAddresses(context, members); + final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); - memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context)); - groupDatabase.updateMembers(groupId, new LinkedList<>(memberE164Numbers)); + memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses)); groupDatabase.updateTitle(groupId, name); groupDatabase.updateAvatar(groupId, avatarBytes); - return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes); + return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); } private static GroupActionResult sendGroupUpdate(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull byte[] groupId, - @NonNull Set e164numbers, + @NonNull Set
members, @Nullable String groupName, @Nullable byte[] avatar) { Attachment avatarAttachment = null; - String groupRecipientId = GroupUtil.getEncodedId(groupId); - Recipients groupRecipient = RecipientFactory.getRecipientsFromString(context, groupRecipientId, false); + Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId)); + Recipients groupRecipient = RecipientFactory.getRecipientsFor(context, new Address[]{groupAddress}, false); + + List numbers = new LinkedList<>(); + + for (Address member : members) { + numbers.add(member.serialize()); + } GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() .setId(ByteString.copyFrom(groupId)) .setType(GroupContext.Type.UPDATE) - .addAllMembers(e164numbers); + .addAllMembers(numbers); if (groupName != null) groupContextBuilder.setName(groupName); GroupContext groupContext = groupContextBuilder.build(); diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java index 544d7c679d..d9b3ad327c 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -10,13 +10,15 @@ import com.google.protobuf.ByteString; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -37,8 +39,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import org.thoughtcrime.securesms.mms.MmsException; - import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; @@ -88,9 +88,16 @@ public class GroupMessageProcessor { GroupContext.Builder builder = createGroupContext(group); builder.setType(GroupContext.Type.UPDATE); - SignalServiceAttachment avatar = group.getAvatar().orNull(); + SignalServiceAttachment avatar = group.getAvatar().orNull(); + List
members = group.getMembers().isPresent() ? new LinkedList
() : null; - database.create(id, group.getName().orNull(), group.getMembers().orNull(), + if (group.getMembers().isPresent()) { + for (String member : group.getMembers().get()) { + members.add(Address.fromExternal(context, member)); + } + } + + database.create(id, group.getName().orNull(), members, avatar != null && avatar.isPointer() ? avatar.asPointer() : null, envelope.getRelay()); @@ -108,24 +115,32 @@ public class GroupMessageProcessor { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); byte[] id = group.getGroupId(); - Set recordMembers = new HashSet<>(groupRecord.getMembers()); - Set messageMembers = new HashSet<>(group.getMembers().get()); + Set
recordMembers = new HashSet<>(groupRecord.getMembers()); + Set
messageMembers = new HashSet<>(); - Set addedMembers = new HashSet<>(messageMembers); + for (String messageMember : group.getMembers().get()) { + messageMembers.add(Address.fromExternal(context, messageMember)); + } + + Set
addedMembers = new HashSet<>(messageMembers); addedMembers.removeAll(recordMembers); - Set missingMembers = new HashSet<>(recordMembers); + Set
missingMembers = new HashSet<>(recordMembers); missingMembers.removeAll(messageMembers); GroupContext.Builder builder = createGroupContext(group); builder.setType(GroupContext.Type.UPDATE); if (addedMembers.size() > 0) { - Set unionMembers = new HashSet<>(recordMembers); + Set
unionMembers = new HashSet<>(recordMembers); unionMembers.addAll(messageMembers); database.updateMembers(id, new LinkedList<>(unionMembers)); - builder.clearMembers().addAllMembers(addedMembers); + builder.clearMembers(); + + for (Address addedMember : addedMembers) { + builder.addMembers(addedMember.serialize()); + } } else { builder.clearMembers(); } @@ -171,13 +186,13 @@ public class GroupMessageProcessor { { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); byte[] id = group.getGroupId(); - List members = record.getMembers(); + List
members = record.getMembers(); GroupContext.Builder builder = createGroupContext(group); builder.setType(GroupContext.Type.QUIT); - if (members.contains(envelope.getSource())) { - database.remove(id, envelope.getSource()); + if (members.contains(Address.fromExternal(context, envelope.getSource()))) { + database.remove(id, Address.fromExternal(context, envelope.getSource())); if (outgoing) database.setActive(id, false); return storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); @@ -202,7 +217,8 @@ public class GroupMessageProcessor { try { if (outgoing) { MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false); + Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId())); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {addres}, false); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null); @@ -213,7 +229,7 @@ public class GroupMessageProcessor { } else { EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); String body = Base64.encodeBytes(storage.toByteArray()); - IncomingTextMessage incoming = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0); + IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); Optional insertResult = smsDatabase.insertMessageInbox(masterSecret, groupMessage); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index f8f888a35f..34d933edd2 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.ApnUnavailableException; import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsRadioException; import org.thoughtcrime.securesms.mms.PartParser; import org.thoughtcrime.securesms.notifications.MessageNotifier; @@ -42,8 +43,6 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; -import org.thoughtcrime.securesms.mms.MmsException; - public class MmsDownloadJob extends MasterSecretJob { private static final String TAG = MmsDownloadJob.class.getSimpleName(); @@ -206,7 +205,7 @@ public class MmsDownloadJob extends MasterSecretJob { - IncomingMediaMessage message = new IncomingMediaMessage(from, to, cc, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false); + IncomingMediaMessage message = new IncomingMediaMessage(context, from, to, cc, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false); Optional insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret), message, contentLocation, threadId); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java index c1c19eddc0..1411299be1 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java @@ -10,6 +10,7 @@ import com.google.android.mms.pdu_alt.PduHeaders; import com.google.android.mms.pdu_alt.PduParser; import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -85,7 +86,7 @@ public class MmsReceiveJob extends ContextJob { private boolean isBlocked(GenericPdu pdu) { if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, Util.toIsoString(pdu.getFrom().getTextString()), false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString()))}, false); return recipients.isBlocked(); } diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 0435d7a309..24a356ec40 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -21,12 +21,14 @@ import com.klinker.android.send_message.Utils; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsSendResult; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -45,8 +47,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; -import org.thoughtcrime.securesms.mms.MmsException; - public class MmsSendJob extends SendJob { private static final long serialVersionUID = 0L; @@ -171,16 +171,16 @@ public class MmsSendJob extends SendJob { { SendReq req = new SendReq(); String lineNumber = Utils.getMyPhoneNumber(context); - List numbers = message.getRecipients().toNumberStringList(true); - MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); + Address[] numbers = message.getRecipients().getAddresses(); + MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); List scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); if (!TextUtils.isEmpty(lineNumber)) { req.setFrom(new EncodedStringValue(lineNumber)); } - for (String recipient : numbers) { - req.addTo(new EncodedStringValue(recipient)); + for (Address recipient : numbers) { + req.addTo(new EncodedStringValue(recipient.serialize())); } req.setDate(System.currentTimeMillis() / 1000); diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java index 1a3ee6d81f..84194c0aee 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java @@ -10,16 +10,13 @@ import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; -import org.whispersystems.libsignal.logging.Log; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.util.LinkedList; @@ -57,11 +54,7 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje while ((recipients = reader.getNext()) != null) { if (recipients.isSingleRecipient()) { - try { - blocked.add(Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber())); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } + blocked.add(recipients.getPrimaryRecipient().getAddress().toPhoneString()); } } diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java index 8ee0bb751f..252b0e6c2e 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java @@ -6,13 +6,14 @@ import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.ContactsContract; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.text.TextUtils; import android.util.Log; import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.dependencies.InjectableType; @@ -20,9 +21,7 @@ import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalM import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.libsignal.IdentityKey; @@ -50,19 +49,19 @@ import javax.inject.Inject; public class MultiDeviceContactUpdateJob extends MasterSecretJob implements InjectableType { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName(); @Inject transient SignalMessageSenderFactory messageSenderFactory; - private final long recipientId; + private final @Nullable String address; public MultiDeviceContactUpdateJob(Context context) { - this(context, -1); + this(context, null); } - public MultiDeviceContactUpdateJob(Context context, long recipientId) { + public MultiDeviceContactUpdateJob(Context context, Address address) { super(context, JobParameters.newBuilder() .withRequirement(new NetworkRequirement(context)) .withRequirement(new MasterSecretRequirement(context)) @@ -70,7 +69,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje .withPersistence() .create()); - this.recipientId = recipientId; + this.address = address.serialize(); } @Override @@ -82,11 +81,11 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje return; } - if (recipientId <= 0) generateFullContactUpdate(); - else generateSingleContactUpdate(recipientId); + if (address == null) generateFullContactUpdate(); + else generateSingleContactUpdate(Address.fromSerialized(address)); } - private void generateSingleContactUpdate(long recipientId) + private void generateSingleContactUpdate(@NonNull Address address) throws IOException, UntrustedIdentityException, NetworkException { SignalServiceMessageSender messageSender = messageSenderFactory.create(); @@ -94,11 +93,11 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje try { DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); - Recipient recipient = RecipientFactory.getRecipientForId(context, recipientId, false); - Optional identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipientId); + Recipient recipient = RecipientFactory.getRecipientFor(context, address, false); + Optional identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); Optional verifiedMessage = getVerifiedMessage(recipient, identityRecord); - out.write(new DeviceContact(Util.canonicalizeNumber(context, recipient.getNumber()), + out.write(new DeviceContact(address.toPhoneString(), Optional.fromNullable(recipient.getName()), getAvatar(recipient.getContactUri()), Optional.fromNullable(recipient.getColor().serialize()), @@ -126,14 +125,14 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje for (ContactData contactData : contacts) { Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id)); - String number = Util.canonicalizeNumber(context, contactData.numbers.get(0).number); - Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, true).getPrimaryRecipient(); - Optional identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getRecipientId()); + Address address = Address.fromExternal(context, contactData.numbers.get(0).number); + Recipient recipient = RecipientFactory.getRecipientFor(context, address, false); + Optional identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); Optional verified = getVerifiedMessage(recipient, identity); Optional name = Optional.fromNullable(contactData.name); - Optional color = getColor(number); + Optional color = Optional.of(recipient.getColor().serialize()); - out.write(new DeviceContact(number, name, getAvatar(contactUri), color, verified)); + out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified)); } out.close(); @@ -161,15 +160,6 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje } - private Optional getColor(String number) { - if (!TextUtils.isEmpty(number)) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false); - return Optional.of(recipients.getColor().serialize()); - } else { - return Optional.absent(); - } - } - private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete) throws IOException, UntrustedIdentityException, NetworkException { @@ -245,7 +235,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje private Optional getVerifiedMessage(Recipient recipient, Optional identity) throws InvalidNumberException { if (!identity.isPresent()) return Optional.absent(); - String destination = Util.canonicalizeNumber(context, recipient.getNumber()); + String destination = recipient.getAddress().toPhoneString(); IdentityKey identityKey = identity.get().getIdentityKey(); VerifiedMessage.VerifiedState state; diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java index fa015fc32a..b993b19226 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java @@ -5,6 +5,7 @@ import android.support.annotation.Nullable; import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.dependencies.InjectableType; @@ -27,6 +28,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import javax.inject.Inject; @@ -61,8 +64,14 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject reader = DatabaseFactory.getGroupDatabase(context).getGroups(); while ((record = reader.getNext()) != null) { + List members = new LinkedList<>(); + + for (Address member : record.getMembers()) { + members.add(member.serialize()); + } + out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()), - record.getMembers(), getAvatar(record.getAvatar()), + members, getAvatar(record.getAvatar()), record.isActive())); } diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java index 9f2adacb61..27155cdd6f 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java @@ -44,7 +44,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta this.messageIds = new LinkedList<>(); for (SyncMessageId messageId : messageIds) { - this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress(), messageId.getTimetamp())); + this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp())); } } diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java index 6dee591a93..141b60e3cd 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java @@ -4,11 +4,11 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.util.Log; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.libsignal.IdentityKey; @@ -18,7 +18,6 @@ import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; @@ -38,14 +37,14 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab private final VerifiedStatus verifiedStatus; private final long timestamp; - public MultiDeviceVerifiedUpdateJob(Context context, String destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) { + public MultiDeviceVerifiedUpdateJob(Context context, Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) { super(context, JobParameters.newBuilder() .withRequirement(new NetworkRequirement(context)) .withPersistence() .withGroupId("__MULTI_DEVICE_VERIFIED_UPDATE__") .create()); - this.destination = destination; + this.destination = destination.serialize(); this.identityKey = identityKey.serialize(); this.verifiedStatus = verifiedStatus; this.timestamp = System.currentTimeMillis(); @@ -64,13 +63,13 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab return; } - String canonicalDestination = Util.canonicalizeNumber(context, destination); + Address canonicalDestination = Address.fromSerialized(destination); VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus); SignalServiceMessageSender messageSender = messageSenderFactory.create(); - VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination, new IdentityKey(identityKey, 0), verifiedState, timestamp); + VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp); messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage)); - } catch (InvalidNumberException | InvalidKeyException e) { + } catch (InvalidKeyException e) { throw new IOException(e); } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 8beb4d7399..4fe5f9a6a2 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.GroupDatabase; @@ -79,6 +80,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -92,11 +94,11 @@ public class PushDecryptJob extends ContextJob { private final long messageId; private final long smsMessageId; - public PushDecryptJob(Context context, long pushMessageId, String sender) { - this(context, pushMessageId, -1, sender); + public PushDecryptJob(Context context, long pushMessageId) { + this(context, pushMessageId, -1); } - public PushDecryptJob(Context context, long pushMessageId, long smsMessageId, String sender) { + public PushDecryptJob(Context context, long pushMessageId, long smsMessageId) { super(context, JobParameters.newBuilder() .withPersistence() .withGroupId("__PUSH_DECRYPT_JOB__") @@ -221,7 +223,7 @@ public class PushDecryptJob extends ContextJob { 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_NUMBER, envelope.getSource()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource())); intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, envelope.getTimestamp()); context.startService(intent); @@ -235,7 +237,7 @@ public class PushDecryptJob extends ContextJob { 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_NUMBER, envelope.getSource()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource())); intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); context.startService(intent); } @@ -248,7 +250,7 @@ public class PushDecryptJob extends ContextJob { 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_NUMBER, envelope.getSource()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource())); 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()); @@ -267,7 +269,7 @@ public class PushDecryptJob extends ContextJob { 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_NUMBER, envelope.getSource()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource())); context.startService(intent); } } @@ -278,7 +280,7 @@ public class PushDecryptJob extends ContextJob { 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_NUMBER, envelope.getSource()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource())); context.startService(intent); } @@ -288,7 +290,7 @@ public class PushDecryptJob extends ContextJob { @NonNull Optional smsMessageId) { EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); - IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(), + IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), message.getTimestamp(), "", Optional.absent(), 0); @@ -328,7 +330,7 @@ public class PushDecryptJob extends ContextJob { if (recipients.isSingleRecipient() && !recipients.isGroupRecipient()) { SessionStore sessionStore = new TextSecureSessionStore(context); - sessionStore.deleteAllSessions(recipients.getPrimaryRecipient().getNumber()); + sessionStore.deleteAllSessions(recipients.getPrimaryRecipient().getAddress().toPhoneString()); SecurityEvent.broadcastSecurityUpdateEvent(context); @@ -373,8 +375,10 @@ public class PushDecryptJob extends ContextJob { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); String localNumber = TextSecurePreferences.getLocalNumber(context); Recipients recipients = getMessageDestination(envelope, message); - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(), - localNumber, message.getTimestamp(), -1, + IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, + Address.fromExternal(context, envelope.getSource()), + Address.fromSerialized(localNumber), + message.getTimestamp(), -1, message.getExpiresInSeconds() * 1000, true, Optional.fromNullable(envelope.getRelay()), Optional.absent(), message.getGroupInfo(), @@ -458,8 +462,8 @@ public class PushDecryptJob extends ContextJob { long envelopeTimestamp) { for (ReadMessage readMessage : readMessages) { - List> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(readMessage.getSender(), readMessage.getTimestamp()), envelopeTimestamp); - List> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(readMessage.getSender(), readMessage.getTimestamp()), envelopeTimestamp); + List> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); + List> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); for (Pair expiringMessage : expiringText) { ApplicationContext.getInstance(context) @@ -488,8 +492,10 @@ public class PushDecryptJob extends ContextJob { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); String localNumber = TextSecurePreferences.getLocalNumber(context); Recipients recipients = getMessageDestination(envelope, message); - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(), - localNumber, message.getTimestamp(), -1, + IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, + Address.fromExternal(context, envelope.getSource()), + Address.fromSerialized(localNumber), + message.getTimestamp(), -1, message.getExpiresInSeconds() * 1000, false, Optional.fromNullable(envelope.getRelay()), message.getBody(), @@ -616,7 +622,7 @@ public class PushDecryptJob extends ContextJob { if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) { threadId = database.updateBundleMessageBody(masterSecret, smsMessageId.get(), body).second; } else { - IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), + IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), message.getTimestamp(), body, message.getGroupInfo(), @@ -764,14 +770,14 @@ public class PushDecryptJob extends ContextJob { { try { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); - long recipientId = recipients.getPrimaryRecipient().getRecipientId(); + Address sourceAddress = Address.fromExternal(context, envelope.getSource()); byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent(); PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized); IdentityKey identityKey = whisperMessage.getIdentityKey(); String encoded = Base64.encodeBytes(serialized); - IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), + IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress, + envelope.getSourceDevice(), envelope.getTimestamp(), encoded, Optional.absent(), 0); @@ -780,13 +786,13 @@ public class PushDecryptJob extends ContextJob { Optional insertResult = database.insertMessageInbox(masterSecret, bundleMessage); if (insertResult.isPresent()) { - database.setMismatchedIdentity(insertResult.get().getMessageId(), recipientId, identityKey); + database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey); MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); } } else { database.updateMessageBody(masterSecret, smsMessageId.get(), encoded); database.markAsPreKeyBundle(smsMessageId.get()); - database.setMismatchedIdentity(smsMessageId.get(), recipientId, identityKey); + database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey); } } catch (InvalidMessageException | InvalidVersionException e) { throw new AssertionError(e); @@ -795,7 +801,8 @@ public class PushDecryptJob extends ContextJob { private Optional insertPlaceholder(@NonNull SignalServiceEnvelope envelope) { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); - IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), + IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), + envelope.getSourceDevice(), envelope.getTimestamp(), "", Optional.absent(), 0); @@ -805,17 +812,17 @@ public class PushDecryptJob extends ContextJob { private Recipients getSyncMessageDestination(SentTranscriptMessage message) { if (message.getMessage().getGroupInfo().isPresent()) { - return RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId()), false); + return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId()))}, false); } else { - return RecipientFactory.getRecipientsFromString(context, message.getDestination().get(), false); + return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, message.getDestination().get())}, false); } } private Recipients getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) { if (message.getGroupInfo().isPresent()) { - return RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId()), false); + return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId()))}, false); } else { - return RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); + return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, envelope.getSource())}, false); } } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index dd9709719c..ce57af90a8 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -1,11 +1,14 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; @@ -17,7 +20,6 @@ import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; @@ -51,20 +53,22 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { @Inject transient SignalMessageSenderFactory messageSenderFactory; - private final long messageId; - private final long filterRecipientId; + private final long messageId; + private final long filterRecipientId; // Deprecated + private final String filterAddress; - public PushGroupSendJob(Context context, long messageId, String destination, long filterRecipientId) { + public PushGroupSendJob(Context context, long messageId, @NonNull Address destination, @Nullable Address filterAddress) { super(context, JobParameters.newBuilder() .withPersistence() - .withGroupId(destination) + .withGroupId(destination.toGroupString()) .withRequirement(new MasterSecretRequirement(context)) .withRequirement(new NetworkRequirement(context)) .withRetryCount(5) .create()); this.messageId = messageId; - this.filterRecipientId = filterRecipientId; + this.filterAddress = filterAddress == null ? null :filterAddress.toPhoneString(); + this.filterRecipientId = -1; } @Override @@ -79,7 +83,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId); try { - deliver(masterSecret, message, filterRecipientId); + deliver(masterSecret, message, filterAddress == null ? null : Address.fromSerialized(filterAddress)); database.markAsSent(messageId, true); markAttachmentsUploaded(messageId, message.getAttachments()); @@ -99,13 +103,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { List failures = new LinkedList<>(); for (NetworkFailureException nfe : e.getNetworkExceptions()) { - Recipient recipient = RecipientFactory.getRecipientsFromString(context, nfe.getE164number(), false).getPrimaryRecipient(); - failures.add(new NetworkFailure(recipient.getRecipientId())); + failures.add(new NetworkFailure(Address.fromSerialized(nfe.getE164number()))); } for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) { - Recipient recipient = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false).getPrimaryRecipient(); - database.addMismatchedIdentity(messageId, recipient.getRecipientId(), uie.getIdentityKey()); + database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey()); } database.addFailures(messageId, failures); @@ -131,20 +133,21 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); } - private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, long filterRecipientId) + private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, @Nullable Address filterAddress) throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions, UndeliverableMessageException { SignalServiceMessageSender messageSender = messageSenderFactory.create(); - byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getNumber()); + byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getAddress().toGroupString()); Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); List scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); + List addresses; - if (filterRecipientId >= 0) addresses = getPushAddresses(filterRecipientId); - else addresses = getPushAddresses(recipients); + if (filterAddress != null) addresses = getPushAddresses(filterAddress); + else addresses = getPushAddresses(recipients); if (message.isGroup()) { OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; @@ -166,20 +169,19 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { } } - private List getPushAddresses(Recipients recipients) throws InvalidNumberException { + private List getPushAddresses(Address address) { + List addresses = new LinkedList<>(); + addresses.add(getPushAddress(address)); + return addresses; + } + + private List getPushAddresses(Recipients recipients) { List addresses = new LinkedList<>(); for (Recipient recipient : recipients.getRecipientsList()) { - addresses.add(getPushAddress(recipient.getNumber())); + addresses.add(getPushAddress(recipient.getAddress())); } return addresses; } - - private List getPushAddresses(long filterRecipientId) throws InvalidNumberException { - List addresses = new LinkedList<>(); - addresses.add(getPushAddress(RecipientFactory.getRecipientForId(context, filterRecipientId, false).getNumber())); - return addresses; - } - } diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java index f7a132d50f..30d30703de 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java @@ -4,6 +4,7 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.util.Log; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; @@ -23,6 +24,8 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import javax.inject.Inject; @@ -72,11 +75,16 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType { .build(); } + List members = new LinkedList<>(); + + for (Address member : record.getMembers()) { + members.add(member.serialize()); + } SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE) .withAvatar(avatar) .withId(groupId) - .withMembers(record.getMembers()) + .withMembers(members) .withName(record.getTitle()) .build(); diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index d0d63ab294..9c538b1f14 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -6,14 +6,14 @@ import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.RetryLaterException; @@ -24,7 +24,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.FileNotFoundException; import java.io.IOException; @@ -32,8 +31,6 @@ import java.util.List; import javax.inject.Inject; -import org.thoughtcrime.securesms.mms.MmsException; - import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory; public class PushMediaSendJob extends PushSendJob implements InjectableType { @@ -46,7 +43,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { private final long messageId; - public PushMediaSendJob(Context context, long messageId, String destination) { + public PushMediaSendJob(Context context, long messageId, Address destination) { super(context, constructParameters(context, destination)); this.messageId = messageId; } @@ -82,10 +79,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); } catch (UntrustedIdentityException uie) { Log.w(TAG, uie); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false); - long recipientId = recipients.getPrimaryRecipient().getRecipientId(); - - database.addMismatchedIdentity(messageId, recipientId, uie.getIdentityKey()); + database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey()); database.markAsSentFailed(messageId); } } @@ -108,17 +102,14 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, UndeliverableMessageException { - if (message.getRecipients() == null || - message.getRecipients().getPrimaryRecipient() == null || - message.getRecipients().getPrimaryRecipient().getNumber() == null) - { + if (message.getRecipients() == null || message.getRecipients().getPrimaryRecipient() == null) { throw new UndeliverableMessageException("No destination address."); } SignalServiceMessageSender messageSender = messageSenderFactory.create(); try { - SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getNumber()); + SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getAddress()); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); List scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); @@ -131,7 +122,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { .build(); messageSender.sendMessage(address, mediaMessage); - } catch (InvalidNumberException | UnregisteredUserException e) { + } catch (UnregisteredUserException e) { Log.w(TAG, e); throw new InsecureFallbackApprovalException(e); } catch (FileNotFoundException e) { diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index 0bd5cc94cd..ecf8fc638f 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -4,8 +4,8 @@ import android.content.Context; import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.TextSecureDirectory; @@ -26,33 +26,35 @@ public abstract class PushReceivedJob extends ContextJob { } public void handle(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) { - if (!isActiveNumber(context, envelope.getSource())) { + Address source = Address.fromExternal(context, envelope.getSource()); + + if (!isActiveNumber(context, source)) { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); ContactTokenDetails contactTokenDetails = new ContactTokenDetails(); contactTokenDetails.setNumber(envelope.getSource()); directory.setNumber(contactTokenDetails, true); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipients)); } if (envelope.isReceipt()) { handleReceipt(envelope); } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) { - handleMessage(envelope, sendExplicitReceipt); + handleMessage(envelope, source, sendExplicitReceipt); } else { Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); } } - private void handleMessage(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); + private void handleMessage(SignalServiceEnvelope envelope, Address source, boolean sendExplicitReceipt) { + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); if (!recipients.isBlocked()) { long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope); - jobManager.add(new PushDecryptJob(context, messageId, envelope.getSource())); + jobManager.add(new PushDecryptJob(context, messageId)); } else { Log.w(TAG, "*** Received blocked push message, ignoring..."); } @@ -66,15 +68,15 @@ public abstract class PushReceivedJob extends ContextJob { private void handleReceipt(SignalServiceEnvelope envelope) { Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp())); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(envelope.getSource(), + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), envelope.getTimestamp())); } - private boolean isActiveNumber(Context context, String e164number) { + private boolean isActiveNumber(Context context, Address address) { boolean isActiveNumber; try { - isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(e164number); + isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(address); } catch (NotInDirectoryException e) { isActiveNumber = false; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index c0104a3cd8..060f19bb41 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.TextSecureExpiredException; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.events.PartProgressEvent; @@ -16,14 +17,12 @@ import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.io.InputStream; @@ -38,10 +37,10 @@ public abstract class PushSendJob extends SendJob { super(context, parameters); } - protected static JobParameters constructParameters(Context context, String destination) { + protected static JobParameters constructParameters(Context context, Address destination) { JobParameters.Builder builder = JobParameters.newBuilder(); builder.withPersistence(); - builder.withGroupId(destination); + builder.withGroupId(destination.serialize()); builder.withRequirement(new MasterSecretRequirement(context)); builder.withRequirement(new NetworkRequirement(context)); builder.withRetryCount(5); @@ -62,10 +61,9 @@ public abstract class PushSendJob extends SendJob { onPushSend(masterSecret); } - protected SignalServiceAddress getPushAddress(String number) throws InvalidNumberException { - String e164number = Util.canonicalizeNumber(context, number); - String relay = TextSecureDirectory.getInstance(context).getRelay(e164number); - return new SignalServiceAddress(e164number, Optional.fromNullable(relay)); + protected SignalServiceAddress getPushAddress(Address address) { + String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString()); + return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay)); } protected List getAttachmentsFor(MasterSecret masterSecret, List parts) { diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 52bce00ddf..402033edc5 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -5,14 +5,13 @@ import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; @@ -22,7 +21,6 @@ import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; @@ -40,7 +38,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { private final long messageId; - public PushTextSendJob(Context context, long messageId, String destination) { + public PushTextSendJob(Context context, long messageId, Address destination) { super(context, constructParameters(context, destination)); this.messageId = messageId; } @@ -72,10 +70,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); } catch (UntrustedIdentityException e) { Log.w(TAG, e); - Recipients recipients = RecipientFactory.getRecipientsFromString(context, e.getE164Number(), false); - long recipientId = recipients.getPrimaryRecipient().getRecipientId(); - - database.addMismatchedIdentity(record.getId(), recipientId, e.getIdentityKey()); + database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey()); database.markAsSentFailed(record.getId()); database.markAsPush(record.getId()); } @@ -104,7 +99,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException { try { - SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getNumber()); + SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress()); SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() .withTimestamp(message.getDateSent()) @@ -115,7 +110,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { messageSender.sendMessage(address, textSecureMessage); - } catch (InvalidNumberException | UnregisteredUserException e) { + } catch (UnregisteredUserException e) { Log.w(TAG, e); throw new InsecureFallbackApprovalException(e); } catch (IOException e) { diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java index 285aeadeef..1ba656681a 100644 --- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java @@ -9,13 +9,11 @@ import android.util.Log; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.MessageRetrievalService; import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; @@ -35,14 +33,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType { @Inject transient SignalServiceMessageReceiver receiver; - private final long[] recipientIds; + private final Recipients recipients; public RetrieveProfileJob(Context context, Recipients recipients) { super(context, JobParameters.newBuilder() .withRetryCount(3) .create()); - this.recipientIds = recipients.getIds(); + this.recipients = recipients; } @Override @@ -51,8 +49,6 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType { @Override public void onRun() throws IOException, InvalidKeyException { try { - Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, true); - for (Recipient recipient : recipients) { if (recipient.isGroupRecipient()) handleGroupRecipient(recipient); else handleIndividualRecipient(recipient); @@ -73,7 +69,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType { private void handleIndividualRecipient(Recipient recipient) throws IOException, InvalidKeyException, InvalidNumberException { - String number = Util.canonicalizeNumber(context, recipient.getNumber()); + String number = recipient.getAddress().toPhoneString(); SignalServiceProfile profile = retrieveProfile(number); if (TextUtils.isEmpty(profile.getIdentityKey())) { @@ -84,7 +80,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType { IdentityKey identityKey = new IdentityKey(Base64.decode(profile.getIdentityKey()), 0); if (!DatabaseFactory.getIdentityDatabase(context) - .getIdentity(recipient.getRecipientId()) + .getIdentity(recipient.getAddress()) .isPresent()) { Log.w(TAG, "Still first use..."); @@ -97,7 +93,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType { private void handleGroupRecipient(Recipient group) throws IOException, InvalidKeyException, InvalidNumberException { - byte[] groupId = GroupUtil.getDecodedId(group.getNumber()); + byte[] groupId = GroupUtil.getDecodedId(group.getAddress().toGroupString()); Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); for (Recipient recipient : recipients) { diff --git a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java index ae6cee56a1..9959fb9896 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java @@ -9,6 +9,7 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.crypto.MasterSecretUtil; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; @@ -85,7 +86,7 @@ public class SmsReceiveJob extends ContextJob { private boolean isBlocked(IncomingTextMessage message) { if (message.getSender() != null) { - Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, false); return recipients.isBlocked(); } @@ -114,7 +115,7 @@ public class SmsReceiveJob extends ContextJob { List messages = new LinkedList<>(); for (Object pdu : pdus) { - messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[])pdu), subscriptionId)); + messages.add(new IncomingTextMessage(context, SmsMessage.createFromPdu((byte[])pdu), subscriptionId)); } if (messages.isEmpty()) { diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java index 7b67c8fc7b..62c77d3bc5 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java @@ -80,7 +80,7 @@ public class SmsSendJob extends SendJob { throw new UndeliverableMessageException("Trying to send a secure SMS?"); } - String recipient = message.getIndividualRecipient().getNumber(); + String recipient = message.getIndividualRecipient().getAddress().serialize(); // See issue #1516 for bug report, and discussion on commits related to #4833 for problems // related to the original fix to #1516. This still may not be a correct fix if networks allow diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java index c4a24e18c4..5d2c7ebb38 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java @@ -7,8 +7,6 @@ import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.SecurityEvent; -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; @@ -17,7 +15,6 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.service.SmsDeliveryListener; import org.whispersystems.jobqueue.JobParameters; -import org.whispersystems.libsignal.state.SessionStore; public class SmsSentJob extends MasterSecretJob { @@ -85,7 +82,7 @@ public class SmsSentJob extends MasterSecretJob { Log.w(TAG, "Service connectivity problem, requeuing..."); ApplicationContext.getInstance(context) .getJobManager() - .add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getNumber())); + .add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getAddress().serialize())); break; default: database.markAsSentFailed(messageId); diff --git a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java index ee464b518f..9eae899683 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -1,8 +1,11 @@ package org.thoughtcrime.securesms.mms; +import android.content.Context; + import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.PointerAttachment; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.MmsAddresses; import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.libsignal.util.guava.Optional; @@ -14,25 +17,25 @@ import java.util.List; public class IncomingMediaMessage { - private final String from; + private final Address from; private final String body; - private final String groupId; + private final Address groupId; private final boolean push; private final long sentTimeMillis; private final int subscriptionId; private final long expiresIn; private final boolean expirationUpdate; - private final List to = new LinkedList<>(); - private final List cc = new LinkedList<>(); + private final List
to = new LinkedList<>(); + private final List
cc = new LinkedList<>(); private final List attachments = new LinkedList<>(); - public IncomingMediaMessage(String from, List to, List cc, + public IncomingMediaMessage(Context context, String from, List to, List cc, String body, long sentTimeMillis, List attachments, int subscriptionId, long expiresIn, boolean expirationUpdate) { - this.from = from; + this.from = Address.fromExternal(context, from); this.sentTimeMillis = sentTimeMillis; this.body = body; this.groupId = null; @@ -41,14 +44,20 @@ public class IncomingMediaMessage { this.expiresIn = expiresIn; this.expirationUpdate = expirationUpdate; - this.to.addAll(to); - this.cc.addAll(cc); + for (String destination : to) { + this.to.add(Address.fromExternal(context, destination)); + } + + for (String destination : cc) { + this.cc.add(Address.fromExternal(context, destination)); + } + this.attachments.addAll(attachments); } public IncomingMediaMessage(MasterSecretUnion masterSecret, - String from, - String to, + Address from, + Address to, long sentTimeMillis, int subscriptionId, long expiresIn, @@ -66,7 +75,7 @@ public class IncomingMediaMessage { this.expiresIn = expiresIn; this.expirationUpdate = expirationUpdate; - if (group.isPresent()) this.groupId = GroupUtil.getEncodedId(group.get().getGroupId()); + if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId())); else this.groupId = null; this.to.add(to); @@ -82,14 +91,14 @@ public class IncomingMediaMessage { } public MmsAddresses getAddresses() { - return new MmsAddresses(from, to, cc, new LinkedList()); + return new MmsAddresses(from, to, cc, new LinkedList
()); } public List getAttachments() { return attachments; } - public String getGroupId() { + public Address getGroupId() { return groupId; } diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java index 346e986d67..92d0b08464 100644 --- a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -17,7 +17,6 @@ package org.thoughtcrime.securesms.notifications; -import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; @@ -27,10 +26,9 @@ import android.support.v4.app.RemoteInput; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -48,11 +46,11 @@ import java.util.List; */ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver { - public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName(); - public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY"; - public static final String RECIPIENT_IDS_EXTRA = "car_recipient_ids"; - public static final String VOICE_REPLY_KEY = "car_voice_reply_key"; - public static final String THREAD_ID_EXTRA = "car_reply_thread_id"; + public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName(); + public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY"; + public static final String ADDRESSES_EXTRA = "car_addresses"; + public static final String VOICE_REPLY_KEY = "car_voice_reply_key"; + public static final String THREAD_ID_EXTRA = "car_reply_thread_id"; @Override protected void onReceive(final Context context, Intent intent, @@ -64,10 +62,10 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver { if (remoteInput == null) return; - final long[] recipientIds = intent.getLongArrayExtra(RECIPIENT_IDS_EXTRA); + final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA)); final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1); final CharSequence responseText = getMessageText(intent); - final Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false); + final Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false); if (responseText != null) { new AsyncTask() { @@ -76,7 +74,7 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver { long replyThreadId; - Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds); + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses); int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index 6f8f214281..a5006449d7 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -107,7 +107,7 @@ public class MessageNotifier { sendInThreadNotification(context, recipients); } else { Intent intent = new Intent(context, ConversationActivity.class); - intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java index 10ea5e0256..423d56132e 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java @@ -70,7 +70,7 @@ public class NotificationItem { public PendingIntent getPendingIntent(Context context) { Intent intent = new Intent(context, ConversationActivity.class); Recipients notifyRecipients = threadRecipients != null ? threadRecipients : recipients; - if (notifyRecipients != null) intent.putExtra("recipients", notifyRecipients.getIds()); + if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, notifyRecipients.getAddresses()); intent.putExtra("thread_id", threadId); intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationState.java b/src/org/thoughtcrime/securesms/notifications/NotificationState.java index 36fb3724e1..9f55d4d7a5 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationState.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationState.java @@ -121,7 +121,7 @@ public class NotificationState { Intent intent = new Intent(RemoteReplyReceiver.REPLY_ACTION); intent.setClass(context, RemoteReplyReceiver.class); intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); - intent.putExtra(RemoteReplyReceiver.RECIPIENT_IDS_EXTRA, recipients.getIds()); + intent.putExtra(RemoteReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses()); intent.setPackage(context.getPackageName()); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -134,7 +134,7 @@ public class NotificationState { intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.setClass(context, AndroidAutoReplyReceiver.class); intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); - intent.putExtra(AndroidAutoReplyReceiver.RECIPIENT_IDS_EXTRA, recipients.getIds()); + intent.putExtra(AndroidAutoReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(AndroidAutoReplyReceiver.THREAD_ID_EXTRA, (long)threads.toArray()[0]); intent.setPackage(context.getPackageName()); @@ -164,7 +164,7 @@ public class NotificationState { if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications! " + threads.size()); Intent intent = new Intent(context, ConversationPopupActivity.class); - intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); + intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]); intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); diff --git a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index d6cc314890..8bc28a24c1 100644 --- a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -26,6 +26,7 @@ import android.support.v4.app.RemoteInput; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; @@ -44,9 +45,9 @@ import java.util.List; */ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver { - public static final String TAG = RemoteReplyReceiver.class.getSimpleName(); - public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY"; - public static final String RECIPIENT_IDS_EXTRA = "recipient_ids"; + public static final String TAG = RemoteReplyReceiver.class.getSimpleName(); + public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY"; + public static final String ADDRESSES_EXTRA = "addresses"; @Override protected void onReceive(final Context context, Intent intent, @@ -58,7 +59,7 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver { if (remoteInput == null) return; - final long[] recipientIds = intent.getLongArrayExtra(RECIPIENT_IDS_EXTRA); + final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA)); final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_REMOTE_REPLY); if (masterSecret != null && responseText != null) { @@ -67,11 +68,11 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver { protected Void doInBackground(Void... params) { long threadId; - Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds); + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses); int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; - Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false); + Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false); if (recipients.isGroupRecipient()) { OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList(), System.currentTimeMillis(), subscriptionId, expiresIn, 0); threadId = MessageSender.send(context, masterSecret, reply, -1, false, null); diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index e76931db15..5218966d33 100644 --- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.notifications; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -19,6 +18,8 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.contacts.avatars.ContactColors; +import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.Slide; @@ -69,11 +70,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil .toConversationColor(context))); } else { setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal)); - setLargeIcon(Recipient.getUnknownRecipient() - .getContactPhoto() - .asDrawable(context, Recipient.getUnknownRecipient() - .getColor() - .toConversationColor(context))); + setLargeIcon(ContactPhotoFactory.getDefaultContactPhoto("Unknown") + .asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context))); } } diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 65f55981a9..5b0e64ec52 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -25,9 +25,9 @@ import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.ListenableFutureTask; import java.util.Collections; @@ -42,9 +42,8 @@ public class Recipient { private final Set listeners = Collections.newSetFromMap(new WeakHashMap()); - private final long recipientId; + private final @NonNull Address address; - private @NonNull String number; private @Nullable String name; private @Nullable String customLabel; private boolean stale; @@ -55,13 +54,11 @@ public class Recipient { @Nullable private MaterialColor color; - Recipient(long recipientId, - @NonNull String number, + Recipient(@NonNull Address address, @Nullable Recipient stale, @NonNull ListenableFutureTask future) { - this.recipientId = recipientId; - this.number = number; + this.address = address; this.contactPhoto = ContactPhotoFactory.getLoadingPhoto(); this.color = null; this.resolving = true; @@ -80,7 +77,6 @@ public class Recipient { if (result != null) { synchronized (Recipient.this) { Recipient.this.name = result.name; - Recipient.this.number = result.number; Recipient.this.contactUri = result.contactUri; Recipient.this.contactPhoto = result.avatar; Recipient.this.color = result.color; @@ -99,9 +95,8 @@ public class Recipient { }); } - Recipient(long recipientId, RecipientDetails details) { - this.recipientId = recipientId; - this.number = details.number; + Recipient(Address address, RecipientDetails details) { + this.address = address; this.contactUri = details.contactUri; this.name = details.name; this.contactPhoto = details.avatar; @@ -132,20 +127,16 @@ public class Recipient { notifyListeners(); } - public @NonNull String getNumber() { - return number; + public @NonNull Address getAddress() { + return address; } public @Nullable String getCustomLabel() { return customLabel; } - public long getRecipientId() { - return recipientId; - } - public boolean isGroupRecipient() { - return GroupUtil.isEncodedGroup(number); + return address.isGroup(); } public synchronized void addListener(RecipientModifiedListener listener) { @@ -157,17 +148,13 @@ public class Recipient { } public synchronized String toShortString() { - return (name == null ? number : name); + return (name == null ? address.serialize() : name); } public synchronized @NonNull ContactPhoto getContactPhoto() { return contactPhoto; } - public static Recipient getUnknownRecipient() { - return new Recipient(-1, new RecipientDetails("Unknown", "Unknown", null, null, - ContactPhotoFactory.getDefaultContactPhoto(null), null)); - } @Override public boolean equals(Object o) { @@ -176,12 +163,12 @@ public class Recipient { Recipient that = (Recipient) o; - return this.recipientId == that.recipientId; + return this.address.equals(that.address); } @Override public int hashCode() { - return 31 + (int)this.recipientId; + return this.address.hashCode(); } private void notifyListeners() { diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java index cfe9667a49..bf83a28607 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java @@ -19,16 +19,10 @@ package org.thoughtcrime.securesms.recipients; import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; -import android.text.TextUtils; -import org.thoughtcrime.securesms.database.CanonicalAddressDatabase; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; +import org.thoughtcrime.securesms.database.Address; import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; public class RecipientFactory { @@ -36,104 +30,32 @@ public class RecipientFactory { private static final RecipientProvider provider = new RecipientProvider(); - public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) { - if (TextUtils.isEmpty(recipientIds)) - return new Recipients(); - - return getRecipientsForIds(context, Util.split(recipientIds, " "), asynchronous); - } - public static @NonNull Recipients getRecipientsFor(Context context, Collection recipients, boolean asynchronous) { - long[] ids = new long[recipients.size()]; + Address[] addresses= new Address[recipients.size()]; int i = 0; for (Recipient recipient : recipients) { - ids[i++] = recipient.getRecipientId(); + addresses[i++] = recipient.getAddress(); } - return provider.getRecipients(context, ids, asynchronous); + return provider.getRecipients(context, addresses, asynchronous); } public static Recipients getRecipientsFor(Context context, Recipient recipient, boolean asynchronous) { - long[] ids = new long[1]; - ids[0] = recipient.getRecipientId(); + Address[] addresses = new Address[1]; + addresses[0] = recipient.getAddress(); - return provider.getRecipients(context, ids, asynchronous); + return provider.getRecipients(context, addresses, asynchronous); } - public @NonNull static Recipient getRecipientForId(Context context, long recipientId, boolean asynchronous) { - return provider.getRecipient(context, recipientId, asynchronous); + public static @NonNull Recipients getRecipientsFor(@NonNull Context context, @NonNull Address[] addresses, boolean asynchronous) { + if (addresses == null || addresses.length == 0) throw new AssertionError(addresses); + return provider.getRecipients(context, addresses, asynchronous); } - public @NonNull static Recipients getRecipientsForIds(Context context, long[] recipientIds, boolean asynchronous) { - return provider.getRecipients(context, recipientIds, asynchronous); - } - - public static @NonNull Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) { - StringTokenizer tokenizer = new StringTokenizer(rawText, ","); - List ids = new LinkedList<>(); - - while (tokenizer.hasMoreTokens()) { - Optional id = getRecipientIdFromNumber(context, tokenizer.nextToken()); - - if (id.isPresent()) { - ids.add(String.valueOf(id.get())); - } - } - - return getRecipientsForIds(context, ids, asynchronous); - } - - public static @NonNull Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List numbers, boolean asynchronous) { - List ids = new LinkedList<>(); - - for (String number : numbers) { - Optional id = getRecipientIdFromNumber(context, number); - - if (id.isPresent()) { - ids.add(String.valueOf(id.get())); - } - } - - return getRecipientsForIds(context, ids, asynchronous); - } - - private static @NonNull Recipients getRecipientsForIds(Context context, List idStrings, boolean asynchronous) { - long[] ids = new long[idStrings.size()]; - int i = 0; - - for (String id : idStrings) { - ids[i++] = Long.parseLong(id); - } - - return provider.getRecipients(context, ids, asynchronous); - } - - private static Optional getRecipientIdFromNumber(Context context, String number) { - number = number.trim(); - - if (number.isEmpty()) return Optional.absent(); - - if (hasBracketedNumber(number)) { - number = parseBracketedNumber(number); - } - - return Optional.of(CanonicalAddressDatabase.getInstance(context).getCanonicalAddressId(number)); - } - - private static boolean hasBracketedNumber(String recipient) { - int openBracketIndex = recipient.indexOf('<'); - - return (openBracketIndex != -1) && - (recipient.indexOf('>', openBracketIndex) != -1); - } - - private static String parseBracketedNumber(String recipient) { - int begin = recipient.indexOf('<'); - int end = recipient.indexOf('>', begin); - String value = recipient.substring(begin + 1, end); - - return value; + public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, boolean asynchronous) { + if (address == null) throw new AssertionError(address); + return provider.getRecipient(context, address, asynchronous); } public static void clearCache(Context context) { diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java index 8238cff6cf..15d10a922f 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; -import org.thoughtcrime.securesms.database.CanonicalAddressDatabase; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; @@ -66,45 +66,43 @@ class RecipientProvider { }; private static final Map STATIC_DETAILS = new HashMap() {{ - put("262966", new RecipientDetails("Amazon", "262966", null, null, + put("262966", new RecipientDetails("Amazon", null, null, ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_amazon), ContactColors.UNKNOWN_COLOR)); }}; - @NonNull Recipient getRecipient(Context context, long recipientId, boolean asynchronous) { - Recipient cachedRecipient = recipientCache.get(recipientId); + @NonNull Recipient getRecipient(Context context, Address address, boolean asynchronous) { + Recipient cachedRecipient = recipientCache.get(address); if (cachedRecipient != null && !cachedRecipient.isStale() && (asynchronous || !cachedRecipient.isResolving())) { return cachedRecipient; } - String number = CanonicalAddressDatabase.getInstance(context).getAddressFromId(recipientId); - if (asynchronous) { - cachedRecipient = new Recipient(recipientId, number, cachedRecipient, getRecipientDetailsAsync(context, recipientId, number)); + cachedRecipient = new Recipient(address, cachedRecipient, getRecipientDetailsAsync(context, address)); } else { - cachedRecipient = new Recipient(recipientId, getRecipientDetailsSync(context, recipientId, number)); + cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address)); } - recipientCache.set(recipientId, cachedRecipient); + recipientCache.set(address, cachedRecipient); return cachedRecipient; } - @NonNull Recipients getRecipients(Context context, long[] recipientIds, boolean asynchronous) { - Recipients cachedRecipients = recipientsCache.get(new RecipientIds(recipientIds)); + @NonNull Recipients getRecipients(Context context, Address[] recipientAddresses, boolean asynchronous) { + Recipients cachedRecipients = recipientsCache.get(new RecipientAddresses(recipientAddresses)); if (cachedRecipients != null && !cachedRecipients.isStale() && (asynchronous || !cachedRecipients.isResolving())) { return cachedRecipients; } List recipientList = new LinkedList<>(); - for (long recipientId : recipientIds) { - recipientList.add(getRecipient(context, recipientId, asynchronous)); + for (Address address : recipientAddresses) { + recipientList.add(getRecipient(context, address, asynchronous)); } - if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientIds)); - else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientIds)); + if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientAddresses)); + else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientAddresses)); - recipientsCache.set(new RecipientIds(recipientIds), cachedRecipients); + recipientsCache.set(new RecipientAddresses(recipientAddresses), cachedRecipients); return cachedRecipients; } @@ -113,14 +111,12 @@ class RecipientProvider { recipientsCache.reset(); } - private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, - final long recipientId, - final @NonNull String number) + private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, final @NonNull Address address) { Callable task = new Callable() { @Override public RecipientDetails call() throws Exception { - return getRecipientDetailsSync(context, recipientId, number); + return getRecipientDetailsSync(context, address); } }; @@ -129,15 +125,15 @@ class RecipientProvider { return future; } - private @NonNull RecipientDetails getRecipientDetailsSync(Context context, long recipientId, @NonNull String number) { - if (GroupUtil.isEncodedGroup(number)) return getGroupRecipientDetails(context, number); - else return getIndividualRecipientDetails(context, recipientId, number); + private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address) { + if (address.isGroup()) return getGroupRecipientDetails(context, address); + else return getIndividualRecipientDetails(context, address); } - private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, long recipientId, @NonNull String number) { - Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new long[]{recipientId}); + private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address) { + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new Address[]{address}); MaterialColor color = preferences.isPresent() ? preferences.get().getColor() : null; - Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); + Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, null, null, null); @@ -151,7 +147,7 @@ class RecipientProvider { Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""), name); - return new RecipientDetails(cursor.getString(0), resultNumber, cursor.getString(4), contactUri, contactPhoto, color); + return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, color); } else { Log.w(TAG, "resultNumber is null"); } @@ -161,14 +157,14 @@ class RecipientProvider { cursor.close(); } - if (STATIC_DETAILS.containsKey(number)) return STATIC_DETAILS.get(number); - else return new RecipientDetails(null, number, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), color); + if (STATIC_DETAILS.containsKey(address.toPhoneString())) return STATIC_DETAILS.get(address.toPhoneString()); + else return new RecipientDetails(null, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), color); } - private @NonNull RecipientDetails getGroupRecipientDetails(Context context, String groupId) { + private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId) { try { GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context) - .getGroup(GroupUtil.getDecodedId(groupId)); + .getGroup(GroupUtil.getDecodedId(groupId.toGroupString())); if (record != null) { ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar()); @@ -178,27 +174,27 @@ class RecipientProvider { title = context.getString(R.string.RecipientProvider_unnamed_group);; } - return new RecipientDetails(title, groupId, null, null, contactPhoto, null); + return new RecipientDetails(title, null, null, contactPhoto, null); } - return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), groupId, null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null); + return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null); } catch (IOException e) { Log.w("RecipientProvider", e); - return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), groupId, null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null); + return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null); } } - private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, long[] recipientIds) { + private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, Address[] addresses) { return DatabaseFactory.getRecipientPreferenceDatabase(context) - .getRecipientsPreferences(recipientIds) + .getRecipientsPreferences(addresses) .orNull(); } - private ListenableFutureTask getRecipientsPreferencesAsync(final Context context, final long[] recipientIds) { + private ListenableFutureTask getRecipientsPreferencesAsync(final Context context, final Address[] addresses) { ListenableFutureTask task = new ListenableFutureTask<>(new Callable() { @Override public RecipientsPreferences call() throws Exception { - return getRecipientsPreferencesSync(context, recipientIds); + return getRecipientsPreferencesSync(context, addresses); } }); @@ -209,52 +205,50 @@ class RecipientProvider { public static class RecipientDetails { @Nullable public final String name; - @NonNull public final String number; @Nullable public final String customLabel; @NonNull public final ContactPhoto avatar; @Nullable public final Uri contactUri; @Nullable public final MaterialColor color; - public RecipientDetails(@Nullable String name, @NonNull String number, - @Nullable String customLabel, @Nullable Uri contactUri, - @NonNull ContactPhoto avatar, @Nullable MaterialColor color) + public RecipientDetails(@Nullable String name, @Nullable String customLabel, + @Nullable Uri contactUri, @NonNull ContactPhoto avatar, + @Nullable MaterialColor color) { this.name = name; this.customLabel = customLabel; - this.number = number; this.avatar = avatar; this.contactUri = contactUri; this.color = color; } } - private static class RecipientIds { - private final long[] ids; + private static class RecipientAddresses { + private final Address[] addresses; - private RecipientIds(long[] ids) { - this.ids = ids; + private RecipientAddresses(Address[] addresses) { + this.addresses = addresses; } public boolean equals(Object other) { - if (other == null || !(other instanceof RecipientIds)) return false; - return Arrays.equals(this.ids, ((RecipientIds) other).ids); + if (other == null || !(other instanceof RecipientAddresses)) return false; + return Arrays.equals(this.addresses, ((RecipientAddresses) other).addresses); } public int hashCode() { - return Arrays.hashCode(ids); + return Arrays.hashCode(addresses); } } private static class RecipientCache { - private final Map cache = new LRUCache<>(1000); + private final Map cache = new LRUCache<>(1000); - public synchronized Recipient get(long recipientId) { - return cache.get(recipientId); + public synchronized Recipient get(Address address) { + return cache.get(address); } - public synchronized void set(long recipientId, Recipient recipient) { - cache.put(recipientId, recipient); + public synchronized void set(Address address, Recipient recipient) { + cache.put(address, recipient); } public synchronized void reset() { @@ -267,14 +261,14 @@ class RecipientProvider { private static class RecipientsCache { - private final Map cache = new LRUCache<>(1000); + private final Map cache = new LRUCache<>(1000); - public synchronized Recipients get(RecipientIds ids) { - return cache.get(ids); + public synchronized Recipients get(RecipientAddresses addresses) { + return cache.get(addresses); } - public synchronized void set(RecipientIds ids, Recipients recipients) { - cache.put(ids, recipients); + public synchronized void set(RecipientAddresses addresses, Recipients recipients) { + cache.put(addresses, recipients); } public synchronized void reset() { diff --git a/src/org/thoughtcrime/securesms/recipients/Recipients.java b/src/org/thoughtcrime/securesms/recipients/Recipients.java index 2faecc9600..7d83c95eba 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipients.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipients.java @@ -20,20 +20,17 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; -import android.util.Patterns; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.ListenableFutureTask; -import org.thoughtcrime.securesms.util.NumberUtil; -import org.thoughtcrime.securesms.util.Util; import java.util.Arrays; import java.util.Collections; @@ -217,15 +214,16 @@ public class Recipients implements Iterable, RecipientModifiedListene public boolean isEmailRecipient() { for (Recipient recipient : recipients) { - if (NumberUtil.isValidEmail(recipient.getNumber())) + if (recipient.getAddress().isEmail()) { return true; + } } return false; } public boolean isGroupRecipient() { - return isSingleRecipient() && GroupUtil.isEncodedGroup(recipients.get(0).getNumber()); + return isSingleRecipient() && recipients.get(0).getAddress().isGroup(); } public boolean isEmpty() { @@ -247,57 +245,20 @@ public class Recipients implements Iterable, RecipientModifiedListene return this.recipients; } - public long[] getIds() { - long[] ids = new long[recipients.size()]; - for (int i=0; i recipientSet = new HashSet<>(); - - for (Recipient recipient : this.recipients) { - recipientSet.add(recipient.getRecipientId()); - } - - long[] recipientArray = new long[recipientSet.size()]; - int i = 0; - - for (Long recipientId : recipientSet) { - recipientArray[i++] = recipientId; - } - - Arrays.sort(recipientArray); - - return Util.join(recipientArray, " "); - } - - public @NonNull String[] toNumberStringArray(boolean scrub) { - String[] recipientsArray = new String[recipients.size()]; - Iterator iterator = recipients.iterator(); - int i = 0; - - while (iterator.hasNext()) { - String number = iterator.next().getNumber(); - - if (scrub && number != null && - !Patterns.EMAIL_ADDRESS.matcher(number).matches() && - !GroupUtil.isEncodedGroup(number)) - { - number = number.replaceAll("[^0-9+]", ""); - } - - recipientsArray[i++] = number; - } - - return recipientsArray; - } - - public @NonNull List toNumberStringList(boolean scrub) { - List results = new LinkedList<>(); - Collections.addAll(results, toNumberStringArray(scrub)); + public List
getAddressesList() { + List
results = new LinkedList<>(); + Collections.addAll(results, getAddresses()); return results; } diff --git a/src/org/thoughtcrime/securesms/service/DirectShareService.java b/src/org/thoughtcrime/securesms/service/DirectShareService.java index 58f758b7ea..48ac80d34f 100644 --- a/src/org/thoughtcrime/securesms/service/DirectShareService.java +++ b/src/org/thoughtcrime/securesms/service/DirectShareService.java @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; +import android.os.Parcel; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.support.annotation.RequiresApi; @@ -48,17 +49,22 @@ public class DirectShareService extends ChooserTargetService { ThreadRecord record; while ((record = reader.getNext()) != null && results.size() < 10) { - Recipients recipients = RecipientFactory.getRecipientsForIds(this, record.getRecipients().getIds(), false); + Recipients recipients = RecipientFactory.getRecipientsFor(this, record.getRecipients().getAddresses(), false); String name = recipients.toShortString(); Drawable drawable = recipients.getContactPhoto().asDrawable(this, recipients.getColor().toConversationColor(this)); Bitmap avatar = BitmapUtil.createFromDrawable(drawable, 500, 500); + Parcel parcel = Parcel.obtain(); + parcel.writeTypedArray(recipients.getAddresses(), 0); + Bundle bundle = new Bundle(); bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getThreadId()); - bundle.putLongArray(ShareActivity.EXTRA_RECIPIENT_IDS, recipients.getIds()); + bundle.putByteArray(ShareActivity.EXTRA_ADDRESSES_MARSHALLED, parcel.marshall()); bundle.putInt(ShareActivity.EXTRA_DISTRIBUTION_TYPE, record.getDistributionType()); + bundle.setClassLoader(getClassLoader()); results.add(new ChooserTarget(name, Icon.createWithBitmap(avatar), 1.0f, componentName, bundle)); + parcel.recycle(); } return results; diff --git a/src/org/thoughtcrime/securesms/service/QuickResponseService.java b/src/org/thoughtcrime/securesms/service/QuickResponseService.java index e4c85d64e4..28197bb76b 100644 --- a/src/org/thoughtcrime/securesms/service/QuickResponseService.java +++ b/src/org/thoughtcrime/securesms/service/QuickResponseService.java @@ -9,6 +9,7 @@ import android.widget.Toast; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.ThreadDatabase; @@ -49,12 +50,20 @@ public class QuickResponseService extends MasterSecretIntentService { Rfc5724Uri uri = new Rfc5724Uri(intent.getDataString()); String content = intent.getStringExtra(Intent.EXTRA_TEXT); String numbers = uri.getPath(); - if(numbers.contains("%")){ + + if (numbers.contains("%")){ numbers = URLDecoder.decode(numbers); } - Recipients recipients = RecipientFactory.getRecipientsFromString(this, numbers, false); - Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getIds()); + String[] numbersArray = numbers.split(","); + Address[] addresses = new Address[numbersArray.length]; + + for (int i=0;i preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getAddresses()); int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java index 1c687f43f2..b7359528dc 100644 --- a/src/org/thoughtcrime/securesms/service/RegistrationService.java +++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java @@ -16,12 +16,11 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.PreKeyUtil; import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.jobs.GcmRefreshJob; import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; @@ -237,7 +236,7 @@ public class RegistrationService extends Service { throws IOException { setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number)); - Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient(); + Address self = Address.fromSerialized(number); IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this); List records = PreKeyUtil.generatePreKeys(this); SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, identityKey, true); @@ -257,7 +256,7 @@ public class RegistrationService extends Service { TextSecurePreferences.setWebsocketRegistered(this, true); - DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true); + DatabaseFactory.getIdentityDatabase(this).saveIdentity(self, identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true); DirectoryHelper.refreshDirectory(this, accountManager, number); DirectoryRefreshListener.schedule(this); diff --git a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java index b2d2f86f80..a8c0484a55 100644 --- a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -17,7 +17,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -27,6 +26,7 @@ import org.greenrobot.eventbus.EventBus; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory; @@ -34,7 +34,6 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -80,7 +79,6 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture; import java.io.IOException; @@ -112,7 +110,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo private static final String DATA_CHANNEL_NAME = "signaling"; - public static final String EXTRA_REMOTE_NUMBER = "remote_number"; + public static final String EXTRA_REMOTE_ADDRESS = "remote_address"; public static final String EXTRA_MUTE = "mute_value"; public static final String EXTRA_AVAILABLE = "enabled_value"; public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description"; @@ -347,7 +345,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo @Override public void onSuccessContinue(List result) { try { - boolean isSystemContact = ContactAccessor.getInstance().isSystemContact(WebRtcCallService.this, recipient.getNumber()); + boolean isSystemContact = ContactAccessor.getInstance().isSystemContact(WebRtcCallService.this, recipient.getAddress().serialize()); boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, !isSystemContact || isAlwaysTurn); @@ -399,7 +397,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo bluetoothStateManager.setWantsConnection(true); setCallInProgressNotification(TYPE_OUTGOING_RINGING, recipient); - DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(recipient.getNumber()); + DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(recipient.getAddress()); timeoutExecutor.schedule(new TimeoutRunnable(this.callId), 2, TimeUnit.MINUTES); @@ -620,7 +618,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); intent.setAction(ACTION_LOCAL_HANGUP); intent.putExtra(EXTRA_CALL_ID, intent.getLongExtra(EXTRA_CALL_ID, -1)); - intent.putExtra(EXTRA_REMOTE_NUMBER, intent.getStringExtra(EXTRA_REMOTE_NUMBER)); + intent.putExtra(EXTRA_REMOTE_ADDRESS, intent.getStringExtra(EXTRA_REMOTE_ADDRESS)); startService(intent); } @@ -652,7 +650,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo } private void insertMissedCall(@NonNull Recipient recipient, boolean signal) { - Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getNumber()); + Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); MessageNotifier.updateNotification(this, KeyCachingService.getMasterSecret(this), messageAndThreadId.second, signal); } @@ -667,14 +665,14 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo throw new AssertionError("assert"); } - DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getNumber()); + DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getAddress()); this.peerConnection.setAudioEnabled(true); this.peerConnection.setVideoEnabled(true); this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setConnected(Connected.newBuilder().setId(this.callId)).build().toByteArray()), false)); intent.putExtra(EXTRA_CALL_ID, callId); - intent.putExtra(EXTRA_REMOTE_NUMBER, recipient.getNumber()); + intent.putExtra(EXTRA_REMOTE_ADDRESS, recipient.getAddress()); handleCallConnected(intent); } @@ -691,7 +689,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); - DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getNumber()); + DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); this.terminate(); } @@ -923,13 +921,8 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo Callable callable = new Callable() { @Override public Boolean call() throws Exception { - try { - String number = Util.canonicalizeNumber(WebRtcCallService.this, recipient.getNumber()); - messageSender.sendCallMessage(new SignalServiceAddress(number), callMessage); - return true; - } catch (InvalidNumberException e) { - throw new IOException(e); - } + messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()), callMessage); + return true; } }; @@ -949,14 +942,10 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo /// private @NonNull Recipient getRemoteRecipient(Intent intent) { - String remoteNumber = intent.getStringExtra(EXTRA_REMOTE_NUMBER); - if (TextUtils.isEmpty(remoteNumber)) throw new AssertionError("No recipient in intent!"); + Address remoteAddress = intent.getParcelableExtra(EXTRA_REMOTE_ADDRESS); + if (remoteAddress == null) throw new AssertionError("No recipient in intent!"); - Recipients recipients = RecipientFactory.getRecipientsFromString(this, remoteNumber, true); - Recipient result = recipients.getPrimaryRecipient(); - - if (result == null) throw new AssertionError("Recipient lookup failed!"); - else return result; + return RecipientFactory.getRecipientFor(this, remoteAddress, true); } private long getCallId(Intent intent) { diff --git a/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java b/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java index 56fc399078..56cfc47f9f 100644 --- a/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java +++ b/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java @@ -1,11 +1,12 @@ package org.thoughtcrime.securesms.sms; +import org.thoughtcrime.securesms.database.Address; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; public class IncomingJoinedMessage extends IncomingTextMessage { - public IncomingJoinedMessage(String sender) { + public IncomingJoinedMessage(Address sender) { super(sender, 1, System.currentTimeMillis(), null, Optional.absent(), 0); } diff --git a/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java index befd029f15..ff392eb497 100644 --- a/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java +++ b/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java @@ -1,9 +1,13 @@ package org.thoughtcrime.securesms.sms; +import android.content.Context; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.telephony.SmsMessage; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; @@ -24,23 +28,24 @@ public class IncomingTextMessage implements Parcelable { return new IncomingTextMessage[size]; } }; + private static final String TAG = IncomingTextMessage.class.getSimpleName(); private final String message; - private final String sender; + private Address sender; private final int senderDeviceId; private final int protocol; private final String serviceCenterAddress; private final boolean replyPathPresent; private final String pseudoSubject; private final long sentTimestampMillis; - private final String groupId; + private final Address groupId; private final boolean push; private final int subscriptionId; private final long expiresInMillis; - public IncomingTextMessage(SmsMessage message, int subscriptionId) { + public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) { this.message = message.getDisplayMessageBody(); - this.sender = message.getDisplayOriginatingAddress(); + this.sender = Address.fromExternal(context, message.getDisplayOriginatingAddress()); this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; this.protocol = message.getProtocolIdentifier(); this.serviceCenterAddress = message.getServiceCenterAddress(); @@ -53,7 +58,7 @@ public class IncomingTextMessage implements Parcelable { this.push = false; } - public IncomingTextMessage(String sender, int senderDeviceId, long sentTimestampMillis, + public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis, String encodedBody, Optional group, long expiresInMillis) { @@ -70,7 +75,7 @@ public class IncomingTextMessage implements Parcelable { this.expiresInMillis = expiresInMillis; if (group.isPresent()) { - this.groupId = GroupUtil.getEncodedId(group.get().getGroupId()); + this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId())); } else { this.groupId = null; } @@ -78,14 +83,14 @@ public class IncomingTextMessage implements Parcelable { public IncomingTextMessage(Parcel in) { this.message = in.readString(); - this.sender = in.readString(); + this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader()); this.senderDeviceId = in.readInt(); this.protocol = in.readInt(); this.serviceCenterAddress = in.readString(); this.replyPathPresent = (in.readInt() == 1); this.pseudoSubject = in.readString(); this.sentTimestampMillis = in.readLong(); - this.groupId = in.readString(); + this.groupId = in.readParcelable(IncomingTextMessage.class.getClassLoader()); this.push = (in.readInt() == 1); this.subscriptionId = in.readInt(); this.expiresInMillis = in.readLong(); @@ -127,7 +132,7 @@ public class IncomingTextMessage implements Parcelable { this.expiresInMillis = fragments.get(0).getExpiresIn(); } - protected IncomingTextMessage(String sender, String groupId) + protected IncomingTextMessage(@NonNull Address sender, @Nullable Address groupId) { this.message = ""; this.sender = sender; @@ -167,7 +172,7 @@ public class IncomingTextMessage implements Parcelable { return new IncomingTextMessage(this, message); } - public String getSender() { + public Address getSender() { return sender; } @@ -211,7 +216,7 @@ public class IncomingTextMessage implements Parcelable { return push; } - public String getGroupId() { + public @Nullable Address getGroupId() { return groupId; } @@ -243,14 +248,14 @@ public class IncomingTextMessage implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeString(message); - out.writeString(sender); + out.writeParcelable(sender, flags); out.writeInt(senderDeviceId); out.writeInt(protocol); out.writeString(serviceCenterAddress); out.writeInt(replyPathPresent ? 1 : 0); out.writeString(pseudoSubject); out.writeLong(sentTimestampMillis); - out.writeString(groupId); + out.writeParcelable(groupId, flags); out.writeInt(push ? 1 : 0); out.writeInt(subscriptionId); } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index be4f99c91c..a29384161b 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -23,6 +23,7 @@ import android.util.Pair; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; @@ -36,24 +37,20 @@ import org.thoughtcrime.securesms.jobs.PushGroupSendJob; import org.thoughtcrime.securesms.jobs.PushMediaSendJob; import org.thoughtcrime.securesms.jobs.PushTextSendJob; import org.thoughtcrime.securesms.jobs.SmsSendJob; +import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobManager; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.push.ContactTokenDetails; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; -import org.thoughtcrime.securesms.mms.MmsException; - public class MessageSender { private static final String TAG = MessageSender.class.getSimpleName(); @@ -116,11 +113,11 @@ public class MessageSender { } } - public static void resendGroupMessage(Context context, MasterSecret masterSecret, MessageRecord messageRecord, long filterRecipientId) { + public static void resendGroupMessage(Context context, MasterSecret masterSecret, MessageRecord messageRecord, Address filterAddress) { if (!messageRecord.isMms()) throw new AssertionError("Not Group"); Recipients recipients = DatabaseFactory.getMmsAddressDatabase(context).getRecipientsForId(messageRecord.getId()); - sendGroupPush(context, recipients, messageRecord.getId(), filterRecipientId); + sendGroupPush(context, recipients, messageRecord.getId(), filterAddress); } public static void resend(Context context, MasterSecret masterSecret, MessageRecord messageRecord) { @@ -150,7 +147,7 @@ public class MessageSender { if (!forceSms && isSelfSend(context, recipients)) { sendMediaSelf(context, masterSecret, messageId, expiresIn); } else if (isGroupPushSend(recipients)) { - sendGroupPush(context, recipients, messageId, -1); + sendGroupPush(context, recipients, messageId, null); } else if (!forceSms && isPushMediaSend(context, recipients)) { sendMediaPush(context, recipients, messageId); } else { @@ -205,17 +202,17 @@ public class MessageSender { private static void sendTextPush(Context context, Recipients recipients, long messageId) { JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new PushTextSendJob(context, messageId, recipients.getPrimaryRecipient().getNumber())); + jobManager.add(new PushTextSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress())); } private static void sendMediaPush(Context context, Recipients recipients, long messageId) { JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new PushMediaSendJob(context, messageId, recipients.getPrimaryRecipient().getNumber())); + jobManager.add(new PushMediaSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress())); } - private static void sendGroupPush(Context context, Recipients recipients, long messageId, long filterRecipientId) { + private static void sendGroupPush(Context context, Recipients recipients, long messageId, Address filterAddress) { JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new PushGroupSendJob(context, messageId, recipients.getPrimaryRecipient().getNumber(), filterRecipientId)); + jobManager.add(new PushGroupSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress(), filterAddress)); } private static void sendSms(Context context, Recipients recipients, long messageId) { @@ -229,47 +226,31 @@ public class MessageSender { } private static boolean isPushTextSend(Context context, Recipients recipients, boolean keyExchange) { - try { - if (!TextSecurePreferences.isPushRegistered(context)) { - return false; - } - - if (keyExchange) { - return false; - } - - Recipient recipient = recipients.getPrimaryRecipient(); - String destination = Util.canonicalizeNumber(context, recipient.getNumber()); - - return isPushDestination(context, destination); - } catch (InvalidNumberException e) { - Log.w(TAG, e); + if (!TextSecurePreferences.isPushRegistered(context)) { return false; } + + if (keyExchange) { + return false; + } + + return isPushDestination(context, recipients.getPrimaryRecipient().getAddress()); } private static boolean isPushMediaSend(Context context, Recipients recipients) { - try { - if (!TextSecurePreferences.isPushRegistered(context)) { - return false; - } - - if (recipients.getRecipientsList().size() > 1) { - return false; - } - - Recipient recipient = recipients.getPrimaryRecipient(); - String destination = Util.canonicalizeNumber(context, recipient.getNumber()); - - return isPushDestination(context, destination); - } catch (InvalidNumberException e) { - Log.w(TAG, e); + if (!TextSecurePreferences.isPushRegistered(context)) { return false; } + + if (recipients.getRecipientsList().size() > 1) { + return false; + } + + return isPushDestination(context, recipients.getPrimaryRecipient().getAddress()); } private static boolean isGroupPushSend(Recipients recipients) { - return GroupUtil.isEncodedGroup(recipients.getPrimaryRecipient().getNumber()); + return recipients.getPrimaryRecipient().getAddress().isGroup(); } private static boolean isSelfSend(Context context, Recipients recipients) { @@ -285,10 +266,10 @@ public class MessageSender { return false; } - return Util.isOwnNumber(context, recipients.getPrimaryRecipient().getNumber()); + return Util.isOwnNumber(context, recipients.getPrimaryRecipient().getAddress()); } - private static boolean isPushDestination(Context context, String destination) { + private static boolean isPushDestination(Context context, Address destination) { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); try { @@ -296,15 +277,15 @@ public class MessageSender { } catch (NotInDirectoryException e) { try { SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - Optional registeredUser = accountManager.getContact(destination); + Optional registeredUser = accountManager.getContact(destination.serialize()); if (!registeredUser.isPresent()) { registeredUser = Optional.of(new ContactTokenDetails()); - registeredUser.get().setNumber(destination); + registeredUser.get().setNumber(destination.serialize()); directory.setNumber(registeredUser.get(), false); return false; } else { - registeredUser.get().setNumber(destination); + registeredUser.get().setNumber(destination.toPhoneString()); directory.setNumber(registeredUser.get(), true); return true; } diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 76db8bafeb..34fb2489db 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -15,9 +15,10 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.notifications.MessageNotifier; @@ -28,7 +29,6 @@ import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capabili import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.push.ContactTokenDetails; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.util.Calendar; @@ -118,36 +118,31 @@ public class DirectoryHelper { @NonNull String localNumber) throws IOException { - try { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - String number = Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber()); - Optional details = accountManager.getContact(number); + TextSecureDirectory directory = TextSecureDirectory.getInstance(context); + SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); + String number = recipients.getPrimaryRecipient().getAddress().serialize(); + Optional details = accountManager.getContact(number); - if (details.isPresent()) { - directory.setNumber(details.get(), true); + if (details.isPresent()) { + directory.setNumber(details.get(), true); - RefreshResult result = updateContactsDatabase(context, localNumber, details.get()); + RefreshResult result = updateContactsDatabase(context, localNumber, details.get()); - if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) { - ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context)); - } - - if (!result.isFresh()) { - notifyNewUsers(context, masterSecret, result.getNewUsers()); - } - - return new UserCapabilities(Capability.SUPPORTED, - details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED, - details.get().isVideo() ? Capability.SUPPORTED : Capability.UNSUPPORTED); - } else { - ContactTokenDetails absent = new ContactTokenDetails(); - absent.setNumber(number); - directory.setNumber(absent, false); - return UserCapabilities.UNSUPPORTED; + if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) { + ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context)); } - } catch (InvalidNumberException e) { - Log.w(TAG, e); + + if (!result.isFresh()) { + notifyNewUsers(context, masterSecret, result.getNewUsers()); + } + + return new UserCapabilities(Capability.SUPPORTED, + details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED, + details.get().isVideo() ? Capability.SUPPORTED : Capability.UNSUPPORTED); + } else { + ContactTokenDetails absent = new ContactTokenDetails(); + absent.setNumber(number); + directory.setNumber(absent, false); return UserCapabilities.UNSUPPORTED; } } @@ -172,24 +167,14 @@ public class DirectoryHelper { return new UserCapabilities(Capability.SUPPORTED, Capability.UNSUPPORTED, Capability.UNSUPPORTED); } - final String number = recipients.getPrimaryRecipient().getNumber(); + final Address address = recipients.getPrimaryRecipient().getAddress(); - if (number == null) { - return UserCapabilities.UNSUPPORTED; - } + boolean secureText = TextSecureDirectory.getInstance(context).isSecureTextSupported(address); - String e164number = Util.canonicalizeNumber(context, number); - boolean secureText = TextSecureDirectory.getInstance(context).isSecureTextSupported(e164number); - boolean secureVoice = TextSecureDirectory.getInstance(context).isSecureVoiceSupported(e164number); - boolean secureVideo = TextSecureDirectory.getInstance(context).isSecureVideoSupported(e164number); + return new UserCapabilities(secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, + secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, + secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED); - return new UserCapabilities(secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, - secureVoice ? Capability.SUPPORTED : Capability.UNSUPPORTED, - secureVideo ? Capability.SUPPORTED : Capability.UNSUPPORTED); - - } catch (InvalidNumberException e) { - Log.w(TAG, e); - return UserCapabilities.UNSUPPORTED; } catch (NotInDirectoryException e) { return UserCapabilities.UNKNOWN; } @@ -231,7 +216,9 @@ public class DirectoryHelper { { if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return; - for (String newUser : newUsers) { + for (String newUserString : newUsers) { + Address newUser = Address.fromSerialized(newUserString); + if (!SessionUtil.hasSession(context, masterSecret, newUser) && !Util.isOwnNumber(context, newUser)) { IncomingJoinedMessage message = new IncomingJoinedMessage(newUser); Optional insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message); diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java index 6b35fe870e..ba971b793e 100644 --- a/src/org/thoughtcrime/securesms/util/GroupUtil.java +++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java @@ -6,11 +6,14 @@ import android.support.annotation.Nullable; import android.util.Log; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; @@ -62,7 +65,13 @@ public class GroupUtil { if (groupContext == null || groupContext.getMembersList().isEmpty()) { this.members = null; } else { - this.members = RecipientFactory.getRecipientsFromStrings(context, groupContext.getMembersList(), true); + List
adddresses = new LinkedList<>(); + + for (String member : groupContext.getMembersList()) { + adddresses.add(Address.fromExternal(context, member)); + } + + this.members = RecipientFactory.getRecipientsFor(context, adddresses.toArray(new Address[0]), true); } } diff --git a/src/org/thoughtcrime/securesms/util/IdentityUtil.java b/src/org/thoughtcrime/securesms/util/IdentityUtil.java index 214e8e6b34..f2ba49a35f 100644 --- a/src/org/thoughtcrime/securesms/util/IdentityUtil.java +++ b/src/org/thoughtcrime/securesms/util/IdentityUtil.java @@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase; @@ -39,7 +40,6 @@ import org.whispersystems.libsignal.state.SessionStore; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.util.List; @@ -57,7 +57,7 @@ public class IdentityUtil { @Override protected Optional doInBackground(Recipient... recipient) { return DatabaseFactory.getIdentityDatabase(context) - .getIdentity(recipient[0].getRecipientId()); + .getIdentity(recipient[0].getAddress()); } @Override @@ -78,29 +78,21 @@ public class IdentityUtil { Recipients recipients = RecipientFactory.getRecipientsFor(context, recipient, true); GroupDatabase.Reader reader = groupDatabase.getGroups(); - String number = recipient.getNumber(); - - try { - number = Util.canonicalizeNumber(context, number); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } - GroupDatabase.GroupRecord groupRecord; while ((groupRecord = reader.getNext()) != null) { - if (groupRecord.getMembers().contains(number) && groupRecord.isActive()) { + if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) { SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId()); if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(number, 1, time, null, Optional.of(group), 0); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming); smsDatabase.insertMessageInbox(incoming); } else { - Recipients groupRecipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), true); + Recipients groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromSerialized(GroupUtil.getEncodedId(group.getGroupId()))}, true); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients); OutgoingTextMessage outgoing ; @@ -113,7 +105,7 @@ public class IdentityUtil { } if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(number, 1, time, null, Optional.absent(), 0); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming); @@ -139,27 +131,19 @@ public class IdentityUtil { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase.Reader reader = groupDatabase.getGroups(); - String number = recipient.getNumber(); - - try { - number = Util.canonicalizeNumber(context, number); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } - GroupDatabase.GroupRecord groupRecord; while ((groupRecord = reader.getNext()) != null) { - if (groupRecord.getMembers().contains(number) && groupRecord.isActive()) { + if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) { SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId()); - IncomingTextMessage incoming = new IncomingTextMessage(number, 1, time, null, Optional.of(group), 0); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0); IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming); smsDatabase.insertMessageInbox(groupUpdate); } } - IncomingTextMessage incoming = new IncomingTextMessage(number, 1, time, null, Optional.absent(), 0); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0); IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming); Optional insertResult = smsDatabase.insertMessageInbox(individualUpdate); @@ -188,8 +172,8 @@ public class IdentityUtil { public static void processVerifiedMessage(Context context, MasterSecretUnion masterSecret, VerifiedMessage verifiedMessage) { synchronized (SESSION_LOCK) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - Recipient recipient = RecipientFactory.getRecipientsFromString(context, verifiedMessage.getDestination(), true).getPrimaryRecipient(); - Optional identityRecord = identityDatabase.getIdentity(recipient.getRecipientId()); + Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, verifiedMessage.getDestination()), true); + Optional identityRecord = identityDatabase.getIdentity(recipient.getAddress()); if (!identityRecord.isPresent() && verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT) { Log.w(TAG, "No existing record for default status"); @@ -201,7 +185,7 @@ public class IdentityUtil { identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey()) && identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.DEFAULT) { - identityDatabase.setVerified(recipient.getRecipientId(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT); + identityDatabase.setVerified(recipient.getAddress(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT); markIdentityVerified(context, masterSecret, recipient, false, true); } @@ -211,7 +195,7 @@ public class IdentityUtil { (identityRecord.isPresent() && identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.VERIFIED))) { saveIdentity(context, verifiedMessage.getDestination(), verifiedMessage.getIdentityKey()); - identityDatabase.setVerified(recipient.getRecipientId(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED); + identityDatabase.setVerified(recipient.getAddress(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED); markIdentityVerified(context, masterSecret, recipient, true, true); } } diff --git a/src/org/thoughtcrime/securesms/util/NumberUtil.java b/src/org/thoughtcrime/securesms/util/NumberUtil.java index 5cb3433451..9ab4f47567 100644 --- a/src/org/thoughtcrime/securesms/util/NumberUtil.java +++ b/src/org/thoughtcrime/securesms/util/NumberUtil.java @@ -34,25 +34,25 @@ public class NumberUtil { return PhoneNumberUtils.isWellFormedSmsAddress(number) || isValidEmail(number); } - public static boolean isValidSmsOrEmailOrGroup(String number) { - return PhoneNumberUtils.isWellFormedSmsAddress(number) || - isValidEmail(number) || - GroupUtil.isEncodedGroup(number); - } - - public static String filterNumber(String number) { - if (number == null) return null; - - int length = number.length(); - StringBuilder builder = new StringBuilder(length); - - for (int i = 0; i < length; i++) { - char character = number.charAt(i); - - if (Character.isDigit(character) || character == '+') - builder.append(character); - } - - return builder.toString(); - } +// public static boolean isValidSmsOrEmailOrGroup(String number) { +// return PhoneNumberUtils.isWellFormedSmsAddress(number) || +// isValidEmail(number) || +// GroupUtil.isEncodedGroup(number); +// } +// +// public static String filterNumber(String number) { +// if (number == null) return null; +// +// int length = number.length(); +// StringBuilder builder = new StringBuilder(length); +// +// for (int i = 0; i < length; i++) { +// char character = number.charAt(i); +// +// if (Character.isDigit(character) || character == '+') +// builder.append(character); +// } +// +// return builder.toString(); +// } } diff --git a/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java b/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java index ae49d41a62..0ac37ca6ce 100644 --- a/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java +++ b/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java @@ -106,7 +106,7 @@ public class SelectedRecipientsAdapter extends BaseAdapter { ImageButton delete = (ImageButton) v.findViewById(R.id.delete); name.setText(p.getName()); - phone.setText(p.getNumber()); + phone.setText(p.getAddress().serialize()); delete.setVisibility(modifiable ? View.VISIBLE : View.GONE); delete.setOnClickListener(new View.OnClickListener() { @Override diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index 453afbc85e..e45b0add60 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -45,8 +45,8 @@ import com.google.android.mms.pdu_alt.EncodedStringValue; import com.google.i18n.phonenumbers.PhoneNumberUtil; import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; -import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.ByteArrayOutputStream; @@ -72,6 +72,20 @@ public class Util { public static Handler handler = new Handler(Looper.getMainLooper()); + public static String join(List
list, String delimiter) { + return join(list.toArray(new Address[0]), delimiter); + } + + public static String join(Address[] list, String delimiter) { + List stringList = new LinkedList<>(); + + for (Address address : list) { + stringList.add(address.serialize()); + } + + return join(stringList, delimiter); + } + public static String join(String[] list, String delimiter) { return join(Arrays.asList(list), delimiter); } @@ -193,28 +207,11 @@ public class Util { return totalSize; } - public static String canonicalizeNumber(Context context, String number) - throws InvalidNumberException - { - String localNumber = TextSecurePreferences.getLocalNumber(context); - return PhoneNumberFormatter.formatNumber(number, localNumber); - } + public static boolean isOwnNumber(Context context, Address address) { + if (address.isGroup()) return false; + if (address.isEmail()) return false; - public static String canonicalizeNumberOrGroup(@NonNull Context context, @NonNull String number) - throws InvalidNumberException - { - if (GroupUtil.isEncodedGroup(number)) return number; - else return canonicalizeNumber(context, number); - } - - public static boolean isOwnNumber(Context context, String number) { - try { - String e164number = canonicalizeNumber(context, number); - return TextSecurePreferences.getLocalNumber(context).equals(e164number); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } - return false; + return TextSecurePreferences.getLocalNumber(context).equals(address.toPhoneString()); } public static byte[] readFully(InputStream in) throws IOException { diff --git a/src/org/thoughtcrime/securesms/util/VerifySpan.java b/src/org/thoughtcrime/securesms/util/VerifySpan.java index d450f6688d..697ff24dd0 100644 --- a/src/org/thoughtcrime/securesms/util/VerifySpan.java +++ b/src/org/thoughtcrime/securesms/util/VerifySpan.java @@ -8,31 +8,32 @@ import android.view.View; import org.thoughtcrime.securesms.VerifyIdentityActivity; import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.whispersystems.libsignal.IdentityKey; public class VerifySpan extends ClickableSpan { private final Context context; - private final long recipientId; + private final Address address; private final IdentityKey identityKey; public VerifySpan(@NonNull Context context, @NonNull IdentityKeyMismatch mismatch) { this.context = context; - this.recipientId = mismatch.getRecipientId(); + this.address = mismatch.getAddress(); this.identityKey = mismatch.getIdentityKey(); } - public VerifySpan(@NonNull Context context, long recipientId, @NonNull IdentityKey identityKey) { + public VerifySpan(@NonNull Context context, @NonNull Address address, @NonNull IdentityKey identityKey) { this.context = context; - this.recipientId = recipientId; + this.address = address; this.identityKey = identityKey; } @Override public void onClick(View widget) { Intent intent = new Intent(context, VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, recipientId); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, address); intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey)); intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); context.startActivity(intent); diff --git a/src/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java b/src/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java index a910e0798d..ddf7f217a5 100644 --- a/src/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java +++ b/src/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java @@ -8,6 +8,7 @@ import android.provider.ContactsContract; import android.text.TextUtils; import org.thoughtcrime.securesms.WebRtcCallActivity; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.service.WebRtcCallService; public class VoiceCallShare extends Activity { @@ -25,12 +26,13 @@ public class VoiceCallShare extends Activity { cursor = getContentResolver().query(getIntent().getData(), null, null, null, null); if (cursor != null && cursor.moveToNext()) { - String destination = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.Data.DATA1)); + String destination = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.Data.DATA1)); + Address address = Address.fromExternal(this, destination); if (!TextUtils.isEmpty(destination)) { Intent serviceIntent = new Intent(this, WebRtcCallService.class); serviceIntent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL); - serviceIntent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, destination); + serviceIntent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, address); startService(serviceIntent); Intent activityIntent = new Intent(this, WebRtcCallActivity.class); diff --git a/test/androidTest/java/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java b/test/androidTest/java/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java index a8fd6c3699..0906f27107 100644 --- a/test/androidTest/java/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java +++ b/test/androidTest/java/org/thoughtcrime/securesms/database/CanonicalAddressDatabaseTest.java @@ -1,119 +1,119 @@ -package org.thoughtcrime.securesms.database; - -import org.thoughtcrime.securesms.TextSecureTestCase; - -import static org.assertj.core.api.Assertions.assertThat; - -public class CanonicalAddressDatabaseTest extends TextSecureTestCase { - private static final String AMBIGUOUS_NUMBER = "222-3333"; - private static final String SPECIFIC_NUMBER = "+49 444 222 3333"; - private static final String EMAIL = "a@b.fom"; - private static final String SIMILAR_EMAIL = "a@b.com"; - private static final String GROUP = "__textsecure_group__!000111222333"; - private static final String SIMILAR_GROUP = "__textsecure_group__!100111222333"; - private static final String ALPHA = "T-Mobile"; - private static final String SIMILAR_ALPHA = "T-Mobila"; - - private CanonicalAddressDatabase db; - - @Override - public void setUp() { - super.setUp(); - this.db = CanonicalAddressDatabase.getInstance(getInstrumentation().getTargetContext()); - } - - public void tearDown() throws Exception { - - } - - /** - * Throw two equivalent numbers (one without locale info, one with full info) at the canonical - * address db and see that the caching and DB operations work properly in revealing the right - * addresses. This is run twice to ensure cache logic is hit. - * - * @throws Exception - */ - public void testNumberAddressUpdates() throws Exception { - final long id = db.getCanonicalAddressId(AMBIGUOUS_NUMBER); - - assertThat(db.getAddressFromId(id)).isEqualTo(AMBIGUOUS_NUMBER); - assertThat(db.getCanonicalAddressId(SPECIFIC_NUMBER)).isEqualTo(id); - assertThat(db.getAddressFromId(id)).isEqualTo(SPECIFIC_NUMBER); - assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); - - assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); - assertThat(db.getAddressFromId(id)).isEqualTo(AMBIGUOUS_NUMBER); - assertThat(db.getCanonicalAddressId(SPECIFIC_NUMBER)).isEqualTo(id); - assertThat(db.getAddressFromId(id)).isEqualTo(SPECIFIC_NUMBER); - assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); - } - - public void testSimilarNumbers() throws Exception { - assertThat(db.getCanonicalAddressId("This is a phone number 222-333-444")) - .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("222-333-444")) - .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("222-333-44")) - .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("222-333-4")) - .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("+49 222-333-4444")) - .isNotEqualTo(db.getCanonicalAddressId("+1 222-333-4444")); - - assertThat(db.getCanonicalAddressId("1 222-333-4444")) - .isEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("1 (222) 333-4444")) - .isEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("+12223334444")) - .isEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("+1 (222) 333.4444")) - .isEqualTo(db.getCanonicalAddressId("222-333-4444")); - assertThat(db.getCanonicalAddressId("+49 (222) 333.4444")) - .isEqualTo(db.getCanonicalAddressId("222-333-4444")); - - } - - public void testEmailAddresses() throws Exception { - final long emailId = db.getCanonicalAddressId(EMAIL); - final long similarEmailId = db.getCanonicalAddressId(SIMILAR_EMAIL); - - assertThat(emailId).isNotEqualTo(similarEmailId); - - assertThat(db.getAddressFromId(emailId)).isEqualTo(EMAIL); - assertThat(db.getAddressFromId(similarEmailId)).isEqualTo(SIMILAR_EMAIL); - } - - public void testGroups() throws Exception { - final long groupId = db.getCanonicalAddressId(GROUP); - final long similarGroupId = db.getCanonicalAddressId(SIMILAR_GROUP); - - assertThat(groupId).isNotEqualTo(similarGroupId); - - assertThat(db.getAddressFromId(groupId)).isEqualTo(GROUP); - assertThat(db.getAddressFromId(similarGroupId)).isEqualTo(SIMILAR_GROUP); - } - - public void testAlpha() throws Exception { - final long id = db.getCanonicalAddressId(ALPHA); - final long similarId = db.getCanonicalAddressId(SIMILAR_ALPHA); - - assertThat(id).isNotEqualTo(similarId); - - assertThat(db.getAddressFromId(id)).isEqualTo(ALPHA); - assertThat(db.getAddressFromId(similarId)).isEqualTo(SIMILAR_ALPHA); - } - - public void testIsNumber() throws Exception { - assertThat(CanonicalAddressDatabase.isNumberAddress("+495556666777")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("(222) 333-4444")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("1 (222) 333-4444")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile123")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("333-4444")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("12345")).isTrue(); - assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile")).isFalse(); - assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile1")).isFalse(); - assertThat(CanonicalAddressDatabase.isNumberAddress("Wherever bank")).isFalse(); - assertThat(CanonicalAddressDatabase.isNumberAddress("__textsecure_group__!afafafafafaf")).isFalse(); - assertThat(CanonicalAddressDatabase.isNumberAddress("email@domain.com")).isFalse(); - } -} \ No newline at end of file +//package org.thoughtcrime.securesms.database; +// +//import org.thoughtcrime.securesms.TextSecureTestCase; +// +//import static org.assertj.core.api.Assertions.assertThat; +// +//public class CanonicalAddressDatabaseTest extends TextSecureTestCase { +// private static final String AMBIGUOUS_NUMBER = "222-3333"; +// private static final String SPECIFIC_NUMBER = "+49 444 222 3333"; +// private static final String EMAIL = "a@b.fom"; +// private static final String SIMILAR_EMAIL = "a@b.com"; +// private static final String GROUP = "__textsecure_group__!000111222333"; +// private static final String SIMILAR_GROUP = "__textsecure_group__!100111222333"; +// private static final String ALPHA = "T-Mobile"; +// private static final String SIMILAR_ALPHA = "T-Mobila"; +// +// private CanonicalAddressDatabase db; +// +// @Override +// public void setUp() { +// super.setUp(); +// this.db = CanonicalAddressDatabase.getInstance(getInstrumentation().getTargetContext()); +// } +// +// public void tearDown() throws Exception { +// +// } +// +// /** +// * Throw two equivalent numbers (one without locale info, one with full info) at the canonical +// * address db and see that the caching and DB operations work properly in revealing the right +// * addresses. This is run twice to ensure cache logic is hit. +// * +// * @throws Exception +// */ +// public void testNumberAddressUpdates() throws Exception { +// final long id = db.getCanonicalAddressId(AMBIGUOUS_NUMBER); +// +// assertThat(db.getAddressFromId(id)).isEqualTo(AMBIGUOUS_NUMBER); +// assertThat(db.getCanonicalAddressId(SPECIFIC_NUMBER)).isEqualTo(id); +// assertThat(db.getAddressFromId(id)).isEqualTo(SPECIFIC_NUMBER); +// assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); +// +// assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); +// assertThat(db.getAddressFromId(id)).isEqualTo(AMBIGUOUS_NUMBER); +// assertThat(db.getCanonicalAddressId(SPECIFIC_NUMBER)).isEqualTo(id); +// assertThat(db.getAddressFromId(id)).isEqualTo(SPECIFIC_NUMBER); +// assertThat(db.getCanonicalAddressId(AMBIGUOUS_NUMBER)).isEqualTo(id); +// } +// +// public void testSimilarNumbers() throws Exception { +// assertThat(db.getCanonicalAddressId("This is a phone number 222-333-444")) +// .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("222-333-444")) +// .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("222-333-44")) +// .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("222-333-4")) +// .isNotEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("+49 222-333-4444")) +// .isNotEqualTo(db.getCanonicalAddressId("+1 222-333-4444")); +// +// assertThat(db.getCanonicalAddressId("1 222-333-4444")) +// .isEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("1 (222) 333-4444")) +// .isEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("+12223334444")) +// .isEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("+1 (222) 333.4444")) +// .isEqualTo(db.getCanonicalAddressId("222-333-4444")); +// assertThat(db.getCanonicalAddressId("+49 (222) 333.4444")) +// .isEqualTo(db.getCanonicalAddressId("222-333-4444")); +// +// } +// +// public void testEmailAddresses() throws Exception { +// final long emailId = db.getCanonicalAddressId(EMAIL); +// final long similarEmailId = db.getCanonicalAddressId(SIMILAR_EMAIL); +// +// assertThat(emailId).isNotEqualTo(similarEmailId); +// +// assertThat(db.getAddressFromId(emailId)).isEqualTo(EMAIL); +// assertThat(db.getAddressFromId(similarEmailId)).isEqualTo(SIMILAR_EMAIL); +// } +// +// public void testGroups() throws Exception { +// final long groupId = db.getCanonicalAddressId(GROUP); +// final long similarGroupId = db.getCanonicalAddressId(SIMILAR_GROUP); +// +// assertThat(groupId).isNotEqualTo(similarGroupId); +// +// assertThat(db.getAddressFromId(groupId)).isEqualTo(GROUP); +// assertThat(db.getAddressFromId(similarGroupId)).isEqualTo(SIMILAR_GROUP); +// } +// +// public void testAlpha() throws Exception { +// final long id = db.getCanonicalAddressId(ALPHA); +// final long similarId = db.getCanonicalAddressId(SIMILAR_ALPHA); +// +// assertThat(id).isNotEqualTo(similarId); +// +// assertThat(db.getAddressFromId(id)).isEqualTo(ALPHA); +// assertThat(db.getAddressFromId(similarId)).isEqualTo(SIMILAR_ALPHA); +// } +// +// public void testIsNumber() throws Exception { +// assertThat(CanonicalAddressDatabase.isNumberAddress("+495556666777")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("(222) 333-4444")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("1 (222) 333-4444")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile123")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("333-4444")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("12345")).isTrue(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile")).isFalse(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("T-Mobile1")).isFalse(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("Wherever bank")).isFalse(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("__textsecure_group__!afafafafafaf")).isFalse(); +// assertThat(CanonicalAddressDatabase.isNumberAddress("email@domain.com")).isFalse(); +// } +//} \ No newline at end of file