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