Switch MMS groups to use the group database infrastructure

Eliminate the concept of 'Recipients' (plural). There is now just
a 'Recipient', which contains an Address that is either an individual
or a group ID.

MMS groups now exist as part of the group database, just like push
groups.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-08-01 08:56:00 -07:00
parent 81682e0302
commit 375207f073
106 changed files with 1587 additions and 2192 deletions

View File

@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
@ -14,7 +14,7 @@ public interface BindableConversationItem extends Unbindable {
@NonNull MessageRecord messageRecord, @NonNull MessageRecord messageRecord,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,
@NonNull Recipients recipients); @NonNull Recipient recipients);
MessageRecord getMessageRecord(); MessageRecord getMessageRecord();
} }

View File

@ -20,13 +20,11 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader; import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
import org.thoughtcrime.securesms.preferences.BlockedContactListItem; import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import java.util.List;
public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity { public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme(); private final DynamicTheme dynamicTheme = new DynamicTheme();
@ -106,9 +104,9 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Recipients recipients = ((BlockedContactListItem)view).getRecipients(); Recipient recipient = ((BlockedContactListItem)view).getRecipient();
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class); Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
startActivity(intent); startActivity(intent);
} }
@ -127,12 +125,10 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void bindView(View view, Context context, Cursor cursor) { public void bindView(View view, Context context, Cursor cursor) {
String addressesConcat = cursor.getString(1); String address = cursor.getString(1);
List<Address> addresses = Address.fromSerializedList(addressesConcat, ' '); Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(address), true);
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), true); ((BlockedContactListItem) view).set(recipient);
((BlockedContactListItem) view).set(recipients);
} }
} }

View File

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsAddressDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.database.PushDatabase;
@ -24,7 +23,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.PushDecryptJob; import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.VerifySpan; import org.thoughtcrime.securesms.util.VerifySpan;
@ -138,17 +136,17 @@ public class ConfirmIdentityDialog extends AlertDialog {
private void processOutgoingMessageRecord(MessageRecord messageRecord) { private void processOutgoingMessageRecord(MessageRecord messageRecord) {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext()); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
MmsAddressDatabase mmsAddressDatabase = DatabaseFactory.getMmsAddressDatabase(getContext());
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(), mismatch.getAddress(),
mismatch.getIdentityKey()); mismatch.getIdentityKey());
Recipients recipients = mmsAddressDatabase.getRecipientsForId(messageRecord.getId()); if (messageRecord.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
if (recipients.isGroupRecipient()) MessageSender.resendGroupMessage(getContext(), masterSecret, messageRecord, mismatch.getAddress()); } else {
else MessageSender.resend(getContext(), masterSecret, messageRecord); MessageSender.resend(getContext(), masterSecret, messageRecord);
}
} else { } else {
smsDatabase.removeMismatchedIdentity(messageRecord.getId(), smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(), mismatch.getAddress(),

View File

@ -132,8 +132,7 @@ import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity; import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
@ -165,7 +164,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -184,7 +183,7 @@ import static org.whispersystems.signalservice.internal.push.SignalServiceProtos
public class ConversationActivity extends PassphraseRequiredActionBarActivity public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener, implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener, AttachmentManager.AttachmentListener,
RecipientsModifiedListener, RecipientModifiedListener,
OnKeyboardShownListener, OnKeyboardShownListener,
AttachmentDrawerListener, AttachmentDrawerListener,
InputPanel.Listener, InputPanel.Listener,
@ -192,7 +191,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
{ {
private static final String TAG = ConversationActivity.class.getSimpleName(); private static final String TAG = ConversationActivity.class.getSimpleName();
public static final String ADDRESSES_EXTRA = "addresses"; public static final String ADDRESS_EXTRA = "address";
public static final String THREAD_ID_EXTRA = "thread_id"; public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_ARCHIVED_EXTRA = "is_archived"; public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text"; public static final String TEXT_EXTRA = "draft_text";
@ -236,7 +235,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private QuickAttachmentDrawer quickAttachmentDrawer; private QuickAttachmentDrawer quickAttachmentDrawer;
private InputPanel inputPanel; private InputPanel inputPanel;
private Recipients recipients; private Recipient recipient;
private long threadId; private long threadId;
private int distributionType; private int distributionType;
private boolean archived; private boolean archived;
@ -325,9 +324,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeIdentityRecords(); initializeIdentityRecords();
composeText.setTransport(sendButton.getSelectedTransport()); composeText.setTransport(sendButton.getSelectedTransport());
titleView.setTitle(recipients); titleView.setTitle(recipient);
setActionBarColor(recipients.getColor()); setActionBarColor(recipient.getColor());
setBlockedUserState(recipients, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
calculateCharactersRemaining(); calculateCharactersRemaining();
MessageNotifier.setVisibleThread(threadId); MessageNotifier.setVisibleThread(threadId);
@ -371,7 +370,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected void onDestroy() { protected void onDestroy() {
saveDraft(); saveDraft();
if (recipients != null) recipients.removeListener(this); if (recipient != null) recipient.removeListener(this);
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver); if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
if (recipientsStaleReceiver != null) unregisterReceiver(recipientsStaleReceiver); if (recipientsStaleReceiver != null) unregisterReceiver(recipientsStaleReceiver);
super.onDestroy(); super.onDestroy();
@ -410,10 +409,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
addAttachmentContactInfo(data.getData()); addAttachmentContactInfo(data.getData());
break; break;
case GROUP_EDIT: case GROUP_EDIT:
recipients = RecipientFactory.getRecipientsFor(this, RecipientFactory.getRecipientFor(this, (Address)data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true), true); recipient = RecipientFactory.getRecipientFor(this, (Address)data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
recipients.addListener(this); recipient.addListener(this);
titleView.setTitle(recipients); titleView.setTitle(recipient);
setBlockedUserState(recipients, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
supportInvalidateOptionsMenu(); supportInvalidateOptionsMenu();
break; break;
case TAKE_PHOTO: case TAKE_PHOTO:
@ -422,8 +421,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
break; break;
case ADD_CONTACT: case ADD_CONTACT:
recipients = RecipientFactory.getRecipientsFor(this, recipients.getAddresses(), true); recipient = RecipientFactory.getRecipientFor(this, recipient.getAddress(), true);
recipients.addListener(this); recipient.addListener(this);
fragment.reloadList(); fragment.reloadList();
break; break;
case PICK_LOCATION: case PICK_LOCATION:
@ -462,14 +461,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
menu.clear(); menu.clear();
if (isSecureText) { if (isSecureText) {
if (recipients.getExpireMessages() > 0) { if (recipient.getExpireMessages() > 0) {
inflater.inflate(R.menu.conversation_expiring_on, menu); inflater.inflate(R.menu.conversation_expiring_on, menu);
final MenuItem item = menu.findItem(R.id.menu_expiring_messages); final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
final View actionView = MenuItemCompat.getActionView(item); final View actionView = MenuItemCompat.getActionView(item);
final TextView badgeView = (TextView)actionView.findViewById(R.id.expiration_badge); final TextView badgeView = (TextView)actionView.findViewById(R.id.expiration_badge);
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipients.getExpireMessages())); badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
actionView.setOnClickListener(new OnClickListener() { actionView.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -507,10 +506,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inflater.inflate(R.menu.conversation_insecure, menu); inflater.inflate(R.menu.conversation_insecure, menu);
} }
if (recipients != null && recipients.isMuted()) inflater.inflate(R.menu.conversation_muted, menu); if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
else inflater.inflate(R.menu.conversation_unmuted, menu); else inflater.inflate(R.menu.conversation_unmuted, menu);
if (isSingleConversation() && getRecipients().getPrimaryRecipient().getContactUri() == null) { if (isSingleConversation() && getRecipient().getContactUri() == null) {
inflater.inflate(R.menu.conversation_add_to_contacts, menu); inflater.inflate(R.menu.conversation_add_to_contacts, menu);
} }
@ -523,7 +522,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
super.onOptionsItemSelected(item); super.onOptionsItemSelected(item);
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_call_secure: case R.id.menu_call_secure:
case R.id.menu_call_insecure: handleDial(getRecipients().getPrimaryRecipient()); return true; case R.id.menu_call_insecure: handleDial(getRecipient()); return true;
case R.id.menu_add_attachment: handleAddAttachment(); return true; case R.id.menu_add_attachment: handleAddAttachment(); return true;
case R.id.menu_view_media: handleViewMedia(); return true; case R.id.menu_view_media: handleViewMedia(); return true;
case R.id.menu_add_to_contacts: handleAddToContacts(); return true; case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
@ -571,17 +570,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return; return;
} }
ExpirationDialog.show(this, recipients.getExpireMessages(), new ExpirationDialog.OnClickListener() { ExpirationDialog.show(this, recipient.getExpireMessages(), new ExpirationDialog.OnClickListener() {
@Override @Override
public void onClick(final int expirationTime) { public void onClick(final int expirationTime) {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.setExpireMessages(recipients, expirationTime); .setExpireMessages(recipient, expirationTime);
recipients.setExpireMessages(expirationTime); recipient.setExpireMessages(expirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipients(), System.currentTimeMillis(), expirationTime * 1000); OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000);
MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null); MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null);
return null; return null;
@ -601,13 +600,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MuteDialog.show(this, new MuteDialog.MuteSelectionListener() { MuteDialog.show(this, new MuteDialog.MuteSelectionListener() {
@Override @Override
public void onMuted(final long until) { public void onMuted(final long until) {
recipients.setMuted(until); recipient.setMuted(until);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.setMuted(recipients, until); .setMuted(recipient, until);
return null; return null;
} }
@ -621,13 +620,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void handleUnmuteNotifications() { private void handleUnmuteNotifications() {
recipients.setMuted(0); recipient.setMuted(0);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.setMuted(recipients, 0); .setMuted(recipient, 0);
return null; return null;
} }
@ -642,13 +641,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.setPositiveButton(R.string.ConversationActivity_unblock, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.ConversationActivity_unblock, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
recipients.setBlocked(false); recipient.setBlocked(false);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.setBlocked(recipients, false); .setBlocked(recipient, false);
ApplicationContext.getInstance(ConversationActivity.this) ApplicationContext.getInstance(ConversationActivity.this)
.getJobManager() .getJobManager()
@ -680,7 +679,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.appendInvite(inviteText); composeText.appendInvite(inviteText);
} else { } else {
Intent intent = new Intent(Intent.ACTION_SENDTO); Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:" + recipients.getPrimaryRecipient().getAddress().serialize())); intent.setData(Uri.parse("smsto:" + recipient.getAddress().serialize()));
intent.putExtra("sms_body", inviteText); intent.putExtra("sms_body", inviteText);
intent.putExtra(Intent.EXTRA_TEXT, inviteText); intent.putExtra(Intent.EXTRA_TEXT, inviteText);
startActivity(intent); startActivity(intent);
@ -703,7 +702,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final Context context = getApplicationContext(); final Context context = getApplicationContext();
OutgoingEndSessionMessage endSessionMessage = OutgoingEndSessionMessage endSessionMessage =
new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipients(), "TERMINATE", 0, -1)); new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1));
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() { new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
@Override @Override
@ -726,12 +725,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleViewMedia() { private void handleViewMedia() {
Intent intent = new Intent(this, MediaOverviewActivity.class); Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(MediaOverviewActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MediaOverviewActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
startActivity(intent); startActivity(intent);
} }
private void handleLeavePushGroup() { private void handleLeavePushGroup() {
if (getRecipients() == null) { if (getRecipient() == null) {
Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
return; return;
@ -747,15 +746,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
Context self = ConversationActivity.this; Context self = ConversationActivity.this;
try { try {
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString()); String groupId = getRecipient().getAddress().toGroupString();
DatabaseFactory.getGroupDatabase(self).setActive(groupId, false); DatabaseFactory.getGroupDatabase(self).setActive(groupId, false);
GroupContext context = GroupContext.newBuilder() GroupContext context = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId)) .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.QUIT) .setType(GroupContext.Type.QUIT)
.build(); .build();
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis(), 0); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0);
MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null); MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null);
DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self))); DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self)));
initializeEnabledCheck(); initializeEnabledCheck();
@ -772,7 +771,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() { private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class); Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress()); intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.getAddress());
startActivityForResult(intent, GROUP_EDIT); startActivityForResult(intent, GROUP_EDIT);
} }
@ -835,18 +834,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void handleDisplayGroupRecipients() { private void handleDisplayGroupRecipients() {
new GroupMembersDialog(this, getRecipients()).display(); new GroupMembersDialog(this, getRecipient()).display();
} }
private void handleAddToContacts() { private void handleAddToContacts() {
if (recipients.getPrimaryRecipient().getAddress().isGroup()) return; if (recipient.getAddress().isGroup()) return;
try { try {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
if (recipients.getPrimaryRecipient().getAddress().isEmail()) { if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipients.getPrimaryRecipient().getAddress().toEmailString()); intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
} else { } else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getAddress().toPhoneString()); intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
} }
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
startActivityForResult(intent, ADD_CONTACT); startActivityForResult(intent, ADD_CONTACT);
@ -928,19 +927,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
this.isSecureText = isSecureText; this.isSecureText = isSecureText;
this.isDefaultSms = isDefaultSms; this.isDefaultSms = isDefaultSms;
boolean isMediaMessage = !recipients.isSingleRecipient() || attachmentManager.isAttachmentPresent(); boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent();
sendButton.resetAvailableTransports(isMediaMessage); sendButton.resetAvailableTransports(isMediaMessage);
if (!isSecureText) sendButton.disableTransport(Type.TEXTSECURE); if (!isSecureText) sendButton.disableTransport(Type.TEXTSECURE);
if (recipients.isGroupRecipient()) sendButton.disableTransport(Type.SMS); if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS);
if (isSecureText) sendButton.setDefaultTransport(Type.TEXTSECURE); if (isSecureText) sendButton.setDefaultTransport(Type.TEXTSECURE);
else sendButton.setDefaultTransport(Type.SMS); else sendButton.setDefaultTransport(Type.SMS);
calculateCharactersRemaining(); calculateCharactersRemaining();
supportInvalidateOptionsMenu(); supportInvalidateOptionsMenu();
setBlockedUserState(recipients, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
} }
///// Initializers ///// Initializers
@ -1012,18 +1011,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms); handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms);
new AsyncTask<Recipients, Void, boolean[]>() { new AsyncTask<Recipient, Void, boolean[]>() {
@Override @Override
protected boolean[] doInBackground(Recipients... params) { protected boolean[] doInBackground(Recipient... params) {
Context context = ConversationActivity.this; Context context = ConversationActivity.this;
Recipients recipients = params[0]; Recipient recipient = params[0];
UserCapabilities capabilities = DirectoryHelper.getUserCapabilities(context, recipients); UserCapabilities capabilities = DirectoryHelper.getUserCapabilities(context, recipient);
if (capabilities.getTextCapability() == Capability.UNKNOWN || if (capabilities.getTextCapability() == Capability.UNKNOWN ||
capabilities.getVideoCapability() == Capability.UNKNOWN) capabilities.getVideoCapability() == Capability.UNKNOWN)
{ {
try { try {
capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients); capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -1040,7 +1039,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
future.set(true); future.set(true);
onSecurityUpdated(); onSecurityUpdated();
} }
}.execute(recipients); }.execute(recipient);
return future; return future;
} }
@ -1050,7 +1049,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void updateRecipientPreferences() { private void updateRecipientPreferences() {
new RecipientPreferencesTask().execute(recipients); new RecipientPreferencesTask().execute(recipient);
} }
protected void updateInviteReminder(boolean seenInvite) { protected void updateInviteReminder(boolean seenInvite) {
@ -1059,11 +1058,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
TextSecurePreferences.isShowInviteReminders(this) && TextSecurePreferences.isShowInviteReminders(this) &&
!isSecureText && !isSecureText &&
!seenInvite && !seenInvite &&
recipients.isSingleRecipient() && !recipient.isGroupRecipient())
recipients.getPrimaryRecipient() != null &&
recipients.getPrimaryRecipient().getContactUri() != null)
{ {
InviteReminder reminder = new InviteReminder(this, recipients); InviteReminder reminder = new InviteReminder(this, recipient);
reminder.setOkListener(new OnClickListener() { reminder.setOkListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -1099,34 +1096,32 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ListenableFuture<Boolean> initializeIdentityRecords() { private ListenableFuture<Boolean> initializeIdentityRecords() {
final SettableFuture<Boolean> future = new SettableFuture<>(); final SettableFuture<Boolean> future = new SettableFuture<>();
new AsyncTask<Recipients, Void, Pair<IdentityRecordList, String>>() { new AsyncTask<Recipient, Void, Pair<IdentityRecordList, String>>() {
@Override @Override
protected @NonNull Pair<IdentityRecordList, String> doInBackground(Recipients... params) { protected @NonNull Pair<IdentityRecordList, String> doInBackground(Recipient... params) {
try { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this);
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this); IdentityRecordList identityRecordList = new IdentityRecordList();
IdentityRecordList identityRecordList = new IdentityRecordList(); List<Recipient> recipients = new LinkedList<>();
Recipients recipients = params[0];
if (recipients.isGroupRecipient()) { if (params[0].isGroupRecipient()) {
recipients = DatabaseFactory.getGroupDatabase(ConversationActivity.this) recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
.getGroupMembers(GroupUtil.getDecodedId(recipients.getPrimaryRecipient().getAddress().toGroupString()), false); .getGroupMembers(params[0].getAddress().toGroupString(), false));
} } else {
recipients.add(params[0]);
for (Address recipientAddress : recipients.getAddresses()) {
Log.w(TAG, "Loading identity for: " + recipientAddress);
identityRecordList.add(identityDatabase.getIdentity(recipientAddress));
}
String message = null;
if (identityRecordList.isUnverified()) {
message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients(ConversationActivity.this));
}
return new Pair<>(identityRecordList, message);
} catch (IOException e) {
throw new AssertionError(e);
} }
for (Recipient recipient : recipients) {
Log.w(TAG, "Loading identity for: " + recipient.getAddress());
identityRecordList.add(identityDatabase.getIdentity(recipient.getAddress()));
}
String message = null;
if (identityRecordList.isUnverified()) {
message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients(ConversationActivity.this));
}
return new Pair<>(identityRecordList, message);
} }
@Override @Override
@ -1149,7 +1144,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
future.set(true); future.set(true);
} }
}.execute(recipients); }.execute(recipient);
return future; return future;
} }
@ -1212,7 +1207,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class); Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA,
isSecureText && !isSelfConversation()); isSecureText && !isSelfConversation());
@ -1257,9 +1252,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void initializeResources() { private void initializeResources() {
if (recipients != null) recipients.removeListener(this); if (recipient != null) recipient.removeListener(this);
recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); recipient = RecipientFactory.getRecipientFor(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false); archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
@ -1270,7 +1265,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
conversationContainer.setClipToPadding(true); conversationContainer.setClipToPadding(true);
} }
recipients.addListener(this); recipient.addListener(this);
} }
private void initializeProfiles() { private void initializeProfiles() {
@ -1281,18 +1276,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
ApplicationContext.getInstance(this) ApplicationContext.getInstance(this)
.getJobManager() .getJobManager()
.add(new RetrieveProfileJob(this, recipients)); .add(new RetrieveProfileJob(this, recipient));
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
titleView.post(new Runnable() { titleView.post(new Runnable() {
@Override @Override
public void run() { public void run() {
titleView.setTitle(recipients); titleView.setTitle(recipient);
titleView.setVerified(identityRecords.isVerified()); titleView.setVerified(identityRecords.isVerified());
setBlockedUserState(recipients, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipients.getColor()); setActionBarColor(recipient.getColor());
invalidateOptionsMenu(); invalidateOptionsMenu();
updateRecipientPreferences(); updateRecipientPreferences();
} }
@ -1301,8 +1296,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onRecipientPreferenceUpdate(final RecipientPreferenceEvent event) { public void onRecipientPreferenceUpdate(final RecipientPreferenceEvent event) {
if (Arrays.equals(event.getRecipients().getAddresses(), this.recipients.getAddresses())) { if (event.getRecipient().getAddress().equals(this.recipient.getAddress())) {
new RecipientPreferencesTask().execute(this.recipients); new RecipientPreferencesTask().execute(this.recipient);
} }
} }
@ -1324,11 +1319,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.w(TAG, "Group update received..."); Log.w(TAG, "Group update received...");
if (recipients != null) { if (recipient != null) {
Log.w(TAG, "Looking up new recipients..."); Log.w(TAG, "Looking up new recipients...");
recipients = RecipientFactory.getRecipientsFor(context, recipients.getAddresses(), true); recipient = RecipientFactory.getRecipientFor(context, recipient.getAddress(), true);
recipients.addListener(ConversationActivity.this); recipient.addListener(ConversationActivity.this);
onModified(recipients); onModified(recipient);
fragment.reloadList(); fragment.reloadList();
} }
} }
@ -1422,7 +1417,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected ListenableFuture<Long> saveDraft() { protected ListenableFuture<Long> saveDraft() {
final SettableFuture<Long> future = new SettableFuture<>(); final SettableFuture<Long> future = new SettableFuture<>();
if (this.recipients == null || this.recipients.isEmpty()) { if (this.recipient == null) {
future.set(threadId); future.set(threadId);
return future; return future;
} }
@ -1440,7 +1435,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long threadId = params[0]; long threadId = params[0];
if (drafts.size() > 0) { if (drafts.size() > 0) {
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipients(), thisDistributionType); if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType);
draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts); draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts);
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
@ -1468,8 +1463,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setStatusBarColor(color.toStatusBarColor(this)); setStatusBarColor(color.toStatusBarColor(this));
} }
private void setBlockedUserState(Recipients recipients, boolean isSecureText, boolean isDefaultSms) { private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
if (recipients.isBlocked()) { if (recipient.isBlocked()) {
unblockButton.setVisibility(View.VISIBLE); unblockButton.setVisibility(View.VISIBLE);
composePanel.setVisibility(View.GONE); composePanel.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.GONE); makeDefaultSmsButton.setVisibility(View.GONE);
@ -1499,42 +1494,33 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private boolean isSingleConversation() { private boolean isSingleConversation() {
return getRecipients() != null && getRecipients().isSingleRecipient() && !getRecipients().isGroupRecipient(); return getRecipient() != null && !getRecipient().isGroupRecipient();
} }
private boolean isActiveGroup() { private boolean isActiveGroup() {
if (!isGroupConversation()) return false; if (!isGroupConversation()) return false;
try { GroupRecord record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString()); return record != null && record.isActive();
GroupRecord record = DatabaseFactory.getGroupDatabase(this).getGroup(groupId);
return record != null && record.isActive();
} catch (IOException e) {
Log.w("ConversationActivity", e);
return false;
}
} }
private boolean isSelfConversation() { private boolean isSelfConversation() {
if (!TextSecurePreferences.isPushRegistered(this)) return false; if (!TextSecurePreferences.isPushRegistered(this)) return false;
if (!recipients.isSingleRecipient()) return false; if (recipient.isGroupRecipient()) return false;
if (recipients.getPrimaryRecipient().isGroupRecipient()) return false;
return Util.isOwnNumber(this, recipients.getPrimaryRecipient().getAddress()); return Util.isOwnNumber(this, recipient.getAddress());
} }
private boolean isGroupConversation() { private boolean isGroupConversation() {
return getRecipients() != null && return getRecipient() != null && getRecipient().isGroupRecipient();
(!getRecipients().isSingleRecipient() || getRecipients().isGroupRecipient());
} }
private boolean isPushGroupConversation() { private boolean isPushGroupConversation() {
return getRecipients() != null && getRecipients().isGroupRecipient(); return getRecipient() != null && getRecipient().isPushGroupRecipient();
} }
protected Recipients getRecipients() { protected Recipient getRecipient() {
return this.recipients; return this.recipient;
} }
protected long getThreadId() { protected long getThreadId() {
@ -1592,7 +1578,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
fragment.setLastSeen(0); fragment.setLastSeen(0);
if (refreshFragment) { if (refreshFragment) {
fragment.reload(recipients, threadId); fragment.reload(recipient, threadId);
MessageNotifier.setVisibleThread(threadId); MessageNotifier.setVisibleThread(threadId);
} }
@ -1602,26 +1588,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void sendMessage() { private void sendMessage() {
try { try {
Recipients recipients = getRecipients(); Recipient recipient = getRecipient();
if (recipients == null) { if (recipient == null) {
throw new RecipientFormattingException("Badly formatted"); throw new RecipientFormattingException("Badly formatted");
} }
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipients.getExpireMessages() * 1000; long expiresIn = recipient.getExpireMessages() * 1000;
Log.w(TAG, "isManual Selection: " + sendButton.isManualSelection()); Log.w(TAG, "isManual Selection: " + sendButton.isManualSelection());
Log.w(TAG, "forceSms: " + forceSms); Log.w(TAG, "forceSms: " + forceSms);
if ((!recipients.isSingleRecipient() || recipients.isEmailRecipient()) && !isMmsEnabled) { if ((!recipient.isMmsGroupRecipient() || recipient.getAddress().isEmail()) && !isMmsEnabled) {
handleManualMmsRequired(); handleManualMmsRequired();
} else if (!forceSms && identityRecords.isUnverified()) { } else if (!forceSms && identityRecords.isUnverified()) {
handleUnverifiedRecipients(); handleUnverifiedRecipients();
} else if (!forceSms && identityRecords.isUntrusted()) { } else if (!forceSms && identityRecords.isUntrusted()) {
handleUntrustedRecipients(); handleUntrustedRecipients();
} else if (attachmentManager.isAttachmentPresent() || !recipients.isSingleRecipient() || recipients.isGroupRecipient() || recipients.isEmailRecipient()) { } else if (attachmentManager.isAttachmentPresent() || recipient.isGroupRecipient() || recipient.getAddress().isEmail()) {
sendMediaMessage(forceSms, expiresIn, subscriptionId); sendMediaMessage(forceSms, expiresIn, subscriptionId);
} else { } else {
sendTextMessage(forceSms, expiresIn, subscriptionId); sendTextMessage(forceSms, expiresIn, subscriptionId);
@ -1649,7 +1635,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
{ {
final SettableFuture<Void> future = new SettableFuture<>(); final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext(); final Context context = getApplicationContext();
OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(recipients, OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(recipient,
slideDeck, slideDeck,
body, body,
System.currentTimeMillis(), System.currentTimeMillis(),
@ -1693,9 +1679,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
OutgoingTextMessage message; OutgoingTextMessage message;
if (isSecureText && !forceSms) { if (isSecureText && !forceSms) {
message = new OutgoingEncryptedMessage(recipients, getMessage(), expiresIn); message = new OutgoingEncryptedMessage(recipient, getMessage(), expiresIn);
} else { } else {
message = new OutgoingTextMessage(recipients, getMessage(), expiresIn, subscriptionId); message = new OutgoingTextMessage(recipient, getMessage(), expiresIn, subscriptionId);
} }
this.composeText.setText(""); this.composeText.setText("");
@ -1734,7 +1720,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.setDefaultSubscriptionId(recipients, subscriptionId.or(-1)); .setDefaultSubscriptionId(recipient, subscriptionId.or(-1));
return null; return null;
} }
}.execute(); }.execute();
@ -1796,7 +1782,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
try { try {
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipients.getExpireMessages() * 1000; long expiresIn = recipient.getExpireMessages() * 1000;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true); AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true);
SlideDeck slideDeck = new SlideDeck(); SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide); slideDeck.addSlide(audioSlide);
@ -1997,21 +1983,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateToggleButtonState(); updateToggleButtonState();
} }
private class RecipientPreferencesTask extends AsyncTask<Recipients, Void, Pair<Recipients,RecipientsPreferences>> { private class RecipientPreferencesTask extends AsyncTask<Recipient, Void, Pair<Recipient,RecipientsPreferences>> {
@Override @Override
protected Pair<Recipients, RecipientsPreferences> doInBackground(Recipients... recipients) { protected Pair<Recipient, RecipientsPreferences> doInBackground(Recipient... recipient) {
if (recipients.length != 1 || recipients[0] == null) { if (recipient.length != 1 || recipient[0] == null) {
throw new AssertionError("task needs exactly one Recipients object"); throw new AssertionError("task needs exactly one Recipients object");
} }
Optional<RecipientsPreferences> prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) Optional<RecipientsPreferences> prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.getRecipientsPreferences(recipients[0].getAddresses()); .getRecipientsPreferences(recipient[0].getAddress());
return new Pair<>(recipients[0], prefs.orNull()); return new Pair<>(recipient[0], prefs.orNull());
} }
@Override @Override
protected void onPostExecute(@NonNull Pair<Recipients, RecipientsPreferences> result) { protected void onPostExecute(@NonNull Pair<Recipient, RecipientsPreferences> result) {
if (result.first == recipients) { if (result.first == recipient) {
updateInviteReminder(result.second != null && result.second.hasSeenInviteReminder()); updateInviteReminder(result.second != null && result.second.hasSeenInviteReminder());
updateDefaultSubscriptionId(result.second != null ? result.second.getDefaultSubscriptionId() : Optional.<Integer>absent()); updateDefaultSubscriptionId(result.second != null ? result.second.getDefaultSubscriptionId() : Optional.<Integer>absent());
} }

View File

@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Conversions; import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.LRUCache;
@ -93,7 +93,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private final @Nullable ItemClickListener clickListener; private final @Nullable ItemClickListener clickListener;
private final @NonNull MasterSecret masterSecret; private final @NonNull MasterSecret masterSecret;
private final @NonNull Locale locale; private final @NonNull Locale locale;
private final @NonNull Recipients recipients; private final @NonNull Recipient recipient;
private final @NonNull MmsSmsDatabase db; private final @NonNull MmsSmsDatabase db;
private final @NonNull LayoutInflater inflater; private final @NonNull LayoutInflater inflater;
private final @NonNull Calendar calendar; private final @NonNull Calendar calendar;
@ -143,7 +143,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
this.masterSecret = null; this.masterSecret = null;
this.locale = null; this.locale = null;
this.clickListener = null; this.clickListener = null;
this.recipients = null; this.recipient = null;
this.inflater = null; this.inflater = null;
this.db = null; this.db = null;
this.calendar = null; this.calendar = null;
@ -158,7 +158,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@NonNull Locale locale, @NonNull Locale locale,
@Nullable ItemClickListener clickListener, @Nullable ItemClickListener clickListener,
@Nullable Cursor cursor, @Nullable Cursor cursor,
@NonNull Recipients recipients) @NonNull Recipient recipient)
{ {
super(context, cursor); super(context, cursor);
@ -166,7 +166,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.locale = locale; this.locale = locale;
this.clickListener = clickListener; this.clickListener = clickListener;
this.recipients = recipients; this.recipient = recipient;
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.db = DatabaseFactory.getMmsSmsDatabase(context); this.db = DatabaseFactory.getMmsSmsDatabase(context);
this.calendar = Calendar.getInstance(); this.calendar = Calendar.getInstance();
@ -188,7 +188,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@Override @Override
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) { protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipients); viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipient);
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start)); Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
} }

View File

@ -61,8 +61,8 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask;
@ -91,7 +91,7 @@ public class ConversationFragment extends Fragment
private ConversationFragmentListener listener; private ConversationFragmentListener listener;
private MasterSecret masterSecret; private MasterSecret masterSecret;
private Recipients recipients; private Recipient recipient;
private long threadId; private long threadId;
private long lastSeen; private long lastSeen;
private boolean firstLoad; private boolean firstLoad;
@ -184,7 +184,7 @@ public class ConversationFragment extends Fragment
} }
private void initializeResources() { private void initializeResources() {
this.recipients = RecipientFactory.getRecipientsFor(getActivity(), Address.fromParcelable(getActivity().getIntent().getParcelableArrayExtra(ConversationActivity.ADDRESSES_EXTRA)), true); this.recipient = RecipientFactory.getRecipientFor(getActivity(), (Address)getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
this.firstLoad = true; this.firstLoad = true;
@ -194,8 +194,8 @@ public class ConversationFragment extends Fragment
} }
private void initializeListAdapter() { private void initializeListAdapter() {
if (this.recipients != null && this.threadId != -1) { if (this.recipient != null && this.threadId != -1) {
ConversationAdapter adapter = new ConversationAdapter(getActivity(), masterSecret, locale, selectionClickListener, null, this.recipients); ConversationAdapter adapter = new ConversationAdapter(getActivity(), masterSecret, locale, selectionClickListener, null, this.recipient);
list.setAdapter(adapter); list.setAdapter(adapter);
list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false)); list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
@ -256,8 +256,8 @@ public class ConversationFragment extends Fragment
else throw new AssertionError(); else throw new AssertionError();
} }
public void reload(Recipients recipients, long threadId) { public void reload(Recipient recipient, long threadId) {
this.recipients = recipients; this.recipient = recipient;
if (this.threadId != threadId) { if (this.threadId != threadId) {
this.threadId = threadId; this.threadId = threadId;
@ -359,8 +359,8 @@ public class ConversationFragment extends Fragment
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId()); intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) && message.isPush()); intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.isGroupRecipient() && message.isPush());
startActivity(intent); startActivity(intent);
} }

View File

@ -70,7 +70,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
@ -97,7 +97,7 @@ import java.util.Set;
*/ */
public class ConversationItem extends LinearLayout public class ConversationItem extends LinearLayout
implements Recipient.RecipientModifiedListener, Recipients.RecipientsModifiedListener, BindableConversationItem implements RecipientModifiedListener, BindableConversationItem
{ {
private final static String TAG = ConversationItem.class.getSimpleName(); private final static String TAG = ConversationItem.class.getSimpleName();
@ -119,7 +119,7 @@ public class ConversationItem extends LinearLayout
private AlertView alertView; private AlertView alertView;
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>(); private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
private @Nullable Recipients conversationRecipients; private @Nullable Recipient conversationRecipient;
private @NonNull Stub<ThumbnailView> mediaThumbnailStub; private @NonNull Stub<ThumbnailView> mediaThumbnailStub;
private @NonNull Stub<AudioView> audioViewStub; private @NonNull Stub<AudioView> audioViewStub;
private @NonNull Stub<DocumentView> documentViewStub; private @NonNull Stub<DocumentView> documentViewStub;
@ -180,18 +180,18 @@ public class ConversationItem extends LinearLayout
@NonNull MessageRecord messageRecord, @NonNull MessageRecord messageRecord,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,
@NonNull Recipients conversationRecipients) @NonNull Recipient conversationRecipient)
{ {
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.messageRecord = messageRecord; this.messageRecord = messageRecord;
this.locale = locale; this.locale = locale;
this.batchSelected = batchSelected; this.batchSelected = batchSelected;
this.conversationRecipients = conversationRecipients; this.conversationRecipient = conversationRecipient;
this.groupThread = !conversationRecipients.isSingleRecipient() || conversationRecipients.isGroupRecipient(); this.groupThread = conversationRecipient.isGroupRecipient();
this.recipient = messageRecord.getIndividualRecipient(); this.recipient = messageRecord.getIndividualRecipient();
this.recipient.addListener(this); this.recipient.addListener(this);
this.conversationRecipients.addListener(this); this.conversationRecipient.addListener(this);
setMediaAttributes(messageRecord); setMediaAttributes(messageRecord);
setInteractionState(messageRecord); setInteractionState(messageRecord);
@ -241,35 +241,35 @@ public class ConversationItem extends LinearLayout
} }
if (audioViewStub.resolved()) { if (audioViewStub.resolved()) {
setAudioViewTint(messageRecord, conversationRecipients); setAudioViewTint(messageRecord, conversationRecipient);
} }
if (documentViewStub.resolved()) { if (documentViewStub.resolved()) {
setDocumentViewTint(messageRecord, conversationRecipients); setDocumentViewTint(messageRecord, conversationRecipient);
} }
} }
private void setAudioViewTint(MessageRecord messageRecord, Recipients recipients) { private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) {
if (messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
if (DynamicTheme.LIGHT.equals(TextSecurePreferences.getTheme(context))) { if (DynamicTheme.LIGHT.equals(TextSecurePreferences.getTheme(context))) {
audioViewStub.get().setTint(recipients.getColor().toConversationColor(context), defaultBubbleColor); audioViewStub.get().setTint(recipient.getColor().toConversationColor(context), defaultBubbleColor);
} else { } else {
audioViewStub.get().setTint(Color.WHITE, defaultBubbleColor); audioViewStub.get().setTint(Color.WHITE, defaultBubbleColor);
} }
} else { } else {
audioViewStub.get().setTint(Color.WHITE, recipients.getColor().toConversationColor(context)); audioViewStub.get().setTint(Color.WHITE, recipient.getColor().toConversationColor(context));
} }
} }
private void setDocumentViewTint(MessageRecord messageRecord, Recipients recipients) { private void setDocumentViewTint(MessageRecord messageRecord, Recipient recipient) {
if (messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
if (DynamicTheme.LIGHT.equals(TextSecurePreferences.getTheme(context))) { if (DynamicTheme.LIGHT.equals(TextSecurePreferences.getTheme(context))) {
documentViewStub.get().setTint(recipients.getColor().toConversationColor(context), defaultBubbleColor); documentViewStub.get().setTint(recipient.getColor().toConversationColor(context), defaultBubbleColor);
} else { } else {
documentViewStub.get().setTint(Color.WHITE, defaultBubbleColor); documentViewStub.get().setTint(Color.WHITE, defaultBubbleColor);
} }
} else { } else {
documentViewStub.get().setTint(Color.WHITE, recipients.getColor().toConversationColor(context)); documentViewStub.get().setTint(Color.WHITE, recipient.getColor().toConversationColor(context));
} }
} }
@ -535,16 +535,7 @@ public class ConversationItem extends LinearLayout
setBubbleState(messageRecord, recipient); setBubbleState(messageRecord, recipient);
setContactPhoto(recipient); setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient); setGroupMessageStatus(messageRecord, recipient);
} setAudioViewTint(messageRecord, recipient);
});
}
@Override
public void onModified(final Recipients recipients) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
setAudioViewTint(messageRecord, recipients);
} }
}); });
} }
@ -635,7 +626,7 @@ public class ConversationItem extends LinearLayout
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId()); intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush()); intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush());
intent.putExtra(MessageDetailsActivity.ADDRESSES_EXTRA, conversationRecipients.getAddresses()); intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
context.startActivity(intent); context.startActivity(intent);
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) { } else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
handleApproveIdentity(); handleApproveIdentity();

View File

@ -37,8 +37,8 @@ import org.thoughtcrime.securesms.components.RatingManager;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
@ -160,9 +160,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
} }
@Override @Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen) { public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
Intent intent = new Intent(this, ConversationActivity.class); Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis()); intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());

View File

@ -78,13 +78,8 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
@Override @Override
public long getItemId(@NonNull Cursor cursor) { public long getItemId(@NonNull Cursor cursor) {
ThreadRecord record = getThreadRecord(cursor); ThreadRecord record = getThreadRecord(cursor);
StringBuilder builder = new StringBuilder("" + record.getThreadId());
for (Address address : record.getRecipients().getAddresses()) { return Conversions.byteArrayToLong(digest.digest(record.getRecipient().getAddress().serialize().getBytes()));
builder.append("::").append(address.serialize());
}
return Conversions.byteArrayToLong(digest.digest(builder.toString().getBytes()));
} }
public ConversationListAdapter(@NonNull Context context, public ConversationListAdapter(@NonNull Context context,

View File

@ -6,7 +6,7 @@ import android.support.annotation.NonNull;
import android.view.MenuItem; import android.view.MenuItem;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
@ -54,9 +54,9 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
} }
@Override @Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeenTime) { public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeenTime) {
Intent intent = new Intent(this, ConversationActivity.class); Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true); intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);

View File

@ -69,7 +69,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader; import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask; import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
@ -309,8 +309,8 @@ public class ConversationListFragment extends Fragment
getListAdapter().getBatchSelections().size())); getListAdapter().getBatchSelections().size()));
} }
private void handleCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen) { private void handleCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipients, distributionType, lastSeen); ((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipient, distributionType, lastSeen);
} }
@Override @Override
@ -331,7 +331,7 @@ public class ConversationListFragment extends Fragment
@Override @Override
public void onItemClick(ConversationListItem item) { public void onItemClick(ConversationListItem item) {
if (actionMode == null) { if (actionMode == null) {
handleCreateConversation(item.getThreadId(), item.getRecipients(), handleCreateConversation(item.getThreadId(), item.getRecipient(),
item.getDistributionType(), item.getLastSeen()); item.getDistributionType(), item.getLastSeen());
} else { } else {
ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter(); ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter();
@ -363,7 +363,7 @@ public class ConversationListFragment extends Fragment
} }
public interface ConversationSelectedListener { public interface ConversationSelectedListener {
void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen); void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen);
void onSwitchToArchive(); void onSwitchToArchive();
} }

View File

@ -31,14 +31,15 @@ import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.components.AlertView;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView; import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.AlertView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ResUtil; import org.thoughtcrime.securesms.util.ResUtil;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
@ -56,7 +57,7 @@ import static org.thoughtcrime.securesms.util.SpanUtil.color;
*/ */
public class ConversationListItem extends RelativeLayout public class ConversationListItem extends RelativeLayout
implements Recipients.RecipientsModifiedListener, implements RecipientModifiedListener,
BindableConversationListItem, Unbindable BindableConversationListItem, Unbindable
{ {
private final static String TAG = ConversationListItem.class.getSimpleName(); private final static String TAG = ConversationListItem.class.getSimpleName();
@ -65,7 +66,7 @@ public class ConversationListItem extends RelativeLayout
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif-light", Typeface.NORMAL); private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif-light", Typeface.NORMAL);
private Set<Long> selectedThreads; private Set<Long> selectedThreads;
private Recipients recipients; private Recipient recipient;
private long threadId; private long threadId;
private TextView subjectView; private TextView subjectView;
private FromTextView fromView; private FromTextView fromView;
@ -116,14 +117,14 @@ public class ConversationListItem extends RelativeLayout
@NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode)
{ {
this.selectedThreads = selectedThreads; this.selectedThreads = selectedThreads;
this.recipients = thread.getRecipients(); this.recipient = thread.getRecipient();
this.threadId = thread.getThreadId(); this.threadId = thread.getThreadId();
this.read = thread.isRead(); this.read = thread.isRead();
this.distributionType = thread.getDistributionType(); this.distributionType = thread.getDistributionType();
this.lastSeen = thread.getLastSeen(); this.lastSeen = thread.getLastSeen();
this.recipients.addListener(this); this.recipient.addListener(this);
this.fromView.setText(recipients, read); this.fromView.setText(recipient, read);
this.subjectView.setText(thread.getDisplayBody()); this.subjectView.setText(thread.getDisplayBody());
this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE); this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
@ -144,21 +145,21 @@ public class ConversationListItem extends RelativeLayout
setThumbnailSnippet(masterSecret, thread); setThumbnailSnippet(masterSecret, thread);
setBatchState(batchMode); setBatchState(batchMode);
setBackground(thread); setBackground(thread);
setRippleColor(recipients); setRippleColor(recipient);
this.contactPhotoImage.setAvatar(recipients, true); this.contactPhotoImage.setAvatar(recipient, true);
} }
@Override @Override
public void unbind() { public void unbind() {
if (this.recipients != null) this.recipients.removeListener(this); if (this.recipient != null) this.recipient.removeListener(this);
} }
private void setBatchState(boolean batch) { private void setBatchState(boolean batch) {
setSelected(batch && selectedThreads.contains(threadId)); setSelected(batch && selectedThreads.contains(threadId));
} }
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
public long getThreadId() { public long getThreadId() {
@ -226,21 +227,21 @@ public class ConversationListItem extends RelativeLayout
} }
@TargetApi(VERSION_CODES.LOLLIPOP) @TargetApi(VERSION_CODES.LOLLIPOP)
private void setRippleColor(Recipients recipients) { private void setRippleColor(Recipient recipient) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
((RippleDrawable)(getBackground()).mutate()) ((RippleDrawable)(getBackground()).mutate())
.setColor(ColorStateList.valueOf(recipients.getColor().toConversationColor(getContext()))); .setColor(ColorStateList.valueOf(recipient.getColor().toConversationColor(getContext())));
} }
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
fromView.setText(recipients, read); fromView.setText(recipient, read);
contactPhotoImage.setAvatar(recipients, true); contactPhotoImage.setAvatar(recipient, true);
setRippleColor(recipients); setRippleColor(recipient);
} }
}); });
} }

View File

@ -84,7 +84,7 @@ public class ConversationPopupActivity extends ConversationActivity {
public void onSuccess(Long result) { public void onSuccess(Long result) {
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height); ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class); Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, getRecipients().getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, getRecipient().getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {

View File

@ -10,7 +10,6 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
public class ConversationTitleView extends LinearLayout { public class ConversationTitleView extends LinearLayout {
@ -42,14 +41,13 @@ public class ConversationTitleView extends LinearLayout {
ViewUtil.setTextViewGravityStart(this.subtitle, getContext()); ViewUtil.setTextViewGravityStart(this.subtitle, getContext());
} }
public void setTitle(@Nullable Recipients recipients) { public void setTitle(@Nullable Recipient recipient) {
if (recipients == null) setComposeTitle(); if (recipient == null) setComposeTitle();
else if (recipients.isSingleRecipient()) setRecipientTitle(recipients.getPrimaryRecipient()); else setRecipientTitle(recipient);
else setRecipientsTitle(recipients);
if (recipients != null && recipients.isBlocked()) { if (recipient != null && recipient.isBlocked()) {
title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_white_18dp, 0, 0, 0); title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_white_18dp, 0, 0, 0);
} else if (recipients != null && recipients.isMuted()) { } else if (recipient != null && recipient.isMuted()) {
title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_white_18dp, 0, 0, 0); title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_white_18dp, 0, 0, 0);
} else { } else {
title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
@ -86,17 +84,4 @@ public class ConversationTitleView extends LinearLayout {
this.subtitle.setVisibility(View.GONE); this.subtitle.setVisibility(View.GONE);
} }
} }
private void setRecipientsTitle(Recipients recipients) {
int size = recipients.getRecipientsList().size();
title.setText(getContext().getString(R.string.ConversationActivity_group_conversation));
subtitle.setText(getContext().getResources().getQuantityString(R.plurals.ConversationActivity_d_recipients_in_group, size, size));
subtitle.setVisibility(View.VISIBLE);
}
} }

View File

@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
@ -33,7 +33,7 @@ import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public class ConversationUpdateItem extends LinearLayout public class ConversationUpdateItem extends LinearLayout
implements Recipients.RecipientsModifiedListener, Recipient.RecipientModifiedListener, BindableConversationItem implements RecipientModifiedListener, BindableConversationItem
{ {
private static final String TAG = ConversationUpdateItem.class.getSimpleName(); private static final String TAG = ConversationUpdateItem.class.getSimpleName();
@ -71,7 +71,7 @@ public class ConversationUpdateItem extends LinearLayout
@NonNull MessageRecord messageRecord, @NonNull MessageRecord messageRecord,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,
@NonNull Recipients conversationRecipients) @NonNull Recipient conversationRecipient)
{ {
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.batchSelected = batchSelected; this.batchSelected = batchSelected;
@ -167,12 +167,7 @@ public class ConversationUpdateItem extends LinearLayout
body.setText(messageRecord.getDisplayBody()); body.setText(messageRecord.getDisplayBody());
date.setVisibility(View.GONE); date.setVisibility(View.GONE);
} }
@Override
public void onModified(Recipients recipients) {
onModified(recipients.getPrimaryRecipient());
}
@Override @Override
public void onModified(Recipient recipient) { public void onModified(Recipient recipient) {
Util.runOnMain(new Runnable() { Util.runOnMain(new Runnable() {

View File

@ -62,11 +62,9 @@ import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.mms.RoundedCorners; import org.thoughtcrime.securesms.mms.RoundedCorners;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter; import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener; import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -76,7 +74,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -214,17 +211,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private void initializeExistingGroup() { private void initializeExistingGroup() {
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA); final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
byte[] groupId; if (groupAddress != null) {
new FillExistingGroupInfoAsyncTask(this).execute(groupAddress.toGroupString());
try {
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;
}
if (groupId != null) {
new FillExistingGroupInfoAsyncTask(this).execute(groupId);
} }
} }
@ -261,8 +249,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
@Override @Override
public void onRecipientsPanelUpdate(Recipients recipients) { public void onRecipientsPanelUpdate(List<Recipient> recipients) {
if (recipients != null) addSelectedContacts(recipients.getRecipientsList()); if (recipients != null && !recipients.isEmpty()) addSelectedContacts(recipients);
} }
private void handleGroupCreate() { private void handleGroupCreate() {
@ -274,7 +262,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (isSignalGroup()) { if (isSignalGroup()) {
new CreateSignalGroupTask(this, masterSecret, avatarBmp, getGroupName(), getAdapter().getRecipients()).execute(); new CreateSignalGroupTask(this, masterSecret, avatarBmp, getGroupName(), getAdapter().getRecipients()).execute();
} else { } else {
new CreateMmsGroupTask(this, getAdapter().getRecipients()).execute(); new CreateMmsGroupTask(this, masterSecret, getAdapter().getRecipients()).execute();
} }
} }
@ -283,11 +271,11 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
getGroupName(), getAdapter().getRecipients()).execute(); getGroupName(), getAdapter().getRecipients()).execute();
} }
private void handleOpenConversation(long threadId, Recipients recipients) { private void handleOpenConversation(long threadId, Recipient recipient) {
Intent intent = new Intent(this, ConversationActivity.class); Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
startActivity(intent); startActivity(intent);
finish(); finish();
} }
@ -347,31 +335,35 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
} }
private static class CreateMmsGroupTask extends AsyncTask<Void,Void,Long> { private static class CreateMmsGroupTask extends AsyncTask<Void,Void,GroupActionResult> {
private GroupCreateActivity activity; private final GroupCreateActivity activity;
private Set<Recipient> members; private final MasterSecret masterSecret;
private final Set<Recipient> members;
public CreateMmsGroupTask(GroupCreateActivity activity, Set<Recipient> members) { public CreateMmsGroupTask(GroupCreateActivity activity, MasterSecret masterSecret, Set<Recipient> members) {
this.activity = activity; this.activity = activity;
this.members = members; this.masterSecret = masterSecret;
this.members = members;
} }
@Override @Override
protected Long doInBackground(Void... avoid) { protected GroupActionResult doInBackground(Void... avoid) {
Recipients recipients = RecipientFactory.getRecipientsFor(activity, members, false); List<Address> memberAddresses = new LinkedList<>();
return DatabaseFactory.getThreadDatabase(activity)
.getThreadIdFor(recipients, ThreadDatabase.DistributionTypes.CONVERSATION);
}
@Override for (Recipient recipient : members) {
protected void onPostExecute(Long resultThread) { memberAddresses.add(recipient.getAddress());
if (resultThread > -1) {
activity.handleOpenConversation(resultThread,
RecipientFactory.getRecipientsFor(activity, members, true));
} else {
Toast.makeText(activity, R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
activity.finish();
} }
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
Recipient groupRecipient = RecipientFactory.getRecipientFor(activity, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
return new GroupActionResult(groupRecipient, threadId);
}
@Override
protected void onPostExecute(GroupActionResult result) {
activity.handleOpenConversation(result.getThreadId(), result.getGroupRecipient());
} }
@Override @Override
@ -427,11 +419,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) { protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
try { return Optional.of(GroupManager.createGroup(activity, masterSecret, members, avatar, name, false));
return Optional.of(GroupManager.createGroup(activity, masterSecret, members, avatar, name));
} catch (InvalidNumberException e) {
return Optional.absent();
}
} }
@Override @Override
@ -449,10 +437,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
private static class UpdateSignalGroupTask extends SignalGroupTask { private static class UpdateSignalGroupTask extends SignalGroupTask {
private byte[] groupId; private String groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity, public UpdateSignalGroupTask(GroupCreateActivity activity,
MasterSecret masterSecret, byte[] groupId, Bitmap avatar, String name, MasterSecret masterSecret, String groupId, Bitmap avatar, String name,
Set<Recipient> members) Set<Recipient> members)
{ {
super(activity, masterSecret, avatar, name, members); super(activity, masterSecret, avatar, name, members);
@ -474,7 +462,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) { if (!activity.isFinishing()) {
Intent intent = activity.getIntent(); Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId()); intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getPrimaryRecipient().getAddress()); intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getAddress());
activity.setResult(RESULT_OK, intent); activity.setResult(RESULT_OK, intent);
activity.finish(); activity.finish();
} }
@ -541,7 +529,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
} }
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<byte[],Void,Optional<GroupData>> { private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<String,Void,Optional<GroupData>> {
private GroupCreateActivity activity; private GroupCreateActivity activity;
public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) { public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
@ -552,12 +540,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
@Override @Override
protected Optional<GroupData> doInBackground(byte[]... groupIds) { protected Optional<GroupData> doInBackground(String... groupIds) {
final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity); final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity);
final Recipients recipients = db.getGroupMembers(groupIds[0], false); final List<Recipient> recipients = db.getGroupMembers(groupIds[0], false);
final GroupRecord group = db.getGroup(groupIds[0]); final GroupRecord group = db.getGroup(groupIds[0]);
final Set<Recipient> existingContacts = new HashSet<>(recipients.getRecipientsList().size()); final Set<Recipient> existingContacts = new HashSet<>(recipients.size());
existingContacts.addAll(recipients.getRecipientsList()); existingContacts.addAll(recipients);
if (group != null) { if (group != null) {
return Optional.of(new GroupData(groupIds[0], return Optional.of(new GroupData(groupIds[0],
@ -600,13 +588,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
private static class GroupData { private static class GroupData {
byte[] id; String id;
Set<Recipient> recipients; Set<Recipient> recipients;
Bitmap avatarBmp; Bitmap avatarBmp;
byte[] avatarBytes; byte[] avatarBytes;
String name; String name;
public GroupData(byte[] id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) { public GroupData(String id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
this.id = id; this.id = id;
this.recipients = recipients; this.recipients = recipients;
this.avatarBmp = avatarBmp; this.avatarBmp = avatarBmp;

View File

@ -7,48 +7,36 @@ import android.graphics.Rect;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.util.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient; 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.Util; import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> { public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
private static final String TAG = GroupMembersDialog.class.getSimpleName(); private static final String TAG = GroupMembersDialog.class.getSimpleName();
private final Recipients recipients; private final Recipient recipient;
private final Context context; private final Context context;
public GroupMembersDialog(Context context, Recipients recipients) { public GroupMembersDialog(Context context, Recipient recipient) {
this.recipients = recipients; this.recipient = recipient;
this.context = context; this.context = context;
} }
@Override @Override
public void onPreExecute() {} public void onPreExecute() {}
@Override @Override
protected Recipients doInBackground(Void... params) { protected List<Recipient> doInBackground(Void... params) {
try { return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), true);
String groupId = recipients.getPrimaryRecipient().getAddress().toGroupString();
return DatabaseFactory.getGroupDatabase(context)
.getGroupMembers(GroupUtil.getDecodedId(groupId), true);
} catch (IOException e) {
Log.w(TAG, e);
return RecipientFactory.getRecipientsFor(context, new LinkedList<Recipient>(), true);
}
} }
@Override @Override
public void onPostExecute(Recipients members) { public void onPostExecute(List<Recipient> members) {
GroupMembers groupMembers = new GroupMembers(members); GroupMembers groupMembers = new GroupMembers(members);
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationActivity_group_members); builder.setTitle(R.string.ConversationActivity_group_members);
@ -60,8 +48,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
} }
public void display() { public void display() {
if (recipients.isGroupRecipient()) execute(); execute();
else onPostExecute(recipients);
} }
private static class GroupMembersOnClickListener implements DialogInterface.OnClickListener { private static class GroupMembersOnClickListener implements DialogInterface.OnClickListener {
@ -107,8 +94,8 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
private final LinkedList<Recipient> members = new LinkedList<>(); private final LinkedList<Recipient> members = new LinkedList<>();
public GroupMembers(Recipients recipients) { public GroupMembers(List<Recipient> recipients) {
for (Recipient recipient : recipients.getRecipientsList()) { for (Recipient recipient : recipients) {
if (isLocalNumber(recipient)) { if (isLocalNumber(recipient)) {
members.push(recipient); members.push(recipient);
} else { } else {

View File

@ -30,8 +30,8 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
@ -231,19 +231,17 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (context == null) return null; if (context == null) return null;
for (String number : numbers) { for (String number : numbers) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, number)}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, number), false);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipient.getAddress());
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
if (recipients.getPrimaryRecipient() != null) { MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);
Optional<RecipientsPreferences> 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); if (recipient.getContactUri() != null) {
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipient, true);
if (recipients.getPrimaryRecipient().getContactUri() != null) {
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
}
} }
} }
return null; return null;
} }

View File

@ -24,7 +24,6 @@ import android.database.Cursor;
import android.os.Build.VERSION; import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
@ -43,8 +42,9 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.AbstractCursorLoader; import org.thoughtcrime.securesms.util.AbstractCursorLoader;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask;
@ -59,7 +59,7 @@ import java.util.List;
public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor> { public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private final static String TAG = MediaOverviewActivity.class.getSimpleName(); private final static String TAG = MediaOverviewActivity.class.getSimpleName();
public static final String ADDRESSES_EXTRA = "addresses"; public static final String ADDRESS_EXTRA = "address";
public static final String THREAD_ID_EXTRA = "thread_id"; public static final String THREAD_ID_EXTRA = "thread_id";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -69,7 +69,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
private RecyclerView gridView; private RecyclerView gridView;
private GridLayoutManager gridManager; private GridLayoutManager gridManager;
private TextView noImages; private TextView noImages;
private Recipients recipients; private Recipient recipient;
private long threadId; private long threadId;
@Override @Override
@ -114,9 +114,9 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
} }
private void initializeActionBar() { private void initializeActionBar() {
getSupportActionBar().setTitle(recipients == null getSupportActionBar().setTitle(recipient == null
? getString(R.string.AndroidManifest__all_media) ? getString(R.string.AndroidManifest__all_media)
: getString(R.string.AndroidManifest__all_media_named, recipients.toShortString())); : getString(R.string.AndroidManifest__all_media_named, recipient.toShortString()));
} }
@Override @Override
@ -133,20 +133,20 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
gridView.setLayoutManager(gridManager); gridView.setLayoutManager(gridManager);
gridView.setHasFixedSize(true); gridView.setHasFixedSize(true);
Parcelable[] parcelables = getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA); Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
if (parcelables != null) { if (address != null) {
recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(parcelables), true); recipient = RecipientFactory.getRecipientFor(this, address, true);
} else if (threadId > -1) { } else if (threadId > -1) {
recipients = DatabaseFactory.getThreadDatabase(this).getRecipientsForThreadId(threadId); recipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId);
} else { } else {
recipients = null; recipient = null;
} }
if (recipients != null) { if (recipient != null) {
recipients.addListener(new Recipients.RecipientsModifiedListener() { recipient.addListener(new RecipientModifiedListener() {
@Override @Override
public void onModified(Recipients recipients) { public void onModified(Recipient recipients) {
initializeActionBar(); initializeActionBar();
} }
}); });

View File

@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;

View File

@ -47,26 +47,25 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.sql.Date; import java.sql.Date;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
* @author Jake McGinty * @author Jake McGinty
*/ */
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, Recipients.RecipientsModifiedListener { public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener {
private final static String TAG = MessageDetailsActivity.class.getSimpleName(); private final static String TAG = MessageDetailsActivity.class.getSimpleName();
public final static String MASTER_SECRET_EXTRA = "master_secret"; public final static String MASTER_SECRET_EXTRA = "master_secret";
@ -74,7 +73,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
public final static String THREAD_ID_EXTRA = "thread_id"; public final static String THREAD_ID_EXTRA = "thread_id";
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group"; public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
public final static String TYPE_EXTRA = "type"; public final static String TYPE_EXTRA = "type";
public final static String ADDRESSES_EXTRA = "addresses"; public final static String ADDRESS_EXTRA = "address";
private MasterSecret masterSecret; private MasterSecret masterSecret;
private long threadId; private long threadId;
@ -139,10 +138,10 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private void initializeActionBar() { private void initializeActionBar() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Recipients recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); Recipient recipient = RecipientFactory.getRecipientFor(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipients.addListener(this); recipient.addListener(this);
setActionBarColor(recipients.getColor()); setActionBarColor(recipient.getColor());
} }
private void setActionBarColor(MaterialColor color) { private void setActionBarColor(MaterialColor color) {
@ -154,11 +153,11 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
Util.runOnMain(new Runnable() { Util.runOnMain(new Runnable() {
@Override @Override
public void run() { public void run() {
setActionBarColor(recipients.getColor()); setActionBarColor(recipient.getColor());
} }
}); });
} }
@ -243,7 +242,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
}); });
} }
private void updateRecipients(MessageRecord messageRecord, Recipients recipients) { private void updateRecipients(MessageRecord messageRecord, Recipient recipient, List<Recipient> recipients) {
final int toFromRes; final int toFromRes;
if (messageRecord.isMms() && !messageRecord.isPush() && !messageRecord.isOutgoing()) { if (messageRecord.isMms() && !messageRecord.isPush() && !messageRecord.isOutgoing()) {
toFromRes = R.string.message_details_header__with; toFromRes = R.string.message_details_header__with;
@ -254,7 +253,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
} }
toFrom.setText(toFromRes); toFrom.setText(toFromRes);
conversationItem.bind(masterSecret, messageRecord, dynamicLanguage.getCurrentLocale(), conversationItem.bind(masterSecret, messageRecord, dynamicLanguage.getCurrentLocale(),
new HashSet<MessageRecord>(), recipients); new HashSet<MessageRecord>(), recipient);
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, masterSecret, messageRecord, recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, masterSecret, messageRecord,
recipients, isPushGroup)); recipients, isPushGroup));
} }
@ -321,7 +320,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
return false; return false;
} }
private class MessageRecipientAsyncTask extends AsyncTask<Void,Void,Recipients> { private class MessageRecipientAsyncTask extends AsyncTask<Void,Void,List<Recipient>> {
private WeakReference<Context> weakContext; private WeakReference<Context> weakContext;
private MessageRecord messageRecord; private MessageRecord messageRecord;
@ -335,41 +334,27 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
} }
@Override @Override
public Recipients doInBackground(Void... voids) { public List<Recipient> doInBackground(Void... voids) {
Context context = getContext(); Context context = getContext();
if (context == null) { if (context == null) {
Log.w(TAG, "associated context is destroyed, finishing early"); Log.w(TAG, "associated context is destroyed, finishing early");
return null; return null;
} }
Recipients recipients; List<Recipient> recipients = new LinkedList<>();
final Recipients intermediaryRecipients; if (!messageRecord.getRecipient().isGroupRecipient()) {
if (messageRecord.isMms()) {
intermediaryRecipients = DatabaseFactory.getMmsAddressDatabase(context).getRecipientsForId(messageRecord.getId());
} else {
intermediaryRecipients = messageRecord.getRecipients();
}
if (!intermediaryRecipients.isGroupRecipient()) {
Log.w(TAG, "Recipient is not a group, resolving members immediately."); Log.w(TAG, "Recipient is not a group, resolving members immediately.");
recipients = intermediaryRecipients; recipients.add(messageRecord.getRecipient());
} else { } else {
try { recipients.addAll(DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false));
Address groupId = intermediaryRecipients.getPrimaryRecipient().getAddress();
recipients = DatabaseFactory.getGroupDatabase(context)
.getGroupMembers(GroupUtil.getDecodedId(groupId.toGroupString()), false);
} catch (IOException e) {
Log.w(TAG, e);
recipients = RecipientFactory.getRecipientsFor(MessageDetailsActivity.this, new LinkedList<Recipient>(), false);
}
} }
return recipients; return recipients;
} }
@Override @Override
public void onPostExecute(Recipients recipients) { public void onPostExecute(List<Recipient> recipients) {
if (getContext() == null) { if (getContext() == null) {
Log.w(TAG, "AsyncTask finished with a destroyed context, leaving early."); Log.w(TAG, "AsyncTask finished with a destroyed context, leaving early.");
return; return;
@ -377,7 +362,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
inflateMessageViewIfAbsent(messageRecord); inflateMessageViewIfAbsent(messageRecord);
updateRecipients(messageRecord, recipients); updateRecipients(messageRecord, messageRecord.getRecipient(), recipients);
if (messageRecord.isFailed()) { if (messageRecord.isFailed()) {
errorText.setVisibility(View.VISIBLE); errorText.setVisibility(View.VISIBLE);
metadataContainer.setVisibility(View.GONE); metadataContainer.setVisibility(View.GONE);

View File

@ -10,22 +10,22 @@ import android.widget.BaseAdapter;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Conversions; import org.thoughtcrime.securesms.util.Conversions;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.List;
public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener { public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context; private final Context context;
private final MasterSecret masterSecret; private final MasterSecret masterSecret;
private final MessageRecord record; private final MessageRecord record;
private final Recipients recipients; private final List<Recipient> recipients;
private final boolean isPushGroup; private final boolean isPushGroup;
public MessageDetailsRecipientAdapter(Context context, MasterSecret masterSecret, public MessageDetailsRecipientAdapter(Context context, MasterSecret masterSecret,
MessageRecord record, Recipients recipients, MessageRecord record, List<Recipient> recipients,
boolean isPushGroup) boolean isPushGroup)
{ {
this.context = context; this.context = context;
@ -37,18 +37,18 @@ public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsLi
@Override @Override
public int getCount() { public int getCount() {
return recipients.getRecipientsList().size(); return recipients.size();
} }
@Override @Override
public Object getItem(int position) { public Object getItem(int position) {
return recipients.getRecipientsList().get(position); return recipients.get(position);
} }
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
try { try {
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(recipients.getRecipientsList().get(position).getAddress().serialize().getBytes())); return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(recipients.get(position).getAddress().serialize().getBytes()));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
@ -60,7 +60,7 @@ public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsLi
convertView = LayoutInflater.from(context).inflate(R.layout.message_recipient_list_item, parent, false); convertView = LayoutInflater.from(context).inflate(R.layout.message_recipient_list_item, parent, false);
} }
Recipient recipient = recipients.getRecipientsList().get(position); Recipient recipient = recipients.get(position);
((MessageRecipientListItem)convertView).set(masterSecret, record, recipient, isPushGroup); ((MessageRecipientListItem)convertView).set(masterSecret, record, recipient, isPushGroup);
return convertView; return convertView;
} }

View File

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
/** /**
@ -43,7 +44,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
* @author Jake McGinty * @author Jake McGinty
*/ */
public class MessageRecipientListItem extends RelativeLayout public class MessageRecipientListItem extends RelativeLayout
implements Recipient.RecipientModifiedListener implements RecipientModifiedListener
{ {
private final static String TAG = MessageRecipientListItem.class.getSimpleName(); private final static String TAG = MessageRecipientListItem.class.getSimpleName();
@ -67,6 +68,7 @@ public class MessageRecipientListItem extends RelativeLayout
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate();
this.fromView = (FromTextView) findViewById(R.id.from); this.fromView = (FromTextView) findViewById(R.id.from);
this.errorDescription = (TextView) findViewById(R.id.error_description); this.errorDescription = (TextView) findViewById(R.id.error_description);
this.actionDescription = (TextView) findViewById(R.id.action_description); this.actionDescription = (TextView) findViewById(R.id.action_description);
@ -172,11 +174,13 @@ public class MessageRecipientListItem extends RelativeLayout
} }
private class ResendAsyncTask extends AsyncTask<Void,Void,Void> { private class ResendAsyncTask extends AsyncTask<Void,Void,Void> {
private final Context context;
private final MasterSecret masterSecret; private final MasterSecret masterSecret;
private final MessageRecord record; private final MessageRecord record;
private final NetworkFailure failure; private final NetworkFailure failure;
public ResendAsyncTask(MasterSecret masterSecret, MessageRecord record, NetworkFailure failure) { public ResendAsyncTask(MasterSecret masterSecret, MessageRecord record, NetworkFailure failure) {
this.context = getContext().getApplicationContext();
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.record = record; this.record = record;
this.failure = failure; this.failure = failure;
@ -184,13 +188,13 @@ public class MessageRecipientListItem extends RelativeLayout
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext()); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
mmsDatabase.removeFailure(record.getId(), failure); mmsDatabase.removeFailure(record.getId(), failure);
if (record.getRecipients().isGroupRecipient()) { if (record.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(getContext(), masterSecret, record, failure.getAddress()); MessageSender.resendGroupMessage(context, record, failure.getAddress());
} else { } else {
MessageSender.resend(getContext(), masterSecret, record); MessageSender.resend(context, masterSecret, record);
} }
return null; return null;
} }

View File

@ -28,8 +28,8 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
/** /**
* Activity container for starting a new conversation. * Activity container for starting a new conversation.
@ -50,14 +50,14 @@ public class NewConversationActivity extends ContactSelectionActivity {
@Override @Override
public void onContactSelected(String number) { public void onContactSelected(String number) {
Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, number)}, true); Recipient recipient = RecipientFactory.getRecipientFor(this, Address.fromExternal(this, number), true);
Intent intent = new Intent(this, ConversationActivity.class); Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)); intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
intent.setDataAndType(getIntent().getData(), getIntent().getType()); intent.setDataAndType(getIntent().getData(), getIntent().getType());
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);

View File

@ -41,8 +41,9 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.preferences.AdvancedRingtonePreference; import org.thoughtcrime.securesms.preferences.AdvancedRingtonePreference;
import org.thoughtcrime.securesms.preferences.ColorPreference; import org.thoughtcrime.securesms.preferences.ColorPreference;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
@ -53,11 +54,11 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements Recipients.RecipientsModifiedListener public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener
{ {
private static final String TAG = RecipientPreferenceActivity.class.getSimpleName(); private static final String TAG = RecipientPreferenceActivity.class.getSimpleName();
public static final String ADDRESSES_EXTRA = "recipient_addresses"; public static final String ADDRESS_EXTRA = "recipient_address";
public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number"; public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number";
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
@ -86,16 +87,16 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public void onCreate(Bundle instanceState, @NonNull MasterSecret masterSecret) { public void onCreate(Bundle instanceState, @NonNull MasterSecret masterSecret) {
setContentView(R.layout.recipient_preference_activity); setContentView(R.layout.recipient_preference_activity);
Address[] addresses = Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)); Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
Recipients recipients = RecipientFactory.getRecipientsFor(this, addresses, true); Recipient recipient = RecipientFactory.getRecipientFor(this, address, true);
initializeToolbar(); initializeToolbar();
initializeReceivers(); initializeReceivers();
setHeader(recipients); setHeader(recipient);
recipients.addListener(this); recipient.addListener(this);
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putParcelableArray(ADDRESSES_EXTRA, addresses); bundle.putParcelable(ADDRESS_EXTRA, address);
initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), masterSecret, null, bundle); initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), masterSecret, null, bundle);
} }
@ -149,9 +150,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.staleReceiver = new BroadcastReceiver() { this.staleReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true); Recipient recipient = RecipientFactory.getRecipientFor(context, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipients.addListener(RecipientPreferenceActivity.this); recipient.addListener(RecipientPreferenceActivity.this);
onModified(recipients); onModified(recipient);
} }
}; };
@ -162,37 +163,37 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
registerReceiver(staleReceiver, staleFilter); registerReceiver(staleReceiver, staleFilter);
} }
private void setHeader(Recipients recipients) { private void setHeader(Recipient recipient) {
this.avatar.setAvatar(recipients, true); this.avatar.setAvatar(recipient, true);
this.title.setText(recipients.toShortString()); this.title.setText(recipient.toShortString());
this.toolbar.setBackgroundColor(recipients.getColor().toActionBarColor(this)); this.toolbar.setBackgroundColor(recipient.getColor().toActionBarColor(this));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(recipients.getColor().toStatusBarColor(this)); getWindow().setStatusBarColor(recipient.getColor().toStatusBarColor(this));
} }
if (recipients.isBlocked()) this.blockedIndicator.setVisibility(View.VISIBLE); if (recipient.isBlocked()) this.blockedIndicator.setVisibility(View.VISIBLE);
else this.blockedIndicator.setVisibility(View.GONE); else this.blockedIndicator.setVisibility(View.GONE);
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
title.post(new Runnable() { title.post(new Runnable() {
@Override @Override
public void run() { public void run() {
setHeader(recipients); setHeader(recipient);
} }
}); });
} }
public static class RecipientPreferenceFragment public static class RecipientPreferenceFragment
extends PreferenceFragment extends PreferenceFragment
implements Recipients.RecipientsModifiedListener implements RecipientModifiedListener
{ {
private final Handler handler = new Handler(); private final Handler handler = new Handler();
private Recipients recipients; private Recipient recipient;
private BroadcastReceiver staleReceiver; private BroadcastReceiver staleReceiver;
private MasterSecret masterSecret; private MasterSecret masterSecret;
private boolean canHaveSafetyNumber; private boolean canHaveSafetyNumber;
@ -223,29 +224,29 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
setSummaries(recipients); setSummaries(recipient);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
this.recipients.removeListener(this); this.recipient.removeListener(this);
getActivity().unregisterReceiver(staleReceiver); getActivity().unregisterReceiver(staleReceiver);
} }
private void initializeRecipients() { private void initializeRecipients() {
this.recipients = RecipientFactory.getRecipientsFor(getActivity(), this.recipient = RecipientFactory.getRecipientFor(getActivity(),
Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)), (Address)getArguments().getParcelable(ADDRESS_EXTRA),
true); true);
this.recipients.addListener(this); this.recipient.addListener(this);
this.staleReceiver = new BroadcastReceiver() { this.staleReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
recipients.removeListener(RecipientPreferenceFragment.this); recipient.removeListener(RecipientPreferenceFragment.this);
recipients = RecipientFactory.getRecipientsFor(getActivity(), Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)), true); recipient = RecipientFactory.getRecipientFor(getActivity(), (Address)getArguments().getParcelable(ADDRESS_EXTRA), true);
onModified(recipients); onModified(recipient);
} }
}; };
@ -256,7 +257,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
getActivity().registerReceiver(staleReceiver, intentFilter); getActivity().registerReceiver(staleReceiver, intentFilter);
} }
private void setSummaries(Recipients recipients) { private void setSummaries(Recipient recipient) {
CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED); CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED);
AdvancedRingtonePreference ringtonePreference = (AdvancedRingtonePreference) this.findPreference(PREFERENCE_TONE); AdvancedRingtonePreference ringtonePreference = (AdvancedRingtonePreference) this.findPreference(PREFERENCE_TONE);
ListPreference vibratePreference = (ListPreference) this.findPreference(PREFERENCE_VIBRATE); ListPreference vibratePreference = (ListPreference) this.findPreference(PREFERENCE_VIBRATE);
@ -264,9 +265,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
Preference blockPreference = this.findPreference(PREFERENCE_BLOCK); Preference blockPreference = this.findPreference(PREFERENCE_BLOCK);
final Preference identityPreference = this.findPreference(PREFERENCE_IDENTITY); final Preference identityPreference = this.findPreference(PREFERENCE_IDENTITY);
mutePreference.setChecked(recipients.isMuted()); mutePreference.setChecked(recipient.isMuted());
final Uri toneUri = recipients.getRingtone(); final Uri toneUri = recipient.getRingtone();
if (toneUri == null) { if (toneUri == null) {
ringtonePreference.setSummary(R.string.preferences__default); ringtonePreference.setSummary(R.string.preferences__default);
@ -283,10 +284,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
} }
if (recipients.getVibrate() == VibrateState.DEFAULT) { if (recipient.getVibrate() == VibrateState.DEFAULT) {
vibratePreference.setSummary(R.string.preferences__default); vibratePreference.setSummary(R.string.preferences__default);
vibratePreference.setValueIndex(0); vibratePreference.setValueIndex(0);
} else if (recipients.getVibrate() == VibrateState.ENABLED) { } else if (recipient.getVibrate() == VibrateState.ENABLED) {
vibratePreference.setSummary(R.string.RecipientPreferenceActivity_enabled); vibratePreference.setSummary(R.string.RecipientPreferenceActivity_enabled);
vibratePreference.setValueIndex(1); vibratePreference.setValueIndex(1);
} else { } else {
@ -294,18 +295,18 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
vibratePreference.setValueIndex(2); vibratePreference.setValueIndex(2);
} }
if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) { if (recipient.isGroupRecipient()) {
if (colorPreference != null) getPreferenceScreen().removePreference(colorPreference); if (colorPreference != null) getPreferenceScreen().removePreference(colorPreference);
if (blockPreference != null) getPreferenceScreen().removePreference(blockPreference); if (blockPreference != null) getPreferenceScreen().removePreference(blockPreference);
if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference); if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference);
} else { } else {
colorPreference.setChoices(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity())); colorPreference.setChoices(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
colorPreference.setValue(recipients.getColor().toActionBarColor(getActivity())); colorPreference.setValue(recipient.getColor().toActionBarColor(getActivity()));
if (recipients.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock); if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock);
else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block); else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block);
IdentityUtil.getRemoteIdentityKey(getActivity(), recipients.getPrimaryRecipient()).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() { IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
@Override @Override
public void onSuccess(Optional<IdentityRecord> result) { public void onSuccess(Optional<IdentityRecord> result) {
if (result.isPresent()) { if (result.isPresent()) {
@ -328,11 +329,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
setSummaries(recipients); setSummaries(recipient);
} }
}); });
} }
@ -350,13 +351,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
uri = Uri.parse(value); uri = Uri.parse(value);
} }
recipients.setRingtone(uri); recipient.setRingtone(uri);
new AsyncTask<Uri, Void, Void>() { new AsyncTask<Uri, Void, Void>() {
@Override @Override
protected Void doInBackground(Uri... params) { protected Void doInBackground(Uri... params) {
DatabaseFactory.getRecipientPreferenceDatabase(getActivity()) DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
.setRingtone(recipients, params[0]); .setRingtone(recipient, params[0]);
return null; return null;
} }
}.execute(uri); }.execute(uri);
@ -371,13 +372,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
int value = Integer.parseInt((String) newValue); int value = Integer.parseInt((String) newValue);
final VibrateState vibrateState = VibrateState.fromId(value); final VibrateState vibrateState = VibrateState.fromId(value);
recipients.setVibrate(vibrateState); recipient.setVibrate(vibrateState);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(getActivity()) DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
.setVibrate(recipients, vibrateState); .setVibrate(recipient, vibrateState);
return null; return null;
} }
}.execute(); }.execute();
@ -392,26 +393,26 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final int value = (Integer) newValue; final int value = (Integer) newValue;
final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(getActivity(), value); final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(getActivity(), value);
final MaterialColor currentColor = recipients.getColor(); final MaterialColor currentColor = recipient.getColor();
if (selectedColor == null) return true; if (selectedColor == null) return true;
if (preference.isEnabled() && !currentColor.equals(selectedColor)) { if (preference.isEnabled() && !currentColor.equals(selectedColor)) {
recipients.setColor(selectedColor); recipient.setColor(selectedColor);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
Context context = getActivity(); Context context = getActivity();
DatabaseFactory.getRecipientPreferenceDatabase(context) DatabaseFactory.getRecipientPreferenceDatabase(context)
.setColor(recipients, selectedColor); .setColor(recipient, selectedColor);
if (DirectoryHelper.getUserCapabilities(context, recipients) if (DirectoryHelper.getUserCapabilities(context, recipient)
.getTextCapability() == DirectoryHelper.UserCapabilities.Capability.SUPPORTED) .getTextCapability() == DirectoryHelper.UserCapabilities.Capability.SUPPORTED)
{ {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new MultiDeviceContactUpdateJob(context, recipients.getPrimaryRecipient().getAddress())); .add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
} }
return null; return null;
} }
@ -424,8 +425,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private class MuteClickedListener implements Preference.OnPreferenceClickListener { private class MuteClickedListener implements Preference.OnPreferenceClickListener {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
if (recipients.isMuted()) handleUnmute(); if (recipient.isMuted()) handleUnmute();
else handleMute(); else handleMute();
return true; return true;
} }
@ -434,25 +435,25 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
MuteDialog.show(getActivity(), new MuteDialog.MuteSelectionListener() { MuteDialog.show(getActivity(), new MuteDialog.MuteSelectionListener() {
@Override @Override
public void onMuted(long until) { public void onMuted(long until) {
setMuted(recipients, until); setMuted(recipient, until);
} }
}); });
setSummaries(recipients); setSummaries(recipient);
} }
private void handleUnmute() { private void handleUnmute() {
setMuted(recipients, 0); setMuted(recipient, 0);
} }
private void setMuted(final Recipients recipients, final long until) { private void setMuted(final Recipient recipient, final long until) {
recipients.setMuted(until); recipient.setMuted(until);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(getActivity()) DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
.setMuted(recipients, until); .setMuted(recipient, until);
return null; return null;
} }
}.execute(); }.execute();
@ -471,7 +472,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class); Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class);
verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress()); verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress());
verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey())); verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
startActivity(verifyIdentityIntent); startActivity(verifyIdentityIntent);
@ -483,8 +484,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private class BlockClickedListener implements Preference.OnPreferenceClickListener { private class BlockClickedListener implements Preference.OnPreferenceClickListener {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
if (recipients.isBlocked()) handleUnblock(); if (recipient.isBlocked()) handleUnblock();
else handleBlock(); else handleBlock();
return true; return true;
} }
@ -498,7 +499,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
.setPositiveButton(R.string.RecipientPreferenceActivity_block, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.RecipientPreferenceActivity_block, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setBlocked(recipients, true); setBlocked(recipient, true);
} }
}).show(); }).show();
} }
@ -512,13 +513,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.RecipientPreferenceActivity_unblock, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setBlocked(recipients, false); setBlocked(recipient, false);
} }
}).show(); }).show();
} }
private void setBlocked(final Recipients recipients, final boolean blocked) { private void setBlocked(final Recipient recipient, final boolean blocked) {
recipients.setBlocked(blocked); recipient.setBlocked(blocked);
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
@ -526,7 +527,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
Context context = getActivity(); Context context = getActivity();
DatabaseFactory.getRecipientPreferenceDatabase(context) DatabaseFactory.getRecipientPreferenceDatabase(context)
.setBlocked(recipients, blocked); .setBlocked(recipient, blocked);
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()

View File

@ -39,7 +39,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.FileUtils;
@ -60,9 +60,9 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
{ {
private static final String TAG = ShareActivity.class.getSimpleName(); private static final String TAG = ShareActivity.class.getSimpleName();
public static final String EXTRA_THREAD_ID = "thread_id"; public static final String EXTRA_THREAD_ID = "thread_id";
public static final String EXTRA_ADDRESSES_MARSHALLED = "addresses"; public static final String EXTRA_ADDRESS_MARSHALLED = "address_marshalled";
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type"; public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
private final DynamicTheme dynamicTheme = new DynamicTheme (); private final DynamicTheme dynamicTheme = new DynamicTheme ();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -165,25 +165,25 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
} }
@Override @Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType) { public void onCreateConversation(long threadId, Recipient recipient, int distributionType) {
createConversation(threadId, recipients.getAddresses(), distributionType); createConversation(threadId, recipient.getAddress(), distributionType);
} }
private void handleResolvedMedia(Intent intent, boolean animate) { private void handleResolvedMedia(Intent intent, boolean animate) {
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1); long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1); int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
Address[] addresses = null; Address address = null;
if (intent.hasExtra(EXTRA_ADDRESSES_MARSHALLED)) { if (intent.hasExtra(EXTRA_ADDRESS_MARSHALLED)) {
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESSES_MARSHALLED); byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESS_MARSHALLED);
parcel.unmarshall(marshalled, 0, marshalled.length); parcel.unmarshall(marshalled, 0, marshalled.length);
parcel.setDataPosition(0); parcel.setDataPosition(0);
addresses = parcel.createTypedArray(Address.CREATOR); address = parcel.readParcelable(getClassLoader());
parcel.recycle(); parcel.recycle();
} }
boolean hasResolvedDestination = threadId != -1 && addresses != null && distributionType != -1; boolean hasResolvedDestination = threadId != -1 && address != null && distributionType != -1;
if (!hasResolvedDestination && animate) { if (!hasResolvedDestination && animate) {
ViewUtil.fadeIn(fragmentContainer, 300); ViewUtil.fadeIn(fragmentContainer, 300);
@ -192,13 +192,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
fragmentContainer.setVisibility(View.VISIBLE); fragmentContainer.setVisibility(View.VISIBLE);
progressWheel.setVisibility(View.GONE); progressWheel.setVisibility(View.GONE);
} else { } else {
createConversation(threadId, addresses, distributionType); createConversation(threadId, address, distributionType);
} }
} }
private void createConversation(long threadId, Address[] addresses, int distributionType) { private void createConversation(long threadId, Address address, int distributionType) {
final Intent intent = getBaseShareIntent(ConversationActivity.class); final Intent intent = getBaseShareIntent(ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, addresses); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType); intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);

View File

@ -28,10 +28,9 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ListView; import android.widget.ListView;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.recipients.Recipient;
/** /**
* A fragment to select and share to open conversations * A fragment to select and share to open conversations
@ -73,7 +72,7 @@ public class ShareFragment extends ListFragment implements LoaderManager.LoaderC
if (v instanceof ShareListItem) { if (v instanceof ShareListItem) {
ShareListItem headerView = (ShareListItem) v; ShareListItem headerView = (ShareListItem) v;
handleCreateConversation(headerView.getThreadId(), headerView.getRecipients(), handleCreateConversation(headerView.getThreadId(), headerView.getRecipient(),
headerView.getDistributionType()); headerView.getDistributionType());
} }
} }
@ -84,8 +83,8 @@ public class ShareFragment extends ListFragment implements LoaderManager.LoaderC
getLoaderManager().restartLoader(0, null, this); getLoaderManager().restartLoader(0, null, this);
} }
private void handleCreateConversation(long threadId, Recipients recipients, int distributionType) { private void handleCreateConversation(long threadId, Recipient recipient, int distributionType) {
listener.onCreateConversation(threadId, recipients, distributionType); listener.onCreateConversation(threadId, recipient, distributionType);
} }
@Override @Override
@ -104,6 +103,6 @@ public class ShareFragment extends ListFragment implements LoaderManager.LoaderC
} }
public interface ConversationSelectedListener { public interface ConversationSelectedListener {
public void onCreateConversation(long threadId, Recipients recipients, int distributionType); public void onCreateConversation(long threadId, Recipient recipient, int distributionType);
} }
} }

View File

@ -25,7 +25,8 @@ import android.widget.RelativeLayout;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
/** /**
* A simple view to show the recipients of an open conversation * A simple view to show the recipients of an open conversation
@ -33,12 +34,12 @@ import org.thoughtcrime.securesms.recipients.Recipients;
* @author Jake McGinty * @author Jake McGinty
*/ */
public class ShareListItem extends RelativeLayout public class ShareListItem extends RelativeLayout
implements Recipients.RecipientsModifiedListener implements RecipientModifiedListener
{ {
private final static String TAG = ShareListItem.class.getSimpleName(); private final static String TAG = ShareListItem.class.getSimpleName();
private Context context; private Context context;
private Recipients recipients; private Recipient recipient;
private long threadId; private long threadId;
private FromTextView fromView; private FromTextView fromView;
@ -59,24 +60,25 @@ public class ShareListItem extends RelativeLayout
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate();
this.fromView = (FromTextView) findViewById(R.id.from); this.fromView = (FromTextView) findViewById(R.id.from);
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image); this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
} }
public void set(ThreadRecord thread) { public void set(ThreadRecord thread) {
this.recipients = thread.getRecipients(); this.recipient = thread.getRecipient();
this.threadId = thread.getThreadId(); this.threadId = thread.getThreadId();
this.distributionType = thread.getDistributionType(); this.distributionType = thread.getDistributionType();
this.recipients.addListener(this); this.recipient.addListener(this);
this.fromView.setText(recipients); this.fromView.setText(recipient);
setBackground(); setBackground();
this.contactPhotoImage.setAvatar(this.recipients, false); this.contactPhotoImage.setAvatar(this.recipient, false);
} }
public void unbind() { public void unbind() {
if (this.recipients != null) this.recipients.removeListener(this); if (this.recipient != null) this.recipient.removeListener(this);
} }
private void setBackground() { private void setBackground() {
@ -88,8 +90,8 @@ public class ShareListItem extends RelativeLayout
drawables.recycle(); drawables.recycle();
} }
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
public long getThreadId() { public long getThreadId() {
@ -101,12 +103,12 @@ public class ShareListItem extends RelativeLayout
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
fromView.setText(recipients); fromView.setText(recipient);
contactPhotoImage.setAvatar(recipients, false); contactPhotoImage.setAvatar(recipient, false);
} }
}); });
} }

View File

@ -12,8 +12,8 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Rfc5724Uri; import org.thoughtcrime.securesms.util.Rfc5724Uri;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -47,13 +47,13 @@ public class SmsSendtoActivity extends Activity {
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody()); nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
} else { } else {
Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, destination.getDestination())}, true); Recipient recipient = RecipientFactory.getRecipientFor(this, Address.fromExternal(this, destination.getDestination()), true);
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
nextIntent = new Intent(this, ConversationActivity.class); nextIntent = new Intent(this, ConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody()); nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
nextIntent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); nextIntent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
} }
return nextIntent; return nextIntent;
} }

View File

@ -74,6 +74,7 @@ import org.thoughtcrime.securesms.qr.ScanListener;
import org.thoughtcrime.securesms.qr.ScanningThread; import org.thoughtcrime.securesms.qr.ScanningThread;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
@ -96,7 +97,7 @@ import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
* *
* @author Moxie Marlinspike * @author Moxie Marlinspike
*/ */
public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements Recipient.RecipientModifiedListener, ScanListener, View.OnClickListener { public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener {
private static final String TAG = VerifyIdentityActivity.class.getSimpleName(); private static final String TAG = VerifyIdentityActivity.class.getSimpleName();
@ -191,7 +192,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
} }
} }
public static class VerifyDisplayFragment extends Fragment implements Recipient.RecipientModifiedListener, CompoundButton.OnCheckedChangeListener { public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener {
public static final String REMOTE_ADDRESS = "remote_address"; public static final String REMOTE_ADDRESS = "remote_address";
public static final String REMOTE_NUMBER = "remote_number"; public static final String REMOTE_NUMBER = "remote_number";

View File

@ -14,8 +14,6 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
public class AvatarImageView extends ImageView { public class AvatarImageView extends ImageView {
@ -37,31 +35,25 @@ public class AvatarImageView extends ImageView {
} }
} }
public void setAvatar(final @Nullable Recipients recipients, boolean quickContactEnabled) { public void setAvatar(final @Nullable Recipient recipient, boolean quickContactEnabled) {
if (recipients != null) { if (recipient != null) {
MaterialColor backgroundColor = recipients.getColor(); MaterialColor backgroundColor = recipient.getColor();
setImageDrawable(recipients.getContactPhoto().asDrawable(getContext(), backgroundColor.toConversationColor(getContext()), inverted)); setImageDrawable(recipient.getContactPhoto().asDrawable(getContext(), backgroundColor.toConversationColor(getContext()), inverted));
setAvatarClickHandler(recipients, quickContactEnabled); setAvatarClickHandler(recipient, quickContactEnabled);
} else { } else {
setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted)); setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted));
setOnClickListener(null); setOnClickListener(null);
} }
} }
public void setAvatar(@Nullable Recipient recipient, boolean quickContactEnabled) { private void setAvatarClickHandler(final Recipient recipient, boolean quickContactEnabled) {
setAvatar(RecipientFactory.getRecipientsFor(getContext(), recipient, true), quickContactEnabled); if (!recipient.isGroupRecipient() && quickContactEnabled) {
}
private void setAvatarClickHandler(final Recipients recipients, boolean quickContactEnabled) {
if (!recipients.isGroupRecipient() && quickContactEnabled) {
setOnClickListener(new View.OnClickListener() { setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Recipient recipient = recipients.getPrimaryRecipient(); if (recipient.getContactUri() != null) {
if (recipient != null && recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else if (recipient != null) { } else {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
if (recipient.getAddress().isEmail()) { if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString()); intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());

View File

@ -11,8 +11,6 @@ import android.util.AttributeSet;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
public class FromTextView extends EmojiTextView { public class FromTextView extends EmojiTextView {
@ -27,17 +25,13 @@ public class FromTextView extends EmojiTextView {
} }
public void setText(Recipient recipient) { public void setText(Recipient recipient) {
setText(RecipientFactory.getRecipientsFor(getContext(), recipient, true)); setText(recipient, true);
} }
public void setText(Recipients recipients) { public void setText(Recipient recipient, boolean read) {
setText(recipients, true);
}
public void setText(Recipients recipients, boolean read) {
int attributes[] = new int[]{R.attr.conversation_list_item_count_color}; int attributes[] = new int[]{R.attr.conversation_list_item_count_color};
TypedArray colors = getContext().obtainStyledAttributes(attributes); TypedArray colors = getContext().obtainStyledAttributes(attributes);
String fromString = recipients.toShortString(); String fromString = recipient.toShortString();
int typeface; int typeface;
@ -55,9 +49,9 @@ public class FromTextView extends EmojiTextView {
setText(builder); setText(builder);
if (recipients.isBlocked()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0); if (recipient.isBlocked()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0);
else if (recipients.isMuted()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_grey600_18dp, 0, 0, 0); else if (recipient.isMuted()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_grey600_18dp, 0, 0, 0);
else setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); else setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} }

View File

@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.components;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -33,9 +32,7 @@ import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -47,7 +44,7 @@ import java.util.StringTokenizer;
* *
* @author Moxie Marlinspike * @author Moxie Marlinspike
*/ */
public class PushRecipientsPanel extends RelativeLayout implements RecipientsModifiedListener { public class PushRecipientsPanel extends RelativeLayout implements RecipientModifiedListener {
private final String TAG = PushRecipientsPanel.class.getSimpleName(); private final String TAG = PushRecipientsPanel.class.getSimpleName();
private RecipientsPanelChangedListener panelChangeListener; private RecipientsPanelChangedListener panelChangeListener;
@ -71,14 +68,9 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
initialize(); initialize();
} }
public Recipients getRecipients() throws RecipientFormattingException { public List<Recipient> getRecipients() {
String rawText = recipientsText.getText().toString(); String rawText = recipientsText.getText().toString();
Recipients recipients = getRecipientsFromString(getContext(), rawText, true); return getRecipientsFromString(getContext(), rawText, true);
if (recipients == null || recipients.isEmpty())
throw new RecipientFormattingException("Recipient List Is Empty!");
return recipients;
} }
public void disable() { public void disable() {
@ -104,15 +96,14 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
} }
private void initRecipientsEditor() { private void initRecipientsEditor() {
Recipients recipients;
recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text);
try { this.recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text);
recipients = getRecipients();
} catch (RecipientFormattingException e) { List<Recipient> recipients = getRecipients();
recipients = RecipientFactory.getRecipientsFor(getContext(), new LinkedList<Recipient>(), true);
for (Recipient recipient : recipients) {
recipient.addListener(this);
} }
recipients.addListener(this);
recipientsText.setAdapter(new RecipientsAdapter(this.getContext())); recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
recipientsText.populate(recipients); recipientsText.populate(recipients);
@ -122,32 +113,27 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
@Override @Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (panelChangeListener != null) { if (panelChangeListener != null) {
try { panelChangeListener.onRecipientsPanelUpdate(getRecipients());
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
} catch (RecipientFormattingException rfe) {
panelChangeListener.onRecipientsPanelUpdate(null);
}
} }
recipientsText.setText(""); recipientsText.setText("");
} }
}); });
} }
private @Nullable Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) { private @NonNull List<Recipient> getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
StringTokenizer tokenizer = new StringTokenizer(rawText, ","); StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
List<Address> addresses = new LinkedList<>(); List<Recipient> recipients = new LinkedList<>();
while (tokenizer.hasMoreTokens()) { while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim(); String token = tokenizer.nextToken().trim();
if (!TextUtils.isEmpty(token)) { if (!TextUtils.isEmpty(token)) {
if (hasBracketedNumber(token)) addresses.add(Address.fromExternal(context, parseBracketedNumber(token))); if (hasBracketedNumber(token)) recipients.add(RecipientFactory.getRecipientFor(context, Address.fromExternal(context, parseBracketedNumber(token)), asynchronous));
else addresses.add(Address.fromExternal(context, token)); else recipients.add(RecipientFactory.getRecipientFor(context, Address.fromExternal(context, token), asynchronous));
} }
} }
if (addresses.size() == 0) return null; return recipients;
else return RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), asynchronous);
} }
private boolean hasBracketedNumber(String recipient) { private boolean hasBracketedNumber(String recipient) {
@ -166,24 +152,20 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
} }
@Override @Override
public void onModified(Recipients recipients) { public void onModified(Recipient recipient) {
recipientsText.populate(recipients); recipientsText.populate(getRecipients());
} }
private class FocusChangedListener implements View.OnFocusChangeListener { private class FocusChangedListener implements View.OnFocusChangeListener {
public void onFocusChange(View v, boolean hasFocus) { public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && (panelChangeListener != null)) { if (!hasFocus && (panelChangeListener != null)) {
try { panelChangeListener.onRecipientsPanelUpdate(getRecipients());
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
} catch (RecipientFormattingException rfe) {
panelChangeListener.onRecipientsPanelUpdate(null);
}
} }
} }
} }
public interface RecipientsPanelChangedListener { public interface RecipientsPanelChangedListener {
public void onRecipientsPanelUpdate(Recipients recipients); public void onRecipientsPanelUpdate(List<Recipient> recipients);
} }
} }

View File

@ -8,22 +8,22 @@ import android.view.View.OnClickListener;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
public class InviteReminder extends Reminder { public class InviteReminder extends Reminder {
public InviteReminder(final @NonNull Context context, public InviteReminder(final @NonNull Context context,
final @NonNull Recipients recipients) final @NonNull Recipient recipient)
{ {
super(context.getString(R.string.reminder_header_invite_title), super(context.getString(R.string.reminder_header_invite_title),
context.getString(R.string.reminder_header_invite_text, recipients.toShortString())); context.getString(R.string.reminder_header_invite_text, recipient.toShortString()));
setDismissListener(new OnClickListener() { setDismissListener(new OnClickListener() {
@Override public void onClick(View v) { @Override public void onClick(View v) {
new AsyncTask<Void,Void,Void>() { new AsyncTask<Void,Void,Void>() {
@Override protected Void doInBackground(Void... params) { @Override protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true); DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipient, true);
return null; return null;
} }
}.execute(); }.execute();

View File

@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.VerifySpan; import org.thoughtcrime.securesms.util.VerifySpan;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
@ -57,7 +58,7 @@ import org.whispersystems.libsignal.IdentityKey;
* @author Moxie Marlinspike * @author Moxie Marlinspike
* *
*/ */
public class WebRtcCallScreen extends FrameLayout implements Recipient.RecipientModifiedListener { public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedListener {
private static final String TAG = WebRtcCallScreen.class.getSimpleName(); private static final String TAG = WebRtcCallScreen.class.getSimpleName();

View File

@ -11,12 +11,12 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactSelectionListItem extends LinearLayout implements Recipients.RecipientsModifiedListener { public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
private AvatarImageView contactPhotoImage; private AvatarImageView contactPhotoImage;
private TextView numberView; private TextView numberView;
@ -24,9 +24,9 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients
private TextView labelView; private TextView labelView;
private CheckBox checkBox; private CheckBox checkBox;
private long id; private long id;
private String number; private String number;
private Recipients recipients; private Recipient recipient;
public ContactSelectionListItem(Context context) { public ContactSelectionListItem(Context context) {
super(context); super(context);
@ -53,24 +53,22 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients
this.number = number; this.number = number;
if (type == ContactsDatabase.NEW_TYPE) { if (type == ContactsDatabase.NEW_TYPE) {
this.recipients = null; this.recipient = null;
this.contactPhotoImage.setAvatar(RecipientFactory.getRecipientFor(getContext(), Address.UNKNOWN, true), false); this.contactPhotoImage.setAvatar(RecipientFactory.getRecipientFor(getContext(), Address.UNKNOWN, true), false);
} else if (!TextUtils.isEmpty(number)) { } else if (!TextUtils.isEmpty(number)) {
Address address = Address.fromExternal(getContext(), number); Address address = Address.fromExternal(getContext(), number);
this.recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[] {address}, true); this.recipient = RecipientFactory.getRecipientFor(getContext(), address, true);
if (this.recipients.getPrimaryRecipient() != null && if (this.recipient.getName() != null) {
this.recipients.getPrimaryRecipient().getName() != null) name = this.recipient.getName();
{
name = this.recipients.getPrimaryRecipient().getName();
} }
this.recipients.addListener(this); this.recipient.addListener(this);
} }
this.nameView.setTextColor(color); this.nameView.setTextColor(color);
this.numberView.setTextColor(color); this.numberView.setTextColor(color);
this.contactPhotoImage.setAvatar(recipients, false); this.contactPhotoImage.setAvatar(recipient, false);
setText(type, name, number, label); setText(type, name, number, label);
@ -83,9 +81,9 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients
} }
public void unbind() { public void unbind() {
if (recipients != null) { if (recipient != null) {
recipients.removeListener(this); recipient.removeListener(this);
recipients = null; recipient = null;
} }
} }
@ -117,13 +115,13 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipient) {
if (this.recipients == recipients) { if (this.recipient == recipient) {
this.contactPhotoImage.post(new Runnable() { this.contactPhotoImage.post(new Runnable() {
@Override @Override
public void run() { public void run() {
contactPhotoImage.setAvatar(recipients, false); contactPhotoImage.setAvatar(recipient, false);
nameView.setText(recipients.toShortString()); nameView.setText(recipient.toShortString());
} }
}); });
} }

View File

@ -29,8 +29,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
@ -103,12 +103,10 @@ public class ContactsCursorLoader extends CursorLoader {
ContactsDatabase.LABEL_COLUMN, ContactsDatabase.LABEL_COLUMN,
ContactsDatabase.CONTACT_TYPE_COLUMN}); ContactsDatabase.CONTACT_TYPE_COLUMN});
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN)); final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
final Recipients recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[]{Address.fromExternal(getContext(), number)}, true); final Recipient recipient = RecipientFactory.getRecipientFor(getContext(), Address.fromExternal(getContext(), number), true);
if (DirectoryHelper.getUserCapabilities(getContext(), recipients) if (DirectoryHelper.getUserCapabilities(getContext(), recipient).getTextCapability() != Capability.SUPPORTED) {
.getTextCapability() != Capability.SUPPORTED)
{
matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)), matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)), cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)),
number, number,

View File

@ -30,16 +30,12 @@ import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.MultiAutoCompleteTextView; import android.widget.MultiAutoCompleteTextView;
import org.thoughtcrime.securesms.recipients.Recipient; 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.RecipientsFormatter; import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
import java.util.ArrayList; import java.util.ArrayList;
@ -205,10 +201,10 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
return s; return s;
} }
public void populate(Recipients list) { public void populate(List<Recipient> list) {
SpannableStringBuilder sb = new SpannableStringBuilder(); SpannableStringBuilder sb = new SpannableStringBuilder();
for (Recipient c : list.getRecipientsList()) { for (Recipient c : list) {
if (sb.length() != 0) { if (sb.length() != 0) {
sb.append(", "); sb.append(", ");
} }

View File

@ -96,20 +96,14 @@ public class Address implements Parcelable, Comparable<Address> {
return Util.join(escapedAddresses, delimiter + ""); return Util.join(escapedAddresses, delimiter + "");
} }
public static Address[] fromParcelable(Parcelable[] parcelables) {
Address[] addresses = new Address[parcelables.length];
for (int i=0;i<parcelables.length;i++) {
addresses[i] = (Address)parcelables[i];
}
return addresses;
}
public boolean isGroup() { public boolean isGroup() {
return GroupUtil.isEncodedGroup(address); return GroupUtil.isEncodedGroup(address);
} }
public boolean isMmsGroup() {
return GroupUtil.isMmsGroup(address);
}
public boolean isEmail() { public boolean isEmail() {
return NumberUtil.isValidEmail(address); return NumberUtil.isValidEmail(address);
} }
@ -204,9 +198,9 @@ public class Address implements Parcelable, Comparable<Address> {
} }
public String format(@Nullable String number) { public String format(@Nullable String number) {
if (number == null) return "Unknown"; if (number == null) return "Unknown";
if (number.startsWith("__textsecure_group__!")) return number; if (GroupUtil.isEncodedGroup(number)) return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim(); if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", ""); String bareNumber = number.replaceAll("[^0-9+]", "");

View File

@ -28,8 +28,6 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber; import com.google.i18n.phonenumbers.Phonenumber;
@ -41,10 +39,10 @@ import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -56,12 +54,12 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class DatabaseFactory { public class DatabaseFactory {
@ -102,7 +100,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35; private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35;
private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36; private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36;
private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37;
private static final int DATABASE_VERSION = 37; private static final int NO_MORE_RECIPIENTS_PLURAL = 38;
private static final int DATABASE_VERSION = 38;
private static final String DATABASE_NAME = "messages.db"; private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object(); private static final Object lock = new Object();
@ -117,7 +116,6 @@ public class DatabaseFactory {
private final AttachmentDatabase attachments; private final AttachmentDatabase attachments;
private final MediaDatabase media; private final MediaDatabase media;
private final ThreadDatabase thread; private final ThreadDatabase thread;
private final MmsAddressDatabase mmsAddress;
private final MmsSmsDatabase mmsSmsDatabase; private final MmsSmsDatabase mmsSmsDatabase;
private final IdentityDatabase identityDatabase; private final IdentityDatabase identityDatabase;
private final DraftDatabase draftDatabase; private final DraftDatabase draftDatabase;
@ -163,10 +161,6 @@ public class DatabaseFactory {
return getInstance(context).media; return getInstance(context).media;
} }
public static MmsAddressDatabase getMmsAddressDatabase(Context context) {
return getInstance(context).mmsAddress;
}
public static IdentityDatabase getIdentityDatabase(Context context) { public static IdentityDatabase getIdentityDatabase(Context context) {
return getInstance(context).identityDatabase; return getInstance(context).identityDatabase;
} }
@ -199,7 +193,6 @@ public class DatabaseFactory {
this.attachments = new AttachmentDatabase(context, databaseHelper); this.attachments = new AttachmentDatabase(context, databaseHelper);
this.media = new MediaDatabase(context, databaseHelper); this.media = new MediaDatabase(context, databaseHelper);
this.thread = new ThreadDatabase(context, databaseHelper); this.thread = new ThreadDatabase(context, databaseHelper);
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper); this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
this.identityDatabase = new IdentityDatabase(context, databaseHelper); this.identityDatabase = new IdentityDatabase(context, databaseHelper);
this.draftDatabase = new DraftDatabase(context, databaseHelper); this.draftDatabase = new DraftDatabase(context, databaseHelper);
@ -218,7 +211,6 @@ public class DatabaseFactory {
this.mms.reset(databaseHelper); this.mms.reset(databaseHelper);
this.attachments.reset(databaseHelper); this.attachments.reset(databaseHelper);
this.thread.reset(databaseHelper); this.thread.reset(databaseHelper);
this.mmsAddress.reset(databaseHelper);
this.mmsSmsDatabase.reset(databaseHelper); this.mmsSmsDatabase.reset(databaseHelper);
this.identityDatabase.reset(databaseHelper); this.identityDatabase.reset(databaseHelper);
this.draftDatabase.reset(databaseHelper); this.draftDatabase.reset(databaseHelper);
@ -533,7 +525,6 @@ public class DatabaseFactory {
db.execSQL(MmsDatabase.CREATE_TABLE); db.execSQL(MmsDatabase.CREATE_TABLE);
db.execSQL(AttachmentDatabase.CREATE_TABLE); db.execSQL(AttachmentDatabase.CREATE_TABLE);
db.execSQL(ThreadDatabase.CREATE_TABLE); db.execSQL(ThreadDatabase.CREATE_TABLE);
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
db.execSQL(IdentityDatabase.CREATE_TABLE); db.execSQL(IdentityDatabase.CREATE_TABLE);
db.execSQL(DraftDatabase.CREATE_TABLE); db.execSQL(DraftDatabase.CREATE_TABLE);
db.execSQL(PushDatabase.CREATE_TABLE); db.execSQL(PushDatabase.CREATE_TABLE);
@ -544,7 +535,6 @@ public class DatabaseFactory {
executeStatements(db, MmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS);
executeStatements(db, AttachmentDatabase.CREATE_INDEXS); executeStatements(db, AttachmentDatabase.CREATE_INDEXS);
executeStatements(db, ThreadDatabase.CREATE_INDEXS); executeStatements(db, ThreadDatabase.CREATE_INDEXS);
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
executeStatements(db, DraftDatabase.CREATE_INDEXS); executeStatements(db, DraftDatabase.CREATE_INDEXS);
executeStatements(db, GroupDatabase.CREATE_INDEXS); executeStatements(db, GroupDatabase.CREATE_INDEXS);
} }
@ -1209,6 +1199,80 @@ public class DatabaseFactory {
} }
if (oldVersion < NO_MORE_RECIPIENTS_PLURAL) {
db.execSQL("ALTER TABLE groups ADD COLUMN mms INTEGER DEFAULT 0");
Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long threadId = cursor.getLong(0);
String addressListString = cursor.getString(1);
String[] addressList = DelimiterUtil.split(addressListString, ' ');
if (addressList.length == 1) {
ContentValues contentValues = new ContentValues();
contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' '));
db.update("thread", contentValues, "_id = ?", new String[] {String.valueOf(threadId)});
} else {
byte[] groupId = new byte[16];
List<String> members = new LinkedList<>();
new SecureRandom().nextBytes(groupId);
for (String address : addressList) {
members.add(DelimiterUtil.escape(DelimiterUtil.unescape(address, ' '), ','));
}
String encodedGroupId = "__signal_mms_group__!" + Hex.toStringCondensed(groupId);
ContentValues groupValues = new ContentValues();
ContentValues threadValues = new ContentValues();
groupValues.put("group_id", encodedGroupId);
groupValues.put("members", Util.join(members, ","));
groupValues.put("mms", 1);
threadValues.put("recipient_ids", encodedGroupId);
db.insert("groups", null, groupValues);
db.update("thread", threadValues, "_id = ?", new String[] {String.valueOf(threadId)});
db.update("recipient_preferences", threadValues, "recipient_ids = ?", new String[] {addressListString});
}
}
if (cursor != null) cursor.close();
cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String addressListString = cursor.getString(1);
String[] addressList = DelimiterUtil.split(addressListString, ' ');
if (addressList.length == 1) {
ContentValues contentValues = new ContentValues();
contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' '));
db.update("recipient_preferences", contentValues, "_id = ?", new String[] {String.valueOf(id)});
} else {
Log.w(TAG, "Found preferences for MMS thread that appears to be gone: " + addressListString);
db.delete("recipient_preferences", "_id = ?", new String[] {String.valueOf(id)});
}
}
if (cursor != null) cursor.close();
cursor = db.rawQuery("SELECT mms._id, thread.recipient_ids FROM mms, thread WHERE mms.address IS NULL AND mms.thread_id = thread._id", null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
ContentValues contentValues = new ContentValues(1);
contentValues.put("address", cursor.getString(1));
db.update("mms", contentValues, "_id = ?", new String[] {String.valueOf(id)});
}
if (cursor != null) cursor.close();
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
} }

View File

@ -14,7 +14,6 @@ import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -23,6 +22,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -45,6 +45,7 @@ public class GroupDatabase extends Database {
private static final String AVATAR_DIGEST = "avatar_digest"; private static final String AVATAR_DIGEST = "avatar_digest";
private static final String TIMESTAMP = "timestamp"; private static final String TIMESTAMP = "timestamp";
private static final String ACTIVE = "active"; private static final String ACTIVE = "active";
private static final String MMS = "mms";
public static final String CREATE_TABLE = public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "CREATE TABLE " + TABLE_NAME +
@ -59,7 +60,8 @@ public class GroupDatabase extends Database {
AVATAR_RELAY + " TEXT, " + AVATAR_RELAY + " TEXT, " +
TIMESTAMP + " INTEGER, " + TIMESTAMP + " INTEGER, " +
ACTIVE + " INTEGER DEFAULT 1, " + ACTIVE + " INTEGER DEFAULT 1, " +
AVATAR_DIGEST + " BLOB);"; AVATAR_DIGEST + " BLOB, " +
MMS + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");", "CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
@ -69,10 +71,10 @@ public class GroupDatabase extends Database {
super(context, databaseHelper); super(context, databaseHelper);
} }
public @Nullable GroupRecord getGroup(byte[] groupId) { public @Nullable GroupRecord getGroup(String groupId) {
@SuppressLint("Recycle") @SuppressLint("Recycle")
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?", Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)}, new String[] {groupId},
null, null, null); null, null, null);
Reader reader = new Reader(cursor); Reader reader = new Reader(cursor);
@ -82,7 +84,7 @@ public class GroupDatabase extends Database {
return record; return record;
} }
public boolean isUnknownGroup(byte[] groupId) { public boolean isUnknownGroup(String groupId) {
return getGroup(groupId) == null; return getGroup(groupId) == null;
} }
@ -94,12 +96,32 @@ public class GroupDatabase extends Database {
return new Reader(cursor); return new Reader(cursor);
} }
public String getOrCreateGroupForMembers(List<Address> members, boolean mms) {
Collections.sort(members);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID},
MEMBERS + " = ? AND " + MMS + " = ?",
new String[] {Address.toSerializedList(members, ','), mms ? "1" : "0"},
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
} else {
String groupId = GroupUtil.getEncodedId(allocateGroupId(), mms);
create(groupId, null, members, null, null);
return groupId;
}
} finally {
if (cursor != null) cursor.close();
}
}
public Reader getGroups() { public Reader getGroups() {
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null); Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null);
return new Reader(cursor); return new Reader(cursor);
} }
public @NonNull Recipients getGroupMembers(byte[] groupId, boolean includeSelf) { public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
List<Address> members = getCurrentMembers(groupId); List<Address> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>(); List<Recipient> recipients = new LinkedList<>();
@ -110,14 +132,16 @@ public class GroupDatabase extends Database {
recipients.add(RecipientFactory.getRecipientFor(context, member, false)); recipients.add(RecipientFactory.getRecipientFor(context, member, false));
} }
return RecipientFactory.getRecipientsFor(context, recipients, false); return recipients;
} }
public void create(byte[] groupId, String title, List<Address> members, public void create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
SignalServiceAttachmentPointer avatar, String relay) @Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay)
{ {
Collections.sort(members);
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(GROUP_ID, GroupUtil.getEncodedId(groupId)); contentValues.put(GROUP_ID, groupId);
contentValues.put(TITLE, title); contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Address.toSerializedList(members, ',')); contentValues.put(MEMBERS, Address.toSerializedList(members, ','));
@ -131,13 +155,14 @@ public class GroupDatabase extends Database {
contentValues.put(AVATAR_RELAY, relay); contentValues.put(AVATAR_RELAY, relay);
contentValues.put(TIMESTAMP, System.currentTimeMillis()); contentValues.put(TIMESTAMP, System.currentTimeMillis());
contentValues.put(ACTIVE, 1); contentValues.put(ACTIVE, 1);
contentValues.put(MMS, GroupUtil.isMmsGroup(groupId));
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues); databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
RecipientFactory.clearCache(context); RecipientFactory.clearCache(context);
notifyConversationListListeners(); notifyConversationListListeners();
} }
public void update(byte[] groupId, String title, SignalServiceAttachmentPointer avatar) { public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) {
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
if (title != null) contentValues.put(TITLE, title); if (title != null) contentValues.put(TITLE, title);
@ -150,65 +175,67 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
GROUP_ID + " = ?", GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)}); new String[] {groupId});
RecipientFactory.clearCache(context); RecipientFactory.clearCache(context);
notifyDatabaseListeners(); notifyDatabaseListeners();
notifyConversationListListeners(); notifyConversationListListeners();
} }
public void updateTitle(byte[] groupId, String title) { public void updateTitle(String groupId, String title) {
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(TITLE, title); contentValues.put(TITLE, title);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)}); new String[] {groupId});
RecipientFactory.clearCache(context); RecipientFactory.clearCache(context);
notifyDatabaseListeners(); notifyDatabaseListeners();
} }
public void updateAvatar(byte[] groupId, Bitmap avatar) { public void updateAvatar(String groupId, Bitmap avatar) {
updateAvatar(groupId, BitmapUtil.toByteArray(avatar)); updateAvatar(groupId, BitmapUtil.toByteArray(avatar));
} }
public void updateAvatar(byte[] groupId, byte[] avatar) { public void updateAvatar(String groupId, byte[] avatar) {
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(AVATAR, avatar); contentValues.put(AVATAR, avatar);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)}); new String[] {groupId});
RecipientFactory.clearCache(context); RecipientFactory.clearCache(context);
notifyDatabaseListeners(); notifyDatabaseListeners();
} }
public void updateMembers(byte[] id, List<Address> members) { public void updateMembers(String groupId, List<Address> members) {
Collections.sort(members);
ContentValues contents = new ContentValues(); ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(members, ',')); contents.put(MEMBERS, Address.toSerializedList(members, ','));
contents.put(ACTIVE, 1); contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)}); new String[] {groupId});
} }
public void remove(byte[] id, Address source) { public void remove(String groupId, Address source) {
List<Address> currentMembers = getCurrentMembers(id); List<Address> currentMembers = getCurrentMembers(groupId);
currentMembers.remove(source); currentMembers.remove(source);
ContentValues contents = new ContentValues(); ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(currentMembers, ',')); contents.put(MEMBERS, Address.toSerializedList(currentMembers, ','));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)}); new String[] {groupId});
} }
private List<Address> getCurrentMembers(byte[] id) { private List<Address> getCurrentMembers(String groupId) {
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS}, cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
GROUP_ID + " = ?", GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)}, new String[] {groupId},
null, null, null); null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
@ -223,16 +250,16 @@ public class GroupDatabase extends Database {
} }
} }
public boolean isActive(byte[] id) { public boolean isActive(String groupId) {
GroupRecord record = getGroup(id); GroupRecord record = getGroup(groupId);
return record != null && record.isActive(); return record != null && record.isActive();
} }
public void setActive(byte[] id, boolean active) { public void setActive(String groupId, boolean active) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(ACTIVE, active ? 1 : 0); values.put(ACTIVE, active ? 1 : 0);
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {GroupUtil.getEncodedId(id)}); database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId});
} }
@ -273,7 +300,8 @@ public class GroupDatabase extends Database {
cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)), cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)), cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)),
cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1, cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1,
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST))); cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)),
cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1);
} }
public void close() { public void close() {
@ -294,10 +322,11 @@ public class GroupDatabase extends Database {
private final String avatarContentType; private final String avatarContentType;
private final String relay; private final String relay;
private final boolean active; private final boolean active;
private final boolean mms;
public GroupRecord(String id, String title, String members, byte[] avatar, public GroupRecord(String id, String title, String members, byte[] avatar,
long avatarId, byte[] avatarKey, String avatarContentType, long avatarId, byte[] avatarKey, String avatarContentType,
String relay, boolean active, byte[] avatarDigest) String relay, boolean active, byte[] avatarDigest, boolean mms)
{ {
this.id = id; this.id = id;
this.title = title; this.title = title;
@ -309,6 +338,7 @@ public class GroupDatabase extends Database {
this.avatarContentType = avatarContentType; this.avatarContentType = avatarContentType;
this.relay = relay; this.relay = relay;
this.active = active; this.active = active;
this.mms = mms;
} }
public byte[] getId() { public byte[] getId() {
@ -358,5 +388,9 @@ public class GroupDatabase extends Database {
public boolean isActive() { public boolean isActive() {
return active; return active;
} }
public boolean isMms() {
return mms;
}
} }
} }

View File

@ -1,150 +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 <http://www.gnu.org/licenses/>.
*/
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.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import com.google.android.mms.pdu_alt.PduHeaders;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
import java.util.List;
public class MmsAddressDatabase extends Database {
private static final String TAG = MmsAddressDatabase.class.getSimpleName();
private static final String TABLE_NAME = "mms_addresses";
private static final String ID = "_id";
private static final String MMS_ID = "mms_id";
private static final String TYPE = "type";
private static final String ADDRESS = "address";
private static final String ADDRESS_CHARSET = "address_charset";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + TYPE + " INTEGER, " + ADDRESS + " TEXT, " +
ADDRESS_CHARSET + " INTEGER);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_addresses_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
};
public MmsAddressDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
}
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.serialize());
contentValues.put(ADDRESS_CHARSET, "UTF-8");
database.insert(TABLE_NAME, null, contentValues);
}
private void insertAddress(long messageId, int type, @NonNull List<Address> addresses) {
for (Address address : addresses) {
insertAddress(messageId, type, address);
}
}
public void insertAddressesForId(long messageId, MmsAddresses addresses) {
if (addresses.getFrom() != null) {
insertAddress(messageId, PduHeaders.FROM, addresses.getFrom());
}
insertAddress(messageId, PduHeaders.TO, addresses.getTo());
insertAddress(messageId, PduHeaders.CC, addresses.getCc());
insertAddress(messageId, PduHeaders.BCC, addresses.getBcc());
}
public MmsAddresses getAddressesForId(long messageId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
Address from = null;
List<Address> to = new LinkedList<>();
List<Address> cc = new LinkedList<>();
List<Address> 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));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (type == PduHeaders.FROM) from = address;
if (type == PduHeaders.TO) to.add(address);
if (type == PduHeaders.CC) cc.add(address);
if (type == PduHeaders.BCC) bcc.add(address);
}
} finally {
if (cursor != null)
cursor.close();
}
return new MmsAddresses(from, to, cc, bcc);
}
public List<Address> getAddressesListForId(long messageId) {
List<Address> results = new LinkedList<>();
MmsAddresses addresses = getAddressesForId(messageId);
if (addresses.getFrom() != null) {
results.add(addresses.getFrom());
}
results.addAll(addresses.getTo());
results.addAll(addresses.getCc());
results.addAll(addresses.getBcc());
return results;
}
public Recipients getRecipientsForId(long messageId) {
List<Address> addresses = getAddressesListForId(messageId);
List<Recipient> results = new LinkedList<>();
for (Address address : addresses) {
if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(address.serialize())) {
results.add(RecipientFactory.getRecipientFor(context, address, false));
}
}
return RecipientFactory.getRecipientsFor(context, results, false);
}
public void deleteAddressesForId(long messageId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {messageId+""});
}
public void deleteAllAddresses() {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null);
}
}

View File

@ -1,56 +0,0 @@
package org.thoughtcrime.securesms.database;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.LinkedList;
import java.util.List;
public class MmsAddresses {
private final @Nullable Address from;
private final @NonNull List<Address> to;
private final @NonNull List<Address> cc;
private final @NonNull List<Address> bcc;
public MmsAddresses(@Nullable Address from, @NonNull List<Address> to,
@NonNull List<Address> cc, @NonNull List<Address> bcc)
{
this.from = from;
this.to = to;
this.cc = cc;
this.bcc = bcc;
}
@NonNull
public List<Address> getTo() {
return to;
}
@NonNull
public List<Address> getCc() {
return cc;
}
@NonNull
public List<Address> getBcc() {
return bcc;
}
@Nullable
public Address getFrom() {
return from;
}
public static MmsAddresses forTo(@NonNull List<Address> to) {
return new MmsAddresses(null, to, new LinkedList<Address>(), new LinkedList<Address>());
}
public static MmsAddresses forBcc(@NonNull List<Address> bcc) {
return new MmsAddresses(null, new LinkedList<Address>(), new LinkedList<Address>(), bcc);
}
public static MmsAddresses forFrom(@NonNull Address from) {
return new MmsAddresses(from, new LinkedList<Address>(), new LinkedList<Address>(), new LinkedList<Address>());
}
}

View File

@ -30,7 +30,6 @@ import android.util.Pair;
import com.google.android.mms.pdu_alt.NotificationInd; import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders; import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -57,12 +56,10 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
@ -195,34 +192,30 @@ public class MmsDatabase extends MessagingDatabase {
} }
public void incrementDeliveryReceiptCount(SyncMessageId messageId) { public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context); SQLiteDatabase database = databaseHelper.getWritableDatabase();
SQLiteDatabase database = databaseHelper.getWritableDatabase(); Cursor cursor = null;
Cursor cursor = null; boolean found = false;
boolean found = false;
try { try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
for (Address theirAddress : addresses) { if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
Address ourAddress = messageId.getAddress(); long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { found = true;
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
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 " + DatabaseFactory.getThreadDatabase(context).update(threadId, false);
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?", notifyConversationListeners(threadId);
new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
} }
} }
} }
@ -257,66 +250,20 @@ public class MmsDatabase extends MessagingDatabase {
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException { private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException {
if (retrieved.getGroupId() != null) { if (retrieved.getGroupId() != null) {
Recipients groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {retrieved.getGroupId()}, true); Recipient groupRecipients = RecipientFactory.getRecipientFor(context, retrieved.getGroupId(), true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
}
String localNumber;
Set<Address> group = new HashSet<>();
if (retrieved.getAddresses().getFrom() == null) {
throw new MmsException("FROM value in PduHeaders did not exist.");
}
group.add(retrieved.getAddresses().getFrom());
if (TextSecurePreferences.isPushRegistered(context)) {
localNumber = TextSecurePreferences.getLocalNumber(context);
} else { } else {
localNumber = ServiceUtil.getTelephonyManager(context).getLine1Number(); Recipient sender = RecipientFactory.getRecipientFor(context, retrieved.getFrom(), true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(sender);
} }
for (Address cc : retrieved.getAddresses().getCc()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc.serialize());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
{
group.add(cc);
}
}
if (retrieved.getAddresses().getTo().size() > 1) {
for (Address to : retrieved.getAddresses().getTo()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to.serialize());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
{
group.add(to);
}
}
}
Recipients recipients = RecipientFactory.getRecipientsFor(context, group.toArray(new Address[0]), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
} }
private long getThreadIdFor(@NonNull NotificationInd notification) { private long getThreadIdFor(@NonNull NotificationInd notification) {
String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null
? Util.toIsoString(notification.getFrom().getTextString()) ? Util.toIsoString(notification.getFrom().getTextString())
: ""; : "";
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, fromString)}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, fromString), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
} }
private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) { private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) {
@ -489,39 +436,35 @@ public class MmsDatabase extends MessagingDatabase {
} }
public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long expireStarted) { public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long expireStarted) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
List<Pair<Long, Long>> expiring = new LinkedList<>(); List<Pair<Long, Long>> expiring = new LinkedList<>();
Cursor cursor = null; Cursor cursor = null;
try { try {
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); cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
for (Address theirAddress : addresses) { if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
Address ourAddress = messageId.getAddress(); 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()) { ContentValues values = new ContentValues();
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); values.put(READ, 1);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
ContentValues values = new ContentValues(); if (expiresIn > 0) {
values.put(READ, 1); values.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
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);
} }
database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
} }
} }
} finally { } finally {
@ -592,7 +535,6 @@ public class MmsDatabase extends MessagingDatabase {
public OutgoingMediaMessage getOutgoingMessage(MasterSecret masterSecret, long messageId) public OutgoingMediaMessage getOutgoingMessage(MasterSecret masterSecret, long messageId)
throws MmsException, NoSuchMessageException throws MmsException, NoSuchMessageException
{ {
MmsAddressDatabase addr = DatabaseFactory.getMmsAddressDatabase(context);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
Cursor cursor = null; Cursor cursor = null;
@ -600,31 +542,27 @@ public class MmsDatabase extends MessagingDatabase {
cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)});
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId)); List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId));
MmsAddresses addresses = addr.getAddressesForId(messageId); String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
List<Address> destinations = new LinkedList<>(); String body = getDecryptedBody(masterSecret, messageText, outboxType);
String body = getDecryptedBody(masterSecret, messageText, outboxType); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
destinations.addAll(addresses.getBcc()); Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(address), false);
destinations.addAll(addresses.getCc());
destinations.addAll(addresses.getTo());
Recipients recipients = RecipientFactory.getRecipientsFor(context, destinations.toArray(new Address[0]), false);
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipients, body, attachments, timestamp, 0); return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0);
} else if (Types.isExpirationTimerUpdate(outboxType)) { } else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipients, timestamp, expiresIn); return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
} }
OutgoingMediaMessage message = new OutgoingMediaMessage(recipients, body, attachments, timestamp, subscriptionId, expiresIn, OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType);
!addresses.getBcc().isEmpty() ? ThreadDatabase.DistributionTypes.BROADCAST :
ThreadDatabase.DistributionTypes.DEFAULT);
if (Types.isSecureType(outboxType)) { if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message); return new OutgoingSecureMediaMessage(message);
} }
@ -645,7 +583,7 @@ public class MmsDatabase extends MessagingDatabase {
try { try {
OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId); OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId);
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getAddress().serialize()); contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis()); contentValues.put(DATE_SENT, request.getSentTimeMillis());
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.ENCRYPTION_SYMMETRIC_BIT); contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.ENCRYPTION_SYMMETRIC_BIT);
contentValues.put(THREAD_ID, getThreadIdForMessage(messageId)); contentValues.put(THREAD_ID, getThreadIdForMessage(messageId));
@ -674,7 +612,6 @@ public class MmsDatabase extends MessagingDatabase {
} }
return insertMediaMessage(new MasterSecretUnion(masterSecret), return insertMediaMessage(new MasterSecretUnion(masterSecret),
MmsAddresses.forTo(request.getRecipients().getAddressesList()),
request.getBody(), request.getBody(),
attachments, attachments,
contentValues, contentValues,
@ -703,7 +640,7 @@ public class MmsDatabase extends MessagingDatabase {
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, retrieved.getSentTimeMillis()); contentValues.put(DATE_SENT, retrieved.getSentTimeMillis());
contentValues.put(ADDRESS, retrieved.getAddresses().getFrom().serialize()); contentValues.put(ADDRESS, retrieved.getFrom().serialize());
contentValues.put(MESSAGE_BOX, mailbox); contentValues.put(MESSAGE_BOX, mailbox);
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
@ -725,9 +662,7 @@ public class MmsDatabase extends MessagingDatabase {
return Optional.absent(); return Optional.absent();
} }
long messageId = insertMediaMessage(masterSecret, retrieved.getAddresses(), long messageId = insertMediaMessage(masterSecret, retrieved.getBody(), retrieved.getAttachments(), contentValues, null);
retrieved.getBody(), retrieved.getAttachments(),
contentValues, null);
if (!Types.isExpirationTimerUpdate(mailbox)) { if (!Types.isExpirationTimerUpdate(mailbox)) {
DatabaseFactory.getThreadDatabase(context).setUnread(threadId); DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
@ -789,8 +724,7 @@ public class MmsDatabase extends MessagingDatabase {
} }
public Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) { public Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
long threadId = getThreadIdFor(notification); long threadId = getThreadIdFor(notification);
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues); ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues);
@ -806,9 +740,7 @@ public class MmsDatabase extends MessagingDatabase {
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType()); contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
if (notification.getFrom() != null) { if (notification.getFrom() != null) {
contentBuilder.add(ADDRESS, notification.getFrom().getTextString()); contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize());
} else {
contentBuilder.add(ADDRESS, null);
} }
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE); contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE);
@ -823,10 +755,6 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = db.insert(TABLE_NAME, null, contentValues); long messageId = db.insert(TABLE_NAME, null, contentValues);
if (notification.getFrom() != null) {
addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString()))));
}
return new Pair<>(messageId, threadId); return new Pair<>(messageId, threadId);
} }
@ -864,18 +792,6 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT; type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
} }
List<Address> recipientNumbers = message.getRecipients().getAddressesList();
MmsAddresses addresses;
if (!message.getRecipients().isSingleRecipient() &&
message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST)
{
addresses = MmsAddresses.forBcc(recipientNumbers);
} else {
addresses = MmsAddresses.forTo(recipientNumbers);
}
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, message.getSentTimeMillis()); contentValues.put(DATE_SENT, message.getSentTimeMillis());
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ); contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
@ -886,15 +802,10 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn()); contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize());
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipient().getAddress()));
if (message.getRecipients().isSingleRecipient()) { long messageId = insertMediaMessage(masterSecret, message.getBody(), message.getAttachments(), contentValues, insertListener);
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipients().getPrimaryRecipient().getAddress()));
}
contentValues.remove(ADDRESS);
long messageId = insertMediaMessage(masterSecret, addresses, message.getBody(),
message.getAttachments(), contentValues, insertListener);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
jobManager.add(new TrimThreadJob(context, threadId)); jobManager.add(new TrimThreadJob(context, threadId));
@ -928,7 +839,6 @@ public class MmsDatabase extends MessagingDatabase {
} }
private long insertMediaMessage(@NonNull MasterSecretUnion masterSecret, private long insertMediaMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull MmsAddresses addresses,
@Nullable String body, @Nullable String body,
@NonNull List<Attachment> attachments, @NonNull List<Attachment> attachments,
@NonNull ContentValues contentValues, @NonNull ContentValues contentValues,
@ -937,7 +847,6 @@ public class MmsDatabase extends MessagingDatabase {
{ {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context);
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) || if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) ||
Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX))) Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)))
@ -953,7 +862,6 @@ public class MmsDatabase extends MessagingDatabase {
try { try {
long messageId = db.insert(TABLE_NAME, null, contentValues); long messageId = db.insert(TABLE_NAME, null, contentValues);
addressDatabase.insertAddressesForId(messageId, addresses);
partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments); partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments);
db.setTransactionSuccessful(); db.setTransactionSuccessful();
@ -972,10 +880,8 @@ public class MmsDatabase extends MessagingDatabase {
public boolean delete(long messageId) { public boolean delete(long messageId) {
long threadId = getThreadIdForMessage(messageId); long threadId = getThreadIdForMessage(messageId);
MmsAddressDatabase addrDatabase = DatabaseFactory.getMmsAddressDatabase(context);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
attachmentDatabase.deleteAttachmentsForMessage(messageId); attachmentDatabase.deleteAttachmentsForMessage(messageId);
addrDatabase.deleteAddressesForId(messageId);
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
@ -993,7 +899,7 @@ public class MmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingMediaMessage message, long threadId) { private boolean isDuplicate(IncomingMediaMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?", Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom().serialize(), String.valueOf(threadId)}, new String[]{String.valueOf(message.getSentTimeMillis()), message.getFrom().serialize(), String.valueOf(threadId)},
null, null, null, "1"); null, null, null, "1");
try { try {
@ -1058,7 +964,6 @@ public class MmsDatabase extends MessagingDatabase {
public void deleteAllThreads() { public void deleteAllThreads() {
DatabaseFactory.getAttachmentDatabase(context).deleteAllAttachments(); DatabaseFactory.getAttachmentDatabase(context).deleteAllAttachments();
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null); database.delete(TABLE_NAME, null, null);
@ -1139,8 +1044,7 @@ public class MmsDatabase extends MessagingDatabase {
public MessageRecord getCurrent() { public MessageRecord getCurrent() {
SlideDeck slideDeck = new SlideDeck(context, message.getAttachments()); SlideDeck slideDeck = new SlideDeck(context, message.getAttachments());
return new MediaMmsMessageRecord(context, id, message.getRecipients(), return new MediaMmsMessageRecord(context, id, message.getRecipient(), message.getRecipient(),
message.getRecipients().getPrimaryRecipient(),
1, System.currentTimeMillis(), System.currentTimeMillis(), 1, System.currentTimeMillis(), System.currentTimeMillis(),
0, threadId, new DisplayRecord.Body(message.getBody(), true), 0, threadId, new DisplayRecord.Body(message.getBody(), true),
slideDeck, slideDeck.getSlides().size(), slideDeck, slideDeck.getSlides().size(),
@ -1192,7 +1096,7 @@ public class MmsDatabase extends MessagingDatabase {
long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
Recipients recipients = getRecipientsFor(address); Recipient recipient = getRecipientFor(address);
String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION)); String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION));
String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID)); String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID));
@ -1214,7 +1118,7 @@ public class MmsDatabase extends MessagingDatabase {
SlideDeck slideDeck = new SlideDeck(context, new MmsNotificationAttachment(status, messageSize)); SlideDeck slideDeck = new SlideDeck(context, new MmsNotificationAttachment(status, messageSize));
return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), return new NotificationMmsMessageRecord(context, id, recipient, recipient,
addressDeviceId, dateSent, dateReceived, receiptCount, threadId, addressDeviceId, dateSent, dateReceived, receiptCount, threadId,
contentLocationBytes, messageSize, expiry, status, contentLocationBytes, messageSize, expiry, status,
transactionIdBytes, mailbox, subscriptionId, slideDeck); transactionIdBytes, mailbox, subscriptionId, slideDeck);
@ -1237,18 +1141,18 @@ public class MmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
Recipients recipients = getRecipientsFor(address); Recipient recipient = getRecipientFor(address);
List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument); List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument);
List<NetworkFailure> networkFailures = getFailures(networkDocument); List<NetworkFailure> networkFailures = getFailures(networkDocument);
SlideDeck slideDeck = getSlideDeck(cursor); SlideDeck slideDeck = getSlideDeck(cursor);
return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), return new MediaMmsMessageRecord(context, id, recipient, recipient,
addressDeviceId, dateSent, dateReceived, receiptCount, addressDeviceId, dateSent, dateReceived, receiptCount,
threadId, body, slideDeck, partCount, box, mismatches, threadId, body, slideDeck, partCount, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted); networkFailures, subscriptionId, expiresIn, expireStarted);
} }
private Recipients getRecipientsFor(String serialized) { private Recipient getRecipientFor(String serialized) {
Address address; Address address;
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) { if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
@ -1257,7 +1161,7 @@ public class MmsDatabase extends MessagingDatabase {
address = Address.fromSerialized(serialized); address = Address.fromSerialized(serialized);
} }
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true); return RecipientFactory.getRecipientFor(context, address, true);
} }
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) { private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {

View File

@ -8,8 +8,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.File; import java.io.File;
@ -34,8 +34,8 @@ public class PlaintextBackupImporter {
XmlBackup.XmlBackupItem item; XmlBackup.XmlBackupItem item;
while ((item = backup.getNext()) != null) { while ((item = backup.getNext()) != null) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, item.getAddress())}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, item.getAddress()), false);
long threadId = threads.getThreadIdFor(recipients); long threadId = threads.getThreadIdFor(recipient);
SQLiteStatement statement = db.createInsertStatement(transaction); SQLiteStatement statement = db.createInsertStatement(transaction);
if (item.getAddress() == null || item.getAddress().equals("null")) if (item.getAddress() == null || item.getAddress().equals("null"))

View File

@ -12,13 +12,10 @@ import android.util.Log;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
import java.util.List;
public class RecipientPreferenceDatabase extends Database { public class RecipientPreferenceDatabase extends Database {
private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName(); private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName();
@ -26,7 +23,7 @@ public class RecipientPreferenceDatabase extends Database {
private static final String TABLE_NAME = "recipient_preferences"; private static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id"; private static final String ID = "_id";
private static final String ADDRESSES = "recipient_ids"; private static final String ADDRESS = "recipient_ids";
private static final String BLOCK = "block"; private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification"; private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate"; private static final String VIBRATE = "vibrate";
@ -57,7 +54,7 @@ public class RecipientPreferenceDatabase extends Database {
public static final String CREATE_TABLE = public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " + " (" + ID + " INTEGER PRIMARY KEY, " +
ADDRESSES + " TEXT UNIQUE, " + ADDRESS + " TEXT UNIQUE, " +
BLOCK + " INTEGER DEFAULT 0," + BLOCK + " INTEGER DEFAULT 0," +
NOTIFICATION + " TEXT DEFAULT NULL, " + NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
@ -74,7 +71,7 @@ public class RecipientPreferenceDatabase extends Database {
public Cursor getBlocked() { public Cursor getBlocked() {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESSES}, BLOCK + " = 1", Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1",
null, null, null, null, null); null, null, null, null, null);
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(RECIPIENT_PREFERENCES_URI)); cursor.setNotificationUri(context.getContentResolver(), Uri.parse(RECIPIENT_PREFERENCES_URI));
@ -85,14 +82,13 @@ public class RecipientPreferenceDatabase extends Database {
return new BlockedReader(context, cursor); return new BlockedReader(context, cursor);
} }
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull Address[] addresses) {
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull Address address) {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = database.query(TABLE_NAME, null, ADDRESSES + " = ?", cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null);
new String[] {Address.toSerializedList(Arrays.asList(addresses), ' ')},
null, null, null);
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
@ -128,69 +124,68 @@ public class RecipientPreferenceDatabase extends Database {
} }
} }
public void setColor(Recipients recipients, MaterialColor color) { public void setColor(Recipient recipient, MaterialColor color) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(COLOR, color.serialize()); values.put(COLOR, color.serialize());
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setDefaultSubscriptionId(@NonNull Recipients recipients, int defaultSubscriptionId) { public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId); values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
EventBus.getDefault().post(new RecipientPreferenceEvent(recipients)); EventBus.getDefault().post(new RecipientPreferenceEvent(recipient));
} }
public void setBlocked(Recipients recipients, boolean blocked) { public void setBlocked(Recipient recipient, boolean blocked) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(BLOCK, blocked ? 1 : 0); values.put(BLOCK, blocked ? 1 : 0);
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setRingtone(Recipients recipients, @Nullable Uri notification) { public void setRingtone(Recipient recipient, @Nullable Uri notification) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(NOTIFICATION, notification == null ? null : notification.toString()); values.put(NOTIFICATION, notification == null ? null : notification.toString());
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setVibrate(Recipients recipients, @NonNull VibrateState enabled) { public void setVibrate(Recipient recipient, @NonNull VibrateState enabled) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(VIBRATE, enabled.getId()); values.put(VIBRATE, enabled.getId());
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setMuted(Recipients recipients, long until) { public void setMuted(Recipient recipient, long until) {
Log.w(TAG, "Setting muted until: " + until); Log.w(TAG, "Setting muted until: " + until);
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(MUTE_UNTIL, until); values.put(MUTE_UNTIL, until);
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setSeenInviteReminder(Recipients recipients, boolean seen) { public void setSeenInviteReminder(Recipient recipient, boolean seen) {
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0); values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0);
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
public void setExpireMessages(Recipients recipients, int expiration) { public void setExpireMessages(Recipient recipient, int expiration) {
recipients.setExpireMessages(expiration); recipient.setExpireMessages(expiration);
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(EXPIRE_MESSAGES, expiration); values.put(EXPIRE_MESSAGES, expiration);
updateOrInsert(recipients, values); updateOrInsert(recipient, values);
} }
private void updateOrInsert(Recipients recipients, ContentValues contentValues) { private void updateOrInsert(Recipient recipient, ContentValues contentValues) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction(); database.beginTransaction();
List<Address> addresses = recipients.getAddressesList(); int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?",
String serializedAddresses = Address.toSerializedList(addresses, ' '); new String[] {recipient.getAddress().serialize()});
int updated = database.update(TABLE_NAME, contentValues, ADDRESSES + " = ?", new String[]{serializedAddresses});
if (updated < 1) { if (updated < 1) {
contentValues.put(ADDRESSES, serializedAddresses); contentValues.put(ADDRESS, recipient.getAddress().serialize());
database.insert(TABLE_NAME, null, contentValues); database.insert(TABLE_NAME, null, contentValues);
} }
@ -271,14 +266,12 @@ public class RecipientPreferenceDatabase extends Database {
this.cursor = cursor; this.cursor = cursor;
} }
public @NonNull Recipients getCurrent() { public @NonNull Recipient getCurrent() {
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES)); String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
List<Address> addressList = Address.fromSerializedList(serialized, ' '); return RecipientFactory.getRecipientFor(context, Address.fromSerialized(serialized), false);
return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), false);
} }
public @Nullable Recipients getNext() { public @Nullable Recipient getNext() {
if (!cursor.moveToNext()) { if (!cursor.moveToNext()) {
return null; return null;
} }
@ -289,14 +282,14 @@ public class RecipientPreferenceDatabase extends Database {
public static class RecipientPreferenceEvent { public static class RecipientPreferenceEvent {
private final Recipients recipients; private final Recipient recipient;
RecipientPreferenceEvent(Recipients recipients) { public RecipientPreferenceEvent(Recipient recipients) {
this.recipients = recipients; this.recipient = recipients;
} }
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
} }
} }

View File

@ -34,8 +34,8 @@ import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.TrimThreadJob; import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@ -457,8 +457,8 @@ public class SmsDatabase extends MessagingDatabase {
} }
private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) { private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {address}, true); Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
ContentValues values = new ContentValues(6); ContentValues values = new ContentValues(6);
values.put(ADDRESS, address.serialize()); values.put(ADDRESS, address.serialize());
@ -506,14 +506,14 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, true); Recipient recipient = RecipientFactory.getRecipientFor(context, message.getSender(), true);
Recipients groupRecipients; Recipient groupRecipient;
if (message.getGroupId() == null) { if (message.getGroupId() == null) {
groupRecipients = null; groupRecipient = null;
} else { } else {
groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getGroupId()}, true); groupRecipient = RecipientFactory.getRecipientFor(context, message.getGroupId(), true);
} }
boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) || boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
@ -522,8 +522,8 @@ public class SmsDatabase extends MessagingDatabase {
long threadId; long threadId;
if (groupRecipients == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients); else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
ContentValues values = new ContentValues(6); ContentValues values = new ContentValues(6);
values.put(ADDRESS, message.getSender().serialize()); values.put(ADDRESS, message.getSender().serialize());
@ -560,7 +560,7 @@ public class SmsDatabase extends MessagingDatabase {
} }
if (message.getSubscriptionId() != -1) { if (message.getSubscriptionId() != -1) {
DatabaseFactory.getRecipientPreferenceDatabase(context).setDefaultSubscriptionId(recipients, message.getSubscriptionId()); DatabaseFactory.getRecipientPreferenceDatabase(context).setDefaultSubscriptionId(recipient, message.getSubscriptionId());
} }
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
@ -589,7 +589,7 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Address address = message.getRecipients().getPrimaryRecipient().getAddress(); Address address = message.getRecipient().getAddress();
ContentValues contentValues = new ContentValues(6); ContentValues contentValues = new ContentValues(6);
contentValues.put(ADDRESS, address.serialize()); contentValues.put(ADDRESS, address.serialize());
@ -783,7 +783,7 @@ public class SmsDatabase extends MessagingDatabase {
public MessageRecord getCurrent() { public MessageRecord getCurrent() {
return new SmsMessageRecord(context, id, new DisplayRecord.Body(message.getMessageBody(), true), return new SmsMessageRecord(context, id, new DisplayRecord.Body(message.getMessageBody(), true),
message.getRecipients(), message.getRecipients().getPrimaryRecipient(), message.getRecipient(), message.getRecipient(),
1, System.currentTimeMillis(), System.currentTimeMillis(), 1, System.currentTimeMillis(), System.currentTimeMillis(),
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), 0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
threadId, 0, new LinkedList<IdentityKeyMismatch>(), threadId, 0, new LinkedList<IdentityKeyMismatch>(),
@ -828,21 +828,17 @@ public class SmsDatabase extends MessagingDatabase {
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument); List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
Recipients recipients = getRecipientsFor(address); Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
DisplayRecord.Body body = getBody(cursor); DisplayRecord.Body body = getBody(cursor);
return new SmsMessageRecord(context, messageId, body, recipients, return new SmsMessageRecord(context, messageId, body, recipient,
recipients.getPrimaryRecipient(), recipient,
addressDeviceId, addressDeviceId,
dateSent, dateReceived, receiptCount, type, dateSent, dateReceived, receiptCount, type,
threadId, status, mismatches, subscriptionId, threadId, status, mismatches, subscriptionId,
expiresIn, expireStarted); expiresIn, expireStarted);
} }
private Recipients getRecipientsFor(Address address) {
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
}
private List<IdentityKeyMismatch> getMismatches(String document) { private List<IdentityKeyMismatch> getMismatches(String document) {
try { try {
if (!TextUtils.isEmpty(document)) { if (!TextUtils.isEmpty(document)) {

View File

@ -22,16 +22,19 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteStatement;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
public class SmsMigrator { public class SmsMigrator {
@ -140,21 +143,21 @@ public class SmsMigrator {
} }
} }
private static Recipients getOurRecipients(Context context, String theirRecipients) { private static @Nullable Set<Recipient> getOurRecipients(Context context, String theirRecipients) {
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " "); StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
List<Address> addressList = new LinkedList<>(); Set<Recipient> recipientList = new HashSet<>();
while (tokenizer.hasMoreTokens()) { while (tokenizer.hasMoreTokens()) {
String theirRecipientId = tokenizer.nextToken(); String theirRecipientId = tokenizer.nextToken();
String address = getTheirCanonicalAddress(context, theirRecipientId); String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address != null) { if (address != null) {
addressList.add(Address.fromExternal(context, address)); recipientList.add(RecipientFactory.getRecipientFor(context, Address.fromExternal(context, address), true));
} }
} }
if (addressList.isEmpty()) return null; if (recipientList.isEmpty()) return null;
else return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), true); else return recipientList;
} }
private static String encrypt(MasterSecret masterSecret, String body) private static String encrypt(MasterSecret masterSecret, String body)
@ -221,16 +224,30 @@ public class SmsMigrator {
cursor = context.getContentResolver().query(threadListUri, null, null, null, "date ASC"); cursor = context.getContentResolver().query(threadListUri, null, null, null, "date ASC");
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id")); long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Recipients ourRecipients = getOurRecipients(context, theirRecipients); Set<Recipient> ourRecipients = getOurRecipients(context, theirRecipients);
ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0); ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0);
if (ourRecipients != null) { if (ourRecipients != null) {
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients); if (ourRecipients.size() == 1) {
migrateConversation(context, masterSecret, long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next());
listener, progress, migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId);
theirThreadId, ourThreadId); } else if (ourRecipients.size() > 1) {
ourRecipients.add(RecipientFactory.getRecipientFor(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
List<Address> memberAddresses = new LinkedList<>();
for (Recipient recipient : ourRecipients) {
memberAddresses.add(recipient.getAddress());
}
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, true);
Recipient ourGroupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(ourGroupId), true);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId);
}
} }
progress.incrementPrimaryComplete(); progress.incrementPrimaryComplete();

View File

@ -36,13 +36,12 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -55,7 +54,7 @@ public class ThreadDatabase extends Database {
public static final String ID = "_id"; public static final String ID = "_id";
public static final String DATE = "date"; public static final String DATE = "date";
public static final String MESSAGE_COUNT = "message_count"; public static final String MESSAGE_COUNT = "message_count";
public static final String ADDRESSES = "recipient_ids"; public static final String ADDRESS = "recipient_ids";
public static final String SNIPPET = "snippet"; public static final String SNIPPET = "snippet";
private static final String SNIPPET_CHARSET = "snippet_cs"; private static final String SNIPPET_CHARSET = "snippet_cs";
public static final String READ = "read"; public static final String READ = "read";
@ -71,7 +70,7 @@ public class ThreadDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESSES + " TEXT, " + SNIPPET + " TEXT, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " +
SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " +
@ -80,7 +79,7 @@ public class ThreadDatabase extends Database {
LAST_SEEN + " INTEGER DEFAULT 0);"; LAST_SEEN + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESSES + ");", "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
}; };
@ -88,14 +87,14 @@ public class ThreadDatabase extends Database {
super(context, databaseHelper); super(context, databaseHelper);
} }
private long createThreadForRecipients(Address[] addresses, int recipientCount, int distributionType) { private long createThreadForRecipient(Address address, boolean group, int distributionType) {
ContentValues contentValues = new ContentValues(4); ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis(); long date = System.currentTimeMillis();
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(ADDRESSES, Address.toSerializedList(Arrays.asList(addresses), ' ')); contentValues.put(ADDRESS, address.serialize());
if (recipientCount > 1) if (group)
contentValues.put(TYPE, distributionType); contentValues.put(TYPE, distributionType);
contentValues.put(MESSAGE_COUNT, 0); contentValues.put(MESSAGE_COUNT, 0);
@ -272,6 +271,22 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners(); notifyConversationListListeners();
} }
public int getDistributionType(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
}
return DistributionTypes.DEFAULT;
} finally {
if (cursor != null) cursor.close();
}
}
public Cursor getFilteredConversationList(List<Address> filter) { public Cursor getFilteredConversationList(List<Address> filter) {
if (filter == null || filter.size() == 0) if (filter == null || filter.size() == 0)
return null; return null;
@ -281,11 +296,11 @@ public class ThreadDatabase extends Database {
List<Cursor> cursors = new LinkedList<>(); List<Cursor> cursors = new LinkedList<>();
for (List<Address> addresses : partitionedAddresses) { for (List<Address> addresses : partitionedAddresses) {
String selection = ADDRESSES + " = ?"; String selection = ADDRESS + " = ?";
String[] selectionArgs = new String[addresses.size()]; String[] selectionArgs = new String[addresses.size()];
for (int i=0;i<addresses.size()-1;i++) for (int i=0;i<addresses.size()-1;i++)
selection += (" OR " + ADDRESSES + " = ?"); selection += (" OR " + ADDRESS + " = ?");
int i= 0; int i= 0;
for (Address address : addresses) { for (Address address : addresses) {
@ -410,12 +425,11 @@ public class ThreadDatabase extends Database {
deleteAllThreads(); deleteAllThreads();
} }
public long getThreadIdIfExistsFor(Recipients recipients) { public long getThreadIdIfExistsFor(Recipient recipient) {
List<Address> addresses = Arrays.asList(recipients.getAddresses()); SQLiteDatabase db = databaseHelper.getReadableDatabase();
SQLiteDatabase db = databaseHelper.getReadableDatabase(); String where = ADDRESS + " = ?";
String where = ADDRESSES + " = ?"; String[] recipientsArg = new String[] {recipient.getAddress().serialize()};
String[] recipientsArg = new String[]{Address.toSerializedList(addresses, ' ')}; Cursor cursor = null;
Cursor cursor = null;
try { try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
@ -430,15 +444,14 @@ public class ThreadDatabase extends Database {
} }
} }
public long getThreadIdFor(Recipients recipients) { public long getThreadIdFor(Recipient recipient) {
return getThreadIdFor(recipients, DistributionTypes.DEFAULT); return getThreadIdFor(recipient, DistributionTypes.DEFAULT);
} }
public long getThreadIdFor(Recipients recipients, int distributionType) { public long getThreadIdFor(Recipient recipient, int distributionType) {
Address[] addresses = recipients.getAddresses();
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESSES + " = ?"; String where = ADDRESS + " = ?";
String[] recipientsArg = new String[]{Address.toSerializedList(Arrays.asList(addresses), ' ')}; String[] recipientsArg = new String[]{recipient.getAddress().serialize()};
Cursor cursor = null; Cursor cursor = null;
try { try {
@ -447,7 +460,7 @@ public class ThreadDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else { } else {
return createThreadForRecipients(addresses, recipients.getRecipientsList().size(), distributionType); return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType);
} }
} finally { } finally {
if (cursor != null) if (cursor != null)
@ -455,7 +468,7 @@ public class ThreadDatabase extends Database {
} }
} }
public @Nullable Recipients getRecipientsForThreadId(long threadId) { public @Nullable Recipient getRecipientForThreadId(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
@ -463,8 +476,8 @@ public class ThreadDatabase extends Database {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null); cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
List<Address> addresses = Address.fromSerializedList(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES)), ' '); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
return RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), false); return RecipientFactory.getRecipientFor(context, address, false);
} }
} finally { } finally {
if (cursor != null) if (cursor != null)
@ -561,9 +574,9 @@ public class ThreadDatabase extends Database {
} }
public ThreadRecord getCurrent() { public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
List<Address> addresses = Address.fromSerializedList(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESSES)), ' '); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS)));
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), true); Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
DisplayRecord.Body body = getPlaintextBody(cursor); DisplayRecord.Body body = getPlaintextBody(cursor);
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
@ -578,7 +591,7 @@ public class ThreadDatabase extends Database {
long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN)); long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN));
Uri snippetUri = getSnippetUri(cursor); Uri snippetUri = getSnippetUri(cursor);
return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1, return new ThreadRecord(context, body, snippetUri, recipient, date, count, read == 1,
threadId, receiptCount, status, type, distributionType, archived, threadId, receiptCount, status, type, distributionType, archived,
expiresIn, lastSeen); expiresIn, lastSeen);
} }

View File

@ -42,7 +42,7 @@ public class ConversationListLoader extends AbstractCursorLoader {
if (archivedCount > 0) { if (archivedCount > 0) {
MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] { MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT, ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.ADDRESSES, ThreadDatabase.SNIPPET, ThreadDatabase.READ, ThreadDatabase.ADDRESS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI, ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT, ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT,
ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN}, 1); ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN}, 1);

View File

@ -21,7 +21,7 @@ import android.text.SpannableString;
import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
/** /**
* The base class for all message record models. Encapsulates basic data * The base class for all message record models. Encapsulates basic data
@ -36,7 +36,7 @@ public abstract class DisplayRecord {
protected final Context context; protected final Context context;
protected final long type; protected final long type;
private final Recipients recipients; private final Recipient recipient;
private final long dateSent; private final long dateSent;
private final long dateReceived; private final long dateReceived;
private final long threadId; private final long threadId;
@ -44,12 +44,12 @@ public abstract class DisplayRecord {
private final int deliveryStatus; private final int deliveryStatus;
private final int receiptCount; private final int receiptCount;
public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent, public DisplayRecord(Context context, Body body, Recipient recipient, long dateSent,
long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type) long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type)
{ {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.threadId = threadId; this.threadId = threadId;
this.recipients = recipients; this.recipient = recipient;
this.dateSent = dateSent; this.dateSent = dateSent;
this.dateReceived = dateReceived; this.dateReceived = dateReceived;
this.type = type; this.type = type;
@ -81,8 +81,8 @@ public abstract class DisplayRecord {
public abstract SpannableString getDisplayBody(); public abstract SpannableString getDisplayBody();
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
public long getDateSent() { public long getDateSent() {

View File

@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.List; import java.util.List;
@ -45,7 +44,7 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
private final Context context; private final Context context;
private final int partCount; private final int partCount;
public MediaMmsMessageRecord(Context context, long id, Recipients recipients, public MediaMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, int receiptCount, long dateSent, long dateReceived, int receiptCount,
long threadId, Body body, long threadId, Body body,
@ -55,7 +54,7 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
List<NetworkFailure> failures, int subscriptionId, List<NetworkFailure> failures, int subscriptionId,
long expiresIn, long expireStarted) long expiresIn, long expireStarted)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent, super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures,
subscriptionId, expiresIn, expireStarted, slideDeck); subscriptionId, expiresIn, expireStarted, slideDeck);

View File

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
@ -55,7 +54,7 @@ public abstract class MessageRecord extends DisplayRecord {
private final long expiresIn; private final long expiresIn;
private final long expireStarted; private final long expireStarted;
MessageRecord(Context context, long id, Body body, Recipients recipients, MessageRecord(Context context, long id, Body body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long threadId, long dateSent, long dateReceived, long threadId,
int deliveryStatus, int receiptCount, long type, int deliveryStatus, int receiptCount, long type,
@ -63,7 +62,7 @@ public abstract class MessageRecord extends DisplayRecord {
List<NetworkFailure> networkFailures, List<NetworkFailure> networkFailures,
int subscriptionId, long expiresIn, long expireStarted) int subscriptionId, long expiresIn, long expireStarted)
{ {
super(context, body, recipients, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, super(context, body, conversationRecipient, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
type); type);
this.id = id; this.id = id;
this.individualRecipient = individualRecipient; this.individualRecipient = individualRecipient;

View File

@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.List; import java.util.List;
@ -17,14 +16,14 @@ public abstract class MmsMessageRecord extends MessageRecord {
private final @NonNull SlideDeck slideDeck; private final @NonNull SlideDeck slideDeck;
MmsMessageRecord(Context context, long id, Body body, Recipients recipients, MmsMessageRecord(Context context, long id, Body body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, long dateSent, Recipient individualRecipient, int recipientDeviceId, long dateSent,
long dateReceived, long threadId, int deliveryStatus, int receiptCount, long dateReceived, long threadId, int deliveryStatus, int receiptCount,
long type, List<IdentityKeyMismatch> mismatches, long type, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn, List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn,
long expireStarted, @NonNull SlideDeck slideDeck) long expireStarted, @NonNull SlideDeck slideDeck)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted); super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted);
this.slideDeck = slideDeck; this.slideDeck = slideDeck;
} }

View File

@ -20,13 +20,12 @@ import android.content.Context;
import android.text.SpannableString; import android.text.SpannableString;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList; import java.util.LinkedList;
@ -46,14 +45,14 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
private final int status; private final int status;
private final byte[] transactionId; private final byte[] transactionId;
public NotificationMmsMessageRecord(Context context, long id, Recipients recipients, public NotificationMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, int receiptCount, long dateSent, long dateReceived, int receiptCount,
long threadId, byte[] contentLocation, long messageSize, long threadId, byte[] contentLocation, long messageSize,
long expiry, int status, byte[] transactionId, long mailbox, long expiry, int status, byte[] transactionId, long mailbox,
int subscriptionId, SlideDeck slideDeck) int subscriptionId, SlideDeck slideDeck)
{ {
super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId, super(context, id, new Body("", true), conversationRecipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox,
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId, new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId,
0, 0, slideDeck); 0, 0, slideDeck);

View File

@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -41,7 +40,7 @@ import java.util.List;
public class SmsMessageRecord extends MessageRecord { public class SmsMessageRecord extends MessageRecord {
public SmsMessageRecord(Context context, long id, public SmsMessageRecord(Context context, long id,
Body body, Recipients recipients, Body body, Recipient recipient,
Recipient individualRecipient, Recipient individualRecipient,
int recipientDeviceId, int recipientDeviceId,
long dateSent, long dateReceived, long dateSent, long dateReceived,
@ -50,7 +49,7 @@ public class SmsMessageRecord extends MessageRecord {
int status, List<IdentityKeyMismatch> mismatches, int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId, long expiresIn, long expireStarted) int subscriptionId, long expiresIn, long expireStarted)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, super(context, id, body, recipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, receiptCount, type, dateSent, dateReceived, threadId, status, receiptCount, type,
mismatches, new LinkedList<NetworkFailure>(), subscriptionId, mismatches, new LinkedList<NetworkFailure>(), subscriptionId,
expiresIn, expireStarted); expiresIn, expireStarted);

View File

@ -28,7 +28,7 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
/** /**
@ -49,11 +49,11 @@ public class ThreadRecord extends DisplayRecord {
private final long lastSeen; private final long lastSeen;
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri, public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
@NonNull Recipients recipients, long date, long count, boolean read, @NonNull Recipient recipient, long date, long count, boolean read,
long threadId, int receiptCount, int status, long snippetType, long threadId, int receiptCount, int status, long snippetType,
int distributionType, boolean archived, long expiresIn, long lastSeen) int distributionType, boolean archived, long expiresIn, long lastSeen)
{ {
super(context, body, recipients, date, date, threadId, status, receiptCount, snippetType); super(context, body, recipient, date, date, threadId, status, receiptCount, snippetType);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.snippetUri = snippetUri; this.snippetUri = snippetUri;
this.count = count; this.count = count;
@ -98,13 +98,13 @@ public class ThreadRecord extends DisplayRecord {
} else if (SmsDatabase.Types.isMissedCall(type)) { } else if (SmsDatabase.Types.isMissedCall(type)) {
return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call)); return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call));
} else if (SmsDatabase.Types.isJoinedType(type)) { } else if (SmsDatabase.Types.isJoinedType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipients().getPrimaryRecipient().toShortString())); return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) { } else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) {
String time = ExpirationUtil.getExpirationDisplayValue(context, (int) (getExpiresIn() / 1000)); String time = ExpirationUtil.getExpirationDisplayValue(context, (int) (getExpiresIn() / 1000));
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time)); return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (SmsDatabase.Types.isIdentityUpdate(type)) { } else if (SmsDatabase.Types.isIdentityUpdate(type)) {
if (getRecipients().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed)); if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipients().getPrimaryRecipient().toShortString())); else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isIdentityVerified(type)) { } else if (SmsDatabase.Types.isIdentityVerified(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified)); return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified));
} else if (SmsDatabase.Types.isIdentityDefault(type)) { } else if (SmsDatabase.Types.isIdentityDefault(type)) {

View File

@ -15,19 +15,21 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -40,23 +42,93 @@ public class GroupManager {
@NonNull MasterSecret masterSecret, @NonNull MasterSecret masterSecret,
@NonNull Set<Recipient> members, @NonNull Set<Recipient> members,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name,
throws InvalidNumberException boolean mms)
{ {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] groupId = groupDatabase.allocateGroupId(); final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final Set<Address> memberAddresses = getMemberAddresses(context, members); final Set<Address> memberAddresses = getMemberAddresses(members);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null); groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null);
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); if (!mms) {
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
} else {
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId);
}
} }
private static Set<Address> getMemberAddresses(Context context, Collection<Recipient> recipients) public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException throws InvalidNumberException
{ {
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
} else {
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
return new GroupActionResult(groupRecipient, threadId);
}
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
try {
Attachment avatarAttachment = null;
Address groupAddress = Address.fromSerialized(groupId);
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, groupAddress, false);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(numbers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static Set<Address> getMemberAddresses(Collection<Recipient> recipients) {
final Set<Address> results = new HashSet<>(); final Set<Address> results = new HashSet<>();
for (Recipient recipient : recipients) { for (Recipient recipient : recipients) {
results.add(recipient.getAddress()); results.add(recipient.getAddress());
@ -65,71 +137,16 @@ public class GroupManager {
return results; return results;
} }
public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull byte[] groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(context, members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
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, memberAddresses, name, avatarBytes);
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull byte[] groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
Attachment avatarAttachment = null;
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId));
Recipients groupRecipient = RecipientFactory.getRecipientsFor(context, new Address[]{groupAddress}, false);
List<String> 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(numbers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
}
public static class GroupActionResult { public static class GroupActionResult {
private Recipients groupRecipient; private Recipient groupRecipient;
private long threadId; private long threadId;
public GroupActionResult(Recipients groupRecipient, long threadId) { public GroupActionResult(Recipient groupRecipient, long threadId) {
this.groupRecipient = groupRecipient; this.groupRecipient = groupRecipient;
this.threadId = threadId; this.threadId = threadId;
} }
public Recipients getGroupRecipient() { public Recipient getGroupRecipient() {
return groupRecipient; return groupRecipient;
} }

View File

@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
@ -60,7 +60,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
SignalServiceGroup group = message.getGroupInfo().get(); SignalServiceGroup group = message.getGroupInfo().get();
byte[] id = group.getGroupId(); String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupRecord record = database.getGroup(id); GroupRecord record = database.getGroup(id);
if (record != null && group.getType() == Type.UPDATE) { if (record != null && group.getType() == Type.UPDATE) {
@ -84,7 +84,7 @@ public class GroupMessageProcessor {
boolean outgoing) boolean outgoing)
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId(); String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE); builder.setType(GroupContext.Type.UPDATE);
@ -113,7 +113,7 @@ public class GroupMessageProcessor {
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId(); String id = GroupUtil.getEncodedId(group.getGroupId(), false);
Set<Address> recordMembers = new HashSet<>(groupRecord.getMembers()); Set<Address> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<Address> messageMembers = new HashSet<>(); Set<Address> messageMembers = new HashSet<>();
@ -185,7 +185,7 @@ public class GroupMessageProcessor {
boolean outgoing) boolean outgoing)
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId(); String id = GroupUtil.getEncodedId(group.getGroupId(), false);
List<Address> members = record.getMembers(); List<Address> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
@ -217,10 +217,10 @@ public class GroupMessageProcessor {
try { try {
if (outgoing) { if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId())); Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false));
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {addres}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, addres, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, envelope.getTimestamp(), 0);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null); long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null);
mmsDatabase.markAsSent(messageId, true); mmsDatabase.markAsSent(messageId, true);

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -54,8 +55,9 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
@Override @Override
public void onRun(MasterSecret masterSecret) throws IOException { public void onRun(MasterSecret masterSecret) throws IOException {
String encodeId = GroupUtil.getEncodedId(groupId, false);
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
GroupDatabase.GroupRecord record = database.getGroup(groupId); GroupDatabase.GroupRecord record = database.getGroup(encodeId);
File attachment = null; File attachment = null;
try { try {
@ -82,7 +84,7 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE); InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key), 500, 500); Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key), 500, 500);
database.updateAvatar(groupId, avatar); database.updateAvatar(encodeId, avatar);
inputStream.close(); inputStream.close();
} }
} catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) { } catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) {

View File

@ -8,10 +8,9 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -20,7 +19,7 @@ import java.io.IOException;
public class DirectoryRefreshJob extends ContextJob { public class DirectoryRefreshJob extends ContextJob {
@Nullable private transient Recipients recipients; @Nullable private transient Recipient recipient;
@Nullable private transient MasterSecret masterSecret; @Nullable private transient MasterSecret masterSecret;
public DirectoryRefreshJob(@NonNull Context context) { public DirectoryRefreshJob(@NonNull Context context) {
@ -29,14 +28,14 @@ public class DirectoryRefreshJob extends ContextJob {
public DirectoryRefreshJob(@NonNull Context context, public DirectoryRefreshJob(@NonNull Context context,
@Nullable MasterSecret masterSecret, @Nullable MasterSecret masterSecret,
@Nullable Recipients recipients) @Nullable Recipient recipient)
{ {
super(context, JobParameters.newBuilder() super(context, JobParameters.newBuilder()
.withGroupId(DirectoryRefreshJob.class.getSimpleName()) .withGroupId(DirectoryRefreshJob.class.getSimpleName())
.withRequirement(new NetworkRequirement(context)) .withRequirement(new NetworkRequirement(context))
.create()); .create());
this.recipients = recipients; this.recipient = recipient;
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
} }
@ -51,10 +50,10 @@ public class DirectoryRefreshJob extends ContextJob {
try { try {
wakeLock.acquire(); wakeLock.acquire();
if (recipients == null) { if (recipient == null) {
DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context)); DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context));
} else { } else {
DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients); DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient);
} }
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
} finally { } finally {

View File

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
@ -28,6 +29,7 @@ import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -39,8 +41,10 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class MmsDownloadJob extends MasterSecretJob { public class MmsDownloadJob extends MasterSecretJob {
@ -88,6 +92,10 @@ public class MmsDownloadJob extends MasterSecretJob {
throw new MmsException("Notification content location was null."); throw new MmsException("Notification content location was null.");
} }
if (!TextSecurePreferences.isPushRegistered(context)) {
throw new MmsException("Not registered");
}
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING); database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING);
String contentLocation = notification.get().getContentLocation(); String contentLocation = notification.get().getContentLocation();
@ -161,28 +169,33 @@ public class MmsDownloadJob extends MasterSecretJob {
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
SingleUseBlobProvider provider = SingleUseBlobProvider.getInstance(); SingleUseBlobProvider provider = SingleUseBlobProvider.getInstance();
String from = null; Optional<Address> group = Optional.absent();
List<String> to = new LinkedList<>(); Set<Address> members = new HashSet<>();
List<String> cc = new LinkedList<>();
String body = null; String body = null;
List<Attachment> attachments = new LinkedList<>(); List<Attachment> attachments = new LinkedList<>();
Address from;
if (retrieved.getFrom() != null) { if (retrieved.getFrom() != null) {
from = Util.toIsoString(retrieved.getFrom().getTextString()); from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString()));
} else {
from = Address.UNKNOWN;
} }
if (retrieved.getTo() != null) { if (retrieved.getTo() != null) {
for (EncodedStringValue toValue : retrieved.getTo()) { for (EncodedStringValue toValue : retrieved.getTo()) {
to.add(Util.toIsoString(toValue.getTextString())); members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString())));
} }
} }
if (retrieved.getCc() != null) { if (retrieved.getCc() != null) {
for (EncodedStringValue ccValue : retrieved.getCc()) { for (EncodedStringValue ccValue : retrieved.getCc()) {
cc.add(Util.toIsoString(ccValue.getTextString())); members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString())));
} }
} }
members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
if (retrieved.getBody() != null) { if (retrieved.getBody() != null) {
body = PartParser.getMessageText(retrieved.getBody()); body = PartParser.getMessageText(retrieved.getBody());
PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody()); PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody());
@ -203,9 +216,11 @@ public class MmsDownloadJob extends MasterSecretJob {
} }
} }
if (members.size() > 1) {
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true)));
}
IncomingMediaMessage message = new IncomingMediaMessage(from, group, 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> insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret), Optional<InsertResult> insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
message, contentLocation, threadId); message, contentLocation, threadId);

View File

@ -13,8 +13,8 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -86,7 +86,7 @@ public class MmsReceiveJob extends ContextJob {
private boolean isBlocked(GenericPdu pdu) { private boolean isBlocked(GenericPdu pdu) {
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) { if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString()))}, false); Recipient recipients = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false);
return recipients.isBlocked(); return recipients.isBlocked();
} }

View File

@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaConstraints;
@ -33,11 +34,12 @@ import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -171,16 +173,28 @@ public class MmsSendJob extends SendJob {
{ {
SendReq req = new SendReq(); SendReq req = new SendReq();
String lineNumber = Utils.getMyPhoneNumber(context); String lineNumber = Utils.getMyPhoneNumber(context);
Address[] numbers = message.getRecipients().getAddresses(); Address destination = message.getRecipient().getAddress();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
if (!TextUtils.isEmpty(lineNumber)) { if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber)); req.setFrom(new EncodedStringValue(lineNumber));
} else {
req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context)));
} }
for (Address recipient : numbers) { if (destination.isMmsGroup()) {
req.addTo(new EncodedStringValue(recipient.serialize())); List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(destination.toGroupString(), false);
for (Recipient member : members) {
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
req.addBcc(new EncodedStringValue(member.getAddress().serialize()));
} else {
req.addTo(new EncodedStringValue(member.getAddress().serialize()));
}
}
} else {
req.addTo(new EncodedStringValue(destination.serialize()));
} }
req.setDate(System.currentTimeMillis() / 1000); req.setDate(System.currentTimeMillis() / 1000);
@ -266,11 +280,11 @@ public class MmsSendJob extends SendJob {
} }
private void notifyMediaMessageDeliveryFailed(Context context, long messageId) { private void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
if (recipients != null) { if (recipient != null) {
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
} }
} }
} }

View File

@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.BlockedRe
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -50,11 +50,11 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
BlockedReader reader = database.readerForBlocked(database.getBlocked()); BlockedReader reader = database.readerForBlocked(database.getBlocked());
List<String> blocked = new LinkedList<>(); List<String> blocked = new LinkedList<>();
Recipients recipients; Recipient recipient;
while ((recipients = reader.getNext()) != null) { while ((recipient = reader.getNext()) != null) {
if (recipients.isSingleRecipient() && !recipients.isGroupRecipient()) { if (!recipient.isGroupRecipient()) {
blocked.add(recipients.getPrimaryRecipient().getAddress().serialize()); blocked.add(recipient.getAddress().serialize());
} }
} }

View File

@ -34,8 +34,8 @@ import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
@ -80,7 +80,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -162,7 +161,7 @@ public class PushDecryptJob extends ContextJob {
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId); else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId);
else handleTextMessage(masterSecret, envelope, message, smsMessageId); else handleTextMessage(masterSecret, envelope, message, smsMessageId);
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(message.getGroupInfo().get().getGroupId())) { if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(envelope, message.getGroupInfo().get()); handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
} }
} else if (content.getSyncMessage().isPresent()) { } else if (content.getSyncMessage().isPresent()) {
@ -322,15 +321,15 @@ public class PushDecryptJob extends ContextJob {
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = getSyncMessageDestination(message); Recipient recipient = getSyncMessageDestination(message);
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipients, "", -1); OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1);
OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage); OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
if (recipients.isSingleRecipient() && !recipients.isGroupRecipient()) { if (!recipient.isGroupRecipient()) {
SessionStore sessionStore = new TextSecureSessionStore(context); SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(recipients.getPrimaryRecipient().getAddress().toPhoneString()); sessionStore.deleteAllSessions(recipient.getAddress().toPhoneString());
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
@ -373,11 +372,9 @@ public class PushDecryptJob extends ContextJob {
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
String localNumber = TextSecurePreferences.getLocalNumber(context); Recipient recipient = getMessageDestination(envelope, message);
Recipients recipients = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()), Address.fromExternal(context, envelope.getSource()),
Address.fromSerialized(localNumber),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, true, message.getExpiresInSeconds() * 1000, true,
Optional.fromNullable(envelope.getRelay()), Optional.fromNullable(envelope.getRelay()),
@ -388,7 +385,7 @@ public class PushDecryptJob extends ContextJob {
database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1); database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1);
DatabaseFactory.getRecipientPreferenceDatabase(context).setExpireMessages(recipients, message.getExpiresInSeconds()); DatabaseFactory.getRecipientPreferenceDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
if (smsMessageId.isPresent()) { if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
@ -423,7 +420,7 @@ public class PushDecryptJob extends ContextJob {
threadId = handleSynchronizeSentTextMessage(masterSecret, message, smsMessageId); threadId = handleSynchronizeSentTextMessage(masterSecret, message, smsMessageId);
} }
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(message.getMessage().getGroupInfo().get().getGroupId())) { if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(envelope, message.getMessage().getGroupInfo().get()); handleUnknownGroupMessage(envelope, message.getMessage().getGroupInfo().get());
} }
@ -490,11 +487,9 @@ public class PushDecryptJob extends ContextJob {
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
String localNumber = TextSecurePreferences.getLocalNumber(context); Recipient recipient = getMessageDestination(envelope, message);
Recipients recipients = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()), Address.fromExternal(context, envelope.getSource()),
Address.fromSerialized(localNumber),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, false, message.getExpiresInSeconds() * 1000, false,
Optional.fromNullable(envelope.getRelay()), Optional.fromNullable(envelope.getRelay()),
@ -502,7 +497,7 @@ public class PushDecryptJob extends ContextJob {
message.getGroupInfo(), message.getGroupInfo(),
message.getAttachments()); message.getAttachments());
if (message.getExpiresInSeconds() != recipients.getExpireMessages()) { if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent()); handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent());
} }
@ -537,18 +532,18 @@ public class PushDecryptJob extends ContextJob {
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipients recipients = getSyncMessageDestination(message); Recipient recipient = getSyncMessageDestination(message);
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipients, OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient,
message.getTimestamp(), message.getTimestamp(),
message.getMessage().getExpiresInSeconds() * 1000); message.getMessage().getExpiresInSeconds() * 1000);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = database.insertMessageOutbox(masterSecret, expirationUpdateMessage, threadId, false, null); long messageId = database.insertMessageOutbox(masterSecret, expirationUpdateMessage, threadId, false, null);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
DatabaseFactory.getRecipientPreferenceDatabase(context).setExpireMessages(recipients, message.getMessage().getExpiresInSeconds()); DatabaseFactory.getRecipientPreferenceDatabase(context).setExpireMessages(recipient, message.getMessage().getExpiresInSeconds());
if (smsMessageId.isPresent()) { if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
@ -563,7 +558,7 @@ public class PushDecryptJob extends ContextJob {
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipients recipients = getSyncMessageDestination(message); Recipient recipients = getSyncMessageDestination(message);
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(), OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(),
PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()), PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
@ -611,9 +606,9 @@ public class PushDecryptJob extends ContextJob {
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : ""; String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipients recipients = getMessageDestination(envelope, message); Recipient recipient = getMessageDestination(envelope, message);
if (message.getExpiresInSeconds() != recipients.getExpireMessages()) { if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent()); handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent());
} }
@ -648,16 +643,16 @@ public class PushDecryptJob extends ContextJob {
throws MmsException throws MmsException
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = getSyncMessageDestination(message); Recipient recipient = getSyncMessageDestination(message);
String body = message.getMessage().getBody().or(""); String body = message.getMessage().getBody().or("");
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000; long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000;
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipients, body, expiresInMillis, -1); OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, body, expiresInMillis, -1);
if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message, Optional.<Long>absent()); handleSynchronizeSentExpirationUpdate(masterSecret, message, Optional.<Long>absent());
} }
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp(), null); long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
@ -810,19 +805,19 @@ public class PushDecryptJob extends ContextJob {
return database.insertMessageInbox(textMessage); return database.insertMessageInbox(textMessage);
} }
private Recipients getSyncMessageDestination(SentTranscriptMessage message) { private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().getGroupInfo().isPresent()) { if (message.getMessage().getGroupInfo().isPresent()) {
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId()))}, false); return RecipientFactory.getRecipientFor(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
} else { } else {
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, message.getDestination().get())}, false); return RecipientFactory.getRecipientFor(context, Address.fromExternal(context, message.getDestination().get()), false);
} }
} }
private Recipients getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) { private Recipient getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) { if (message.getGroupInfo().isPresent()) {
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId()))}, false); return RecipientFactory.getRecipientFor(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
} else { } else {
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, envelope.getSource())}, false); return RecipientFactory.getRecipientFor(context, Address.fromExternal(context, envelope.getSource()), false);
} }
} }
} }

View File

@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -138,8 +137,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
EncapsulatedExceptions, UndeliverableMessageException EncapsulatedExceptions, UndeliverableMessageException
{ {
SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceMessageSender messageSender = messageSenderFactory.create();
byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getAddress().toGroupString()); String groupId = message.getRecipient().getAddress().toGroupString();
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
@ -154,12 +153,12 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
GroupContext groupContext = groupMessage.getGroupContext(); GroupContext groupContext = groupMessage.getGroupContext();
SignalServiceAttachment avatar = attachmentStreams.isEmpty() ? null : attachmentStreams.get(0); SignalServiceAttachment avatar = attachmentStreams.isEmpty() ? null : attachmentStreams.get(0);
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
SignalServiceGroup group = new SignalServiceGroup(type, groupId, groupContext.getName(), groupContext.getMembersList(), avatar); SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), groupContext.getMembersList(), avatar);
SignalServiceDataMessage groupDataMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group, null, null); SignalServiceDataMessage groupDataMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group, null, null);
messageSender.sendMessage(addresses, groupDataMessage); messageSender.sendMessage(addresses, groupDataMessage);
} else { } else {
SignalServiceGroup group = new SignalServiceGroup(groupId); SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId));
SignalServiceDataMessage groupMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group, SignalServiceDataMessage groupMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group,
attachmentStreams, message.getBody(), false, attachmentStreams, message.getBody(), false,
(int)(message.getExpiresIn() / 1000), (int)(message.getExpiresIn() / 1000),
@ -175,10 +174,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
return addresses; return addresses;
} }
private List<SignalServiceAddress> getPushAddresses(Recipients recipients) { private List<SignalServiceAddress> getPushAddresses(List<Recipient> recipients) {
List<SignalServiceAddress> addresses = new LinkedList<>(); List<SignalServiceAddress> addresses = new LinkedList<>();
for (Recipient recipient : recipients.getRecipientsList()) { for (Recipient recipient : recipients) {
addresses.add(getPushAddress(recipient.getAddress())); addresses.add(getPushAddress(recipient.getAddress()));
} }

View File

@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -59,7 +60,7 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
public void onRun() throws IOException, UntrustedIdentityException { public void onRun() throws IOException, UntrustedIdentityException {
SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceMessageSender messageSender = messageSenderFactory.create();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
GroupRecord record = groupDatabase.getGroup(groupId); GroupRecord record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false));
SignalServiceAttachment avatar = null; SignalServiceAttachment avatar = null;
if (record == null) { if (record == null) {

View File

@ -102,14 +102,14 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException UndeliverableMessageException
{ {
if (message.getRecipients() == null || message.getRecipients().getPrimaryRecipient() == null) { if (message.getRecipient() == null) {
throw new UndeliverableMessageException("No destination address."); throw new UndeliverableMessageException("No destination address.");
} }
SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceMessageSender messageSender = messageSenderFactory.create();
try { try {
SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getAddress()); SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);

View File

@ -9,8 +9,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.NotInDirectoryException;
import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.database.TextSecureDirectory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -35,8 +35,8 @@ public abstract class PushReceivedJob extends ContextJob {
directory.setNumber(contactTokenDetails, true); directory.setNumber(contactTokenDetails, true);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, source, false);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipients)); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipient));
} }
if (envelope.isReceipt()) { if (envelope.isReceipt()) {
@ -49,7 +49,7 @@ public abstract class PushReceivedJob extends ContextJob {
} }
private void handleMessage(SignalServiceEnvelope envelope, Address source, boolean sendExplicitReceipt) { private void handleMessage(SignalServiceEnvelope envelope, Address source, boolean sendExplicitReceipt) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false); Recipient recipients = RecipientFactory.getRecipientFor(context, source, false);
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
if (!recipients.isBlocked()) { if (!recipients.isBlocked()) {

View File

@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -95,11 +95,11 @@ public abstract class PushSendJob extends SendJob {
} }
protected void notifyMediaMessageDeliveryFailed(Context context, long messageId) { protected void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
if (threadId != -1 && recipients != null) { if (threadId != -1 && recipient != null) {
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
} }
} }

View File

@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
@ -66,7 +66,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
} catch (InsecureFallbackApprovalException e) { } catch (InsecureFallbackApprovalException e) {
Log.w(TAG, e); Log.w(TAG, e);
database.markAsPendingInsecureSmsFallback(record.getId()); database.markAsPendingInsecureSmsFallback(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId()); MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context));
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
Log.w(TAG, e); Log.w(TAG, e);
@ -87,11 +87,11 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
public void onCanceled() { public void onCanceled() {
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
if (threadId != -1 && recipients != null) { if (threadId != -1 && recipient != null) {
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
} }
} }

View File

@ -9,10 +9,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.MessageRetrievalService; import org.thoughtcrime.securesms.service.MessageRetrievalService;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
@ -24,6 +22,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceProfile;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@ -33,14 +32,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
@Inject transient SignalServiceMessageReceiver receiver; @Inject transient SignalServiceMessageReceiver receiver;
private final Recipients recipients; private final Recipient recipient;
public RetrieveProfileJob(Context context, Recipients recipients) { public RetrieveProfileJob(Context context, Recipient recipient) {
super(context, JobParameters.newBuilder() super(context, JobParameters.newBuilder()
.withRetryCount(3) .withRetryCount(3)
.create()); .create());
this.recipients = recipients; this.recipient = recipient;
} }
@Override @Override
@ -49,10 +48,8 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
@Override @Override
public void onRun() throws IOException, InvalidKeyException { public void onRun() throws IOException, InvalidKeyException {
try { try {
for (Recipient recipient : recipients) { if (recipient.isGroupRecipient()) handleGroupRecipient(recipient);
if (recipient.isGroupRecipient()) handleGroupRecipient(recipient); else handleIndividualRecipient(recipient);
else handleIndividualRecipient(recipient);
}
} catch (InvalidNumberException e) { } catch (InvalidNumberException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -93,8 +90,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
private void handleGroupRecipient(Recipient group) private void handleGroupRecipient(Recipient group)
throws IOException, InvalidKeyException, InvalidNumberException throws IOException, InvalidKeyException, InvalidNumberException
{ {
byte[] groupId = GroupUtil.getDecodedId(group.getAddress().toGroupString()); List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.getAddress().toGroupString(), false);
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
for (Recipient recipient : recipients) { for (Recipient recipient : recipients) {
handleIndividualRecipient(recipient); handleIndividualRecipient(recipient);

View File

@ -9,13 +9,12 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -86,8 +85,8 @@ public class SmsReceiveJob extends ContextJob {
private boolean isBlocked(IncomingTextMessage message) { private boolean isBlocked(IncomingTextMessage message) {
if (message.getSender() != null) { if (message.getSender() != null) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, false); Recipient recipient = RecipientFactory.getRecipientFor(context, message.getSender(), false);
return recipients.isBlocked(); return recipient.isBlocked();
} }
return false; return false;

View File

@ -13,13 +13,12 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement; import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement;
import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirement; import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SmsDeliveryListener; import org.thoughtcrime.securesms.service.SmsDeliveryListener;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
@ -54,7 +53,7 @@ public class SmsSendJob extends SendJob {
} catch (UndeliverableMessageException ude) { } catch (UndeliverableMessageException ude) {
Log.w(TAG, ude); Log.w(TAG, ude);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId()); DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId()); MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
} }
} }
@ -66,11 +65,11 @@ public class SmsSendJob extends SendJob {
@Override @Override
public void onCanceled() { public void onCanceled() {
Log.w(TAG, "onCanceled()"); Log.w(TAG, "onCanceled()");
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
} }
private void deliver(SmsMessageRecord message) private void deliver(SmsMessageRecord message)

View File

@ -86,7 +86,7 @@ public class SmsSentJob extends MasterSecretJob {
break; break;
default: default:
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId()); MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
} }
} catch (NoSuchMessageException e) { } catch (NoSuchMessageException e) {
Log.w(TAG, e); Log.w(TAG, e);

View File

@ -1,12 +1,9 @@
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment; import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MmsAddresses;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -18,46 +15,39 @@ import java.util.List;
public class IncomingMediaMessage { public class IncomingMediaMessage {
private final Address from; private final Address from;
private final String body;
private final Address groupId; private final Address groupId;
private final String body;
private final boolean push; private final boolean push;
private final long sentTimeMillis; private final long sentTimeMillis;
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
private final boolean expirationUpdate; private final boolean expirationUpdate;
private final List<Address> to = new LinkedList<>();
private final List<Address> cc = new LinkedList<>();
private final List<Attachment> attachments = new LinkedList<>(); private final List<Attachment> attachments = new LinkedList<>();
public IncomingMediaMessage(Context context, String from, List<String> to, List<String> cc, public IncomingMediaMessage(Address from,
String body, long sentTimeMillis, Optional<Address> groupId,
List<Attachment> attachments, int subscriptionId, String body,
long expiresIn, boolean expirationUpdate) long sentTimeMillis,
List<Attachment> attachments,
int subscriptionId,
long expiresIn,
boolean expirationUpdate)
{ {
this.from = Address.fromExternal(context, from); this.from = from;
this.groupId = groupId.orNull();
this.sentTimeMillis = sentTimeMillis; this.sentTimeMillis = sentTimeMillis;
this.body = body; this.body = body;
this.groupId = null;
this.push = false; this.push = false;
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate; this.expirationUpdate = expirationUpdate;
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); this.attachments.addAll(attachments);
} }
public IncomingMediaMessage(MasterSecretUnion masterSecret, public IncomingMediaMessage(MasterSecretUnion masterSecret,
Address from, Address from,
Address to,
long sentTimeMillis, long sentTimeMillis,
int subscriptionId, int subscriptionId,
long expiresIn, long expiresIn,
@ -75,10 +65,9 @@ public class IncomingMediaMessage {
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate; this.expirationUpdate = expirationUpdate;
if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId())); if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId(), false));
else this.groupId = null; else this.groupId = null;
this.to.add(to);
this.attachments.addAll(PointerAttachment.forPointers(masterSecret, attachments)); this.attachments.addAll(PointerAttachment.forPointers(masterSecret, attachments));
} }
@ -90,14 +79,14 @@ public class IncomingMediaMessage {
return body; return body;
} }
public MmsAddresses getAddresses() {
return new MmsAddresses(from, to, cc, new LinkedList<Address>());
}
public List<Attachment> getAttachments() { public List<Attachment> getAttachments() {
return attachments; return attachments;
} }
public Address getFrom() {
return from;
}
public Address getGroupId() { public Address getGroupId() {
return groupId; return groupId;
} }
@ -119,6 +108,6 @@ public class IncomingMediaMessage {
} }
public boolean isGroupMessage() { public boolean isGroupMessage() {
return groupId != null || to.size() > 1 || cc.size() > 0; return groupId != null;
} }
} }

View File

@ -2,14 +2,14 @@ package org.thoughtcrime.securesms.mms;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.LinkedList; import java.util.LinkedList;
public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage { public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage {
public OutgoingExpirationUpdateMessage(Recipients recipients, long sentTimeMillis, long expiresIn) { public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) {
super(recipients, "", new LinkedList<Attachment>(), sentTimeMillis, super(recipient, "", new LinkedList<Attachment>(), sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn); ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn);
} }

View File

@ -5,7 +5,7 @@ import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@ -17,26 +17,26 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
private final GroupContext group; private final GroupContext group;
public OutgoingGroupMediaMessage(@NonNull Recipients recipients, public OutgoingGroupMediaMessage(@NonNull Recipient recipient,
@NonNull String encodedGroupContext, @NonNull String encodedGroupContext,
@NonNull List<Attachment> avatar, @NonNull List<Attachment> avatar,
long sentTimeMillis, long sentTimeMillis,
long expiresIn) long expiresIn)
throws IOException throws IOException
{ {
super(recipients, encodedGroupContext, avatar, sentTimeMillis, super(recipient, encodedGroupContext, avatar, sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn); ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn);
this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext)); this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext));
} }
public OutgoingGroupMediaMessage(@NonNull Recipients recipients, public OutgoingGroupMediaMessage(@NonNull Recipient recipient,
@NonNull GroupContext group, @NonNull GroupContext group,
@Nullable final Attachment avatar, @Nullable final Attachment avatar,
long sentTimeMillis, long sentTimeMillis,
long expireIn) long expireIn)
{ {
super(recipients, Base64.encodeBytes(group.toByteArray()), super(recipient, Base64.encodeBytes(group.toByteArray()),
new LinkedList<Attachment>() {{if (avatar != null) add(avatar);}}, new LinkedList<Attachment>() {{if (avatar != null) add(avatar);}},
System.currentTimeMillis(), System.currentTimeMillis(),
ThreadDatabase.DistributionTypes.CONVERSATION, expireIn); ThreadDatabase.DistributionTypes.CONVERSATION, expireIn);

View File

@ -3,13 +3,13 @@ package org.thoughtcrime.securesms.mms;
import android.text.TextUtils; import android.text.TextUtils;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.List; import java.util.List;
public class OutgoingMediaMessage { public class OutgoingMediaMessage {
private final Recipients recipients; private final Recipient recipient;
protected final String body; protected final String body;
protected final List<Attachment> attachments; protected final List<Attachment> attachments;
private final long sentTimeMillis; private final long sentTimeMillis;
@ -17,12 +17,12 @@ public class OutgoingMediaMessage {
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
public OutgoingMediaMessage(Recipients recipients, String message, public OutgoingMediaMessage(Recipient recipient, String message,
List<Attachment> attachments, long sentTimeMillis, List<Attachment> attachments, long sentTimeMillis,
int subscriptionId, long expiresIn, int subscriptionId, long expiresIn,
int distributionType) int distributionType)
{ {
this.recipients = recipients; this.recipient = recipient;
this.body = message; this.body = message;
this.sentTimeMillis = sentTimeMillis; this.sentTimeMillis = sentTimeMillis;
this.distributionType = distributionType; this.distributionType = distributionType;
@ -31,9 +31,9 @@ public class OutgoingMediaMessage {
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
} }
public OutgoingMediaMessage(Recipients recipients, SlideDeck slideDeck, String message, long sentTimeMillis, int subscriptionId, long expiresIn, int distributionType) public OutgoingMediaMessage(Recipient recipient, SlideDeck slideDeck, String message, long sentTimeMillis, int subscriptionId, long expiresIn, int distributionType)
{ {
this(recipients, this(recipient,
buildMessage(slideDeck, message), buildMessage(slideDeck, message),
slideDeck.asAttachments(), slideDeck.asAttachments(),
sentTimeMillis, subscriptionId, sentTimeMillis, subscriptionId,
@ -41,7 +41,7 @@ public class OutgoingMediaMessage {
} }
public OutgoingMediaMessage(OutgoingMediaMessage that) { public OutgoingMediaMessage(OutgoingMediaMessage that) {
this.recipients = that.getRecipients(); this.recipient = that.getRecipient();
this.body = that.body; this.body = that.body;
this.distributionType = that.distributionType; this.distributionType = that.distributionType;
this.attachments = that.attachments; this.attachments = that.attachments;
@ -50,8 +50,8 @@ public class OutgoingMediaMessage {
this.expiresIn = that.expiresIn; this.expiresIn = that.expiresIn;
} }
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
public String getBody() { public String getBody() {

View File

@ -1,19 +1,19 @@
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.List; import java.util.List;
public class OutgoingSecureMediaMessage extends OutgoingMediaMessage { public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
public OutgoingSecureMediaMessage(Recipients recipients, String body, public OutgoingSecureMediaMessage(Recipient recipient, String body,
List<Attachment> attachments, List<Attachment> attachments,
long sentTimeMillis, long sentTimeMillis,
int distributionType, int distributionType,
long expiresIn) long expiresIn)
{ {
super(recipients, body, attachments, sentTimeMillis, -1, expiresIn, distributionType); super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, distributionType);
} }
public OutgoingSecureMediaMessage(OutgoingMediaMessage base) { public OutgoingSecureMediaMessage(OutgoingMediaMessage base) {

View File

@ -31,8 +31,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.whispersystems.libsignal.logging.Log; import org.whispersystems.libsignal.logging.Log;
@ -48,7 +48,7 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName(); 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 REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY";
public static final String ADDRESSES_EXTRA = "car_addresses"; public static final String ADDRESS_EXTRA = "car_address";
public static final String VOICE_REPLY_KEY = "car_voice_reply_key"; 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 THREAD_ID_EXTRA = "car_reply_thread_id";
@ -62,10 +62,10 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
if (remoteInput == null) return; if (remoteInput == null) return;
final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA)); final Address address = intent.getParcelableExtra(ADDRESS_EXTRA);
final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1); final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1);
final CharSequence responseText = getMessageText(intent); final CharSequence responseText = getMessageText(intent);
final Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false); final Recipient recipient = RecipientFactory.getRecipientFor(context, address, false);
if (responseText != null) { if (responseText != null) {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@ -74,17 +74,17 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
long replyThreadId; long replyThreadId;
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses); Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(address);
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
if (recipients.isGroupRecipient()) { if (recipient.isGroupRecipient()) {
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0); OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null); replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null);
} else { } else {
Log.w("AndroidAutoReplyReceiver", "Sending regular message "); Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId); OutgoingTextMessage reply = new OutgoingTextMessage(recipient, responseText.toString(), expiresIn, subscriptionId);
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null); replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null);
} }

View File

@ -49,7 +49,6 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.MessageRetrievalService; import org.thoughtcrime.securesms.service.MessageRetrievalService;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
@ -102,12 +101,12 @@ public class MessageNotifier {
lastDesktopActivityTimestamp = timestamp; lastDesktopActivityTimestamp = timestamp;
} }
public static void notifyMessageDeliveryFailed(Context context, Recipients recipients, long threadId) { public static void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) {
if (visibleThread == threadId) { if (visibleThread == threadId) {
sendInThreadNotification(context, recipients); sendInThreadNotification(context, recipient);
} else { } else {
Intent intent = new Intent(context, ConversationActivity.class); Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); intent.setData((Uri.parse("custom://" + System.currentTimeMillis())));
@ -213,8 +212,8 @@ public class MessageNotifier {
boolean isVisible = visibleThread == threadId; boolean isVisible = visibleThread == threadId;
ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
Recipients recipients = DatabaseFactory.getThreadDatabase(context) Recipient recipients = DatabaseFactory.getThreadDatabase(context)
.getRecipientsForThreadId(threadId); .getRecipientForThreadId(threadId);
if (isVisible) { if (isVisible) {
List<MarkedMessageInfo> messageIds = threads.setRead(threadId, false); List<MarkedMessageInfo> messageIds = threads.setRead(threadId, false);
@ -228,7 +227,7 @@ public class MessageNotifier {
} }
if (isVisible) { if (isVisible) {
sendInThreadNotification(context, threads.getRecipientsForThreadId(threadId)); sendInThreadNotification(context, threads.getRecipientForThreadId(threadId));
} else { } else {
updateNotification(context, masterSecret, signal, 0); updateNotification(context, masterSecret, signal, 0);
} }
@ -299,13 +298,13 @@ public class MessageNotifier {
SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, masterSecret, TextSecurePreferences.getNotificationPrivacy(context)); SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, masterSecret, TextSecurePreferences.getNotificationPrivacy(context));
List<NotificationItem> notifications = notificationState.getNotifications(); List<NotificationItem> notifications = notificationState.getNotifications();
Recipients recipients = notifications.get(0).getRecipients(); Recipient recipient = notifications.get(0).getRecipient();
int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0));
builder.setThread(notifications.get(0).getRecipients()); builder.setThread(notifications.get(0).getRecipient());
builder.setMessageCount(notificationState.getMessageCount()); builder.setMessageCount(notificationState.getMessageCount());
builder.setPrimaryMessageBody(recipients, notifications.get(0).getIndividualRecipient(), builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(),
notifications.get(0).getText(), notifications.get(0).getSlideDeck()); notifications.get(0).getText(), notifications.get(0).getSlideDeck());
builder.setContentIntent(notifications.get(0).getPendingIntent(context)); builder.setContentIntent(notifications.get(0).getPendingIntent(context));
builder.setGroup(NOTIFICATION_GROUP); builder.setGroup(NOTIFICATION_GROUP);
@ -316,17 +315,17 @@ public class MessageNotifier {
builder.addActions(masterSecret, builder.addActions(masterSecret,
notificationState.getMarkAsReadIntent(context, notificationId), notificationState.getMarkAsReadIntent(context, notificationId),
notificationState.getQuickReplyIntent(context, notifications.get(0).getRecipients()), notificationState.getQuickReplyIntent(context, notifications.get(0).getRecipient()),
notificationState.getRemoteReplyIntent(context, notifications.get(0).getRecipients())); notificationState.getRemoteReplyIntent(context, notifications.get(0).getRecipient()));
builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, notifications.get(0).getRecipients()), builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, notifications.get(0).getRecipient()),
notificationState.getAndroidAutoHeardIntent(context, notificationId), notifications.get(0).getTimestamp()); notificationState.getAndroidAutoHeardIntent(context, notificationId), notifications.get(0).getTimestamp());
ListIterator<NotificationItem> iterator = notifications.listIterator(notifications.size()); ListIterator<NotificationItem> iterator = notifications.listIterator(notifications.size());
while(iterator.hasPrevious()) { while(iterator.hasPrevious()) {
NotificationItem item = iterator.previous(); NotificationItem item = iterator.previous();
builder.addMessageBody(item.getRecipients(), item.getIndividualRecipient(), item.getText()); builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
} }
if (signal) { if (signal) {
@ -375,14 +374,14 @@ public class MessageNotifier {
NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build()); NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build());
} }
private static void sendInThreadNotification(Context context, Recipients recipients) { private static void sendInThreadNotification(Context context, Recipient recipient) {
if (!TextSecurePreferences.isInThreadNotifications(context) || if (!TextSecurePreferences.isInThreadNotifications(context) ||
ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL)
{ {
return; return;
} }
Uri uri = recipients != null ? recipients.getRingtone() : null; Uri uri = recipient != null ? recipient.getRingtone() : null;
if (uri == null) { if (uri == null) {
String ringtone = TextSecurePreferences.getNotificationRingtone(context); String ringtone = TextSecurePreferences.getNotificationRingtone(context);
@ -435,19 +434,19 @@ public class MessageNotifier {
else reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor, masterSecret); else reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor, masterSecret);
while ((record = reader.getNext()) != null) { while ((record = reader.getNext()) != null) {
long id = record.getId(); long id = record.getId();
boolean mms = record.isMms() || record.isMmsNotification(); boolean mms = record.isMms() || record.isMmsNotification();
Recipient recipient = record.getIndividualRecipient(); Recipient recipient = record.getIndividualRecipient();
Recipients recipients = record.getRecipients(); Recipient conversationRecipient = record.getRecipient();
long threadId = record.getThreadId(); long threadId = record.getThreadId();
CharSequence body = record.getDisplayBody(); CharSequence body = record.getDisplayBody();
Recipients threadRecipients = null; Recipient threadRecipients = null;
SlideDeck slideDeck = null; SlideDeck slideDeck = null;
long timestamp = record.getTimestamp(); long timestamp = record.getTimestamp();
if (threadId != -1) { if (threadId != -1) {
threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
} }
if (SmsDatabase.Types.isDecryptInProgressType(record.getType()) || !record.getBody().isPlaintext()) { if (SmsDatabase.Types.isDecryptInProgressType(record.getType()) || !record.getBody().isPlaintext()) {
@ -463,7 +462,7 @@ public class MessageNotifier {
} }
if (threadRecipients == null || !threadRecipients.isMuted()) { if (threadRecipients == null || !threadRecipients.isMuted()) {
notificationState.addNotification(new NotificationItem(id, mms, recipient, recipients, threadRecipients, threadId, body, timestamp, slideDeck)); notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck));
} }
} }

View File

@ -11,15 +11,14 @@ import android.support.v4.app.TaskStackBuilder;
import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
public class NotificationItem { public class NotificationItem {
private final long id; private final long id;
private final boolean mms; private final boolean mms;
private final @NonNull Recipients recipients; private final @NonNull Recipient conversationRecipient;
private final @NonNull Recipient individualRecipient; private final @NonNull Recipient individualRecipient;
private final @Nullable Recipients threadRecipients; private final @Nullable Recipient threadRecipient;
private final long threadId; private final long threadId;
private final @Nullable CharSequence text; private final @Nullable CharSequence text;
private final long timestamp; private final long timestamp;
@ -27,24 +26,24 @@ public class NotificationItem {
public NotificationItem(long id, boolean mms, public NotificationItem(long id, boolean mms,
@NonNull Recipient individualRecipient, @NonNull Recipient individualRecipient,
@NonNull Recipients recipients, @NonNull Recipient conversationRecipient,
@Nullable Recipients threadRecipients, @Nullable Recipient threadRecipient,
long threadId, @Nullable CharSequence text, long timestamp, long threadId, @Nullable CharSequence text, long timestamp,
@Nullable SlideDeck slideDeck) @Nullable SlideDeck slideDeck)
{ {
this.id = id; this.id = id;
this.mms = mms; this.mms = mms;
this.individualRecipient = individualRecipient; this.individualRecipient = individualRecipient;
this.recipients = recipients; this.conversationRecipient = conversationRecipient;
this.threadRecipients = threadRecipients; this.threadRecipient = threadRecipient;
this.text = text; this.text = text;
this.threadId = threadId; this.threadId = threadId;
this.timestamp = timestamp; this.timestamp = timestamp;
this.slideDeck = slideDeck; this.slideDeck = slideDeck;
} }
public @NonNull Recipients getRecipients() { public @NonNull Recipient getRecipient() {
return threadRecipients == null ? recipients : threadRecipients; return threadRecipient == null ? conversationRecipient : threadRecipient;
} }
public @NonNull Recipient getIndividualRecipient() { public @NonNull Recipient getIndividualRecipient() {
@ -69,8 +68,8 @@ public class NotificationItem {
public PendingIntent getPendingIntent(Context context) { public PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, ConversationActivity.class); Intent intent = new Intent(context, ConversationActivity.class);
Recipients notifyRecipients = threadRecipients != null ? threadRecipients : recipients; Recipient notifyRecipients = threadRecipient != null ? threadRecipient : conversationRecipient;
if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, notifyRecipients.getAddresses()); if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESS_EXTRA, notifyRecipients.getAddress());
intent.putExtra("thread_id", threadId); intent.putExtra("thread_id", threadId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));

View File

@ -11,7 +11,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.ConversationPopupActivity; import org.thoughtcrime.securesms.ConversationPopupActivity;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -45,10 +45,10 @@ public class NotificationState {
public @Nullable Uri getRingtone() { public @Nullable Uri getRingtone() {
if (!notifications.isEmpty()) { if (!notifications.isEmpty()) {
Recipients recipients = notifications.getFirst().getRecipients(); Recipient recipient = notifications.getFirst().getRecipient();
if (recipients != null) { if (recipient != null) {
return recipients.getRingtone(); return recipient.getRingtone();
} }
} }
@ -57,10 +57,10 @@ public class NotificationState {
public VibrateState getVibrate() { public VibrateState getVibrate() {
if (!notifications.isEmpty()) { if (!notifications.isEmpty()) {
Recipients recipients = notifications.getFirst().getRecipients(); Recipient recipient = notifications.getFirst().getRecipient();
if (recipients != null) { if (recipient != null) {
return recipients.getVibrate(); return recipient.getVibrate();
} }
} }
@ -115,26 +115,26 @@ public class NotificationState {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
} }
public PendingIntent getRemoteReplyIntent(Context context, Recipients recipients) { public PendingIntent getRemoteReplyIntent(Context context, Recipient recipient) {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!"); if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!");
Intent intent = new Intent(RemoteReplyReceiver.REPLY_ACTION); Intent intent = new Intent(RemoteReplyReceiver.REPLY_ACTION);
intent.setClass(context, RemoteReplyReceiver.class); intent.setClass(context, RemoteReplyReceiver.class);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
intent.putExtra(RemoteReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(RemoteReplyReceiver.ADDRESS_EXTRA, recipient.getAddress());
intent.setPackage(context.getPackageName()); intent.setPackage(context.getPackageName());
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
} }
public PendingIntent getAndroidAutoReplyIntent(Context context, Recipients recipients) { public PendingIntent getAndroidAutoReplyIntent(Context context, Recipient recipient) {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!"); if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!");
Intent intent = new Intent(AndroidAutoReplyReceiver.REPLY_ACTION); Intent intent = new Intent(AndroidAutoReplyReceiver.REPLY_ACTION);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.setClass(context, AndroidAutoReplyReceiver.class); intent.setClass(context, AndroidAutoReplyReceiver.class);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
intent.putExtra(AndroidAutoReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(AndroidAutoReplyReceiver.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(AndroidAutoReplyReceiver.THREAD_ID_EXTRA, (long)threads.toArray()[0]); intent.putExtra(AndroidAutoReplyReceiver.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
intent.setPackage(context.getPackageName()); intent.setPackage(context.getPackageName());
@ -160,11 +160,11 @@ public class NotificationState {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
} }
public PendingIntent getQuickReplyIntent(Context context, Recipients recipients) { public PendingIntent getQuickReplyIntent(Context context, Recipient recipient) {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications! " + threads.size()); 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 intent = new Intent(context, ConversationPopupActivity.class);
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses()); intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));

View File

@ -31,8 +31,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -45,9 +45,9 @@ import java.util.List;
*/ */
public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver { public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
public static final String TAG = RemoteReplyReceiver.class.getSimpleName(); public static final String TAG = RemoteReplyReceiver.class.getSimpleName();
public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY"; public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY";
public static final String ADDRESSES_EXTRA = "addresses"; public static final String ADDRESS_EXTRA = "address";
@Override @Override
protected void onReceive(final Context context, Intent intent, protected void onReceive(final Context context, Intent intent,
@ -59,7 +59,7 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
if (remoteInput == null) return; if (remoteInput == null) return;
final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA)); final Address address = intent.getParcelableExtra(ADDRESS_EXTRA);
final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_REMOTE_REPLY); final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_REMOTE_REPLY);
if (masterSecret != null && responseText != null) { if (masterSecret != null && responseText != null) {
@ -68,16 +68,16 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
long threadId; long threadId;
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses); Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(address);
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
Recipient recipient = RecipientFactory.getRecipientFor(context, address, false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false); if (recipient.isGroupRecipient()) {
if (recipients.isGroupRecipient()) { OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
threadId = MessageSender.send(context, masterSecret, reply, -1, false, null); threadId = MessageSender.send(context, masterSecret, reply, -1, false, null);
} else { } else {
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId); OutgoingTextMessage reply = new OutgoingTextMessage(recipient, responseText.toString(), expiresIn, subscriptionId);
threadId = MessageSender.send(context, masterSecret, reply, -1, false, null); threadId = MessageSender.send(context, masterSecret, reply, -1, false, null);
} }

View File

@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -57,16 +56,16 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
setCategory(NotificationCompat.CATEGORY_MESSAGE); setCategory(NotificationCompat.CATEGORY_MESSAGE);
} }
public void setThread(@NonNull Recipients recipients) { public void setThread(@NonNull Recipient recipient) {
if (privacy.isDisplayContact()) { if (privacy.isDisplayContact()) {
setContentTitle(recipients.toShortString()); setContentTitle(recipient.toShortString());
if (recipients.isSingleRecipient() && recipients.getPrimaryRecipient().getContactUri() != null) { if (recipient.getContactUri() != null) {
addPerson(recipients.getPrimaryRecipient().getContactUri().toString()); addPerson(recipient.getContactUri().toString());
} }
setLargeIcon(recipients.getContactPhoto() setLargeIcon(recipient.getContactPhoto()
.asDrawable(context, recipients.getColor() .asDrawable(context, recipient.getColor()
.toConversationColor(context))); .toConversationColor(context)));
} else { } else {
setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal)); setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal));
@ -80,14 +79,14 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
setNumber(messageCount); setNumber(messageCount);
} }
public void setPrimaryMessageBody(@NonNull Recipients threadRecipients, public void setPrimaryMessageBody(@NonNull Recipient threadRecipients,
@NonNull Recipient individualRecipient, @NonNull Recipient individualRecipient,
@NonNull CharSequence message, @NonNull CharSequence message,
@Nullable SlideDeck slideDeck) @Nullable SlideDeck slideDeck)
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && (threadRecipients.isGroupRecipient() || !threadRecipients.isSingleRecipient())) { if (privacy.isDisplayContact() && threadRecipients.isGroupRecipient()) {
stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": ")); stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": "));
} }
@ -162,13 +161,13 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
} }
} }
public void addMessageBody(@NonNull Recipients threadRecipients, public void addMessageBody(@NonNull Recipient threadRecipient,
@NonNull Recipient individualRecipient, @NonNull Recipient individualRecipient,
@Nullable CharSequence messageBody) @Nullable CharSequence messageBody)
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && (threadRecipients.isGroupRecipient() || !threadRecipients.isSingleRecipient())) { if (privacy.isDisplayContact() && threadRecipient.isGroupRecipient()) {
stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": ")); stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": "));
} }

View File

@ -7,14 +7,15 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
public class BlockedContactListItem extends RelativeLayout implements Recipients.RecipientsModifiedListener { public class BlockedContactListItem extends RelativeLayout implements RecipientModifiedListener {
private AvatarImageView contactPhotoImage; private AvatarImageView contactPhotoImage;
private TextView nameView; private TextView nameView;
private Recipients recipients; private Recipient recipient;
public BlockedContactListItem(Context context) { public BlockedContactListItem(Context context) {
super(context); super(context);
@ -35,15 +36,15 @@ public class BlockedContactListItem extends RelativeLayout implements Recipients
this.nameView = (TextView) findViewById(R.id.name); this.nameView = (TextView) findViewById(R.id.name);
} }
public void set(Recipients recipients) { public void set(Recipient recipients) {
this.recipients = recipients; this.recipient = recipients;
onModified(recipients); onModified(recipients);
recipients.addListener(this); recipients.addListener(this);
} }
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipient recipients) {
final AvatarImageView contactPhotoImage = this.contactPhotoImage; final AvatarImageView contactPhotoImage = this.contactPhotoImage;
final TextView nameView = this.nameView; final TextView nameView = this.nameView;
@ -56,7 +57,7 @@ public class BlockedContactListItem extends RelativeLayout implements Recipients
}); });
} }
public Recipients getRecipients() { public Recipient getRecipient() {
return recipients; return recipient;
} }
} }

View File

@ -1,27 +1,14 @@
package org.thoughtcrime.securesms.providers; package org.thoughtcrime.securesms.providers;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.Context;
import android.content.UriMatcher;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@ -26,31 +26,41 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public class Recipient { public class Recipient implements RecipientModifiedListener {
private final static String TAG = Recipient.class.getSimpleName(); private final static String TAG = Recipient.class.getSimpleName();
private final Set<RecipientModifiedListener> listeners = Collections.newSetFromMap(new WeakHashMap<RecipientModifiedListener, Boolean>()); private final Set<RecipientModifiedListener> listeners = Collections.newSetFromMap(new WeakHashMap<RecipientModifiedListener, Boolean>());
private final @NonNull Address address; private final @NonNull Address address;
private final @NonNull List<Recipient> participants = new LinkedList<>();
private @Nullable String name; private @Nullable String name;
private @Nullable String customLabel; private @Nullable String customLabel;
private boolean stale; private boolean stale;
private boolean resolving; private boolean resolving;
private ContactPhoto contactPhoto; private ContactPhoto contactPhoto;
private Uri contactUri; private Uri contactUri;
private Uri ringtone = null;
private long mutedUntil = 0;
private boolean blocked = false;
private VibrateState vibrate = VibrateState.DEFAULT;
private int expireMessages = 0;
@Nullable private MaterialColor color; @Nullable private MaterialColor color;
@ -64,11 +74,16 @@ public class Recipient {
this.resolving = true; this.resolving = true;
if (stale != null) { if (stale != null) {
this.name = stale.name; this.name = stale.name;
this.contactUri = stale.contactUri; this.contactUri = stale.contactUri;
this.contactPhoto = stale.contactPhoto; this.contactPhoto = stale.contactPhoto;
this.color = stale.color; this.color = stale.color;
this.customLabel = stale.customLabel; this.customLabel = stale.customLabel;
this.ringtone = stale.ringtone;
this.mutedUntil = stale.mutedUntil;
this.blocked = stale.blocked;
this.vibrate = stale.vibrate;
this.expireMessages = stale.expireMessages;
} }
future.addListener(new FutureTaskListener<RecipientDetails>() { future.addListener(new FutureTaskListener<RecipientDetails>() {
@ -76,12 +91,22 @@ public class Recipient {
public void onSuccess(RecipientDetails result) { public void onSuccess(RecipientDetails result) {
if (result != null) { if (result != null) {
synchronized (Recipient.this) { synchronized (Recipient.this) {
Recipient.this.name = result.name; Recipient.this.name = result.name;
Recipient.this.contactUri = result.contactUri; Recipient.this.contactUri = result.contactUri;
Recipient.this.contactPhoto = result.avatar; Recipient.this.contactPhoto = result.avatar;
Recipient.this.color = result.color; Recipient.this.color = result.color;
Recipient.this.customLabel = result.customLabel; Recipient.this.customLabel = result.customLabel;
Recipient.this.resolving = false; Recipient.this.ringtone = result.ringtone;
Recipient.this.mutedUntil = result.mutedUntil;
Recipient.this.blocked = result.blocked;
Recipient.this.vibrate = result.vibrateState;
Recipient.this.expireMessages = result.expireMessages;
Recipient.this.participants.addAll(result.participants);
Recipient.this.resolving = false;
if (!listeners.isEmpty()) {
for (Recipient recipient : participants) recipient.addListener(Recipient.this);
}
} }
notifyListeners(); notifyListeners();
@ -95,14 +120,20 @@ public class Recipient {
}); });
} }
Recipient(Address address, RecipientDetails details) { Recipient(@NonNull Address address, @NonNull RecipientDetails details) {
this.address = address; this.address = address;
this.contactUri = details.contactUri; this.contactUri = details.contactUri;
this.name = details.name; this.name = details.name;
this.contactPhoto = details.avatar; this.contactPhoto = details.avatar;
this.color = details.color; this.color = details.color;
this.customLabel = details.customLabel;
this.ringtone = details.ringtone;
this.mutedUntil = details.mutedUntil;
this.blocked = details.blocked;
this.vibrate = details.vibrateState;
this.expireMessages = details.expireMessages;
this.participants.addAll(details.participants);
this.resolving = false; this.resolving = false;
this.customLabel = details.customLabel;
} }
public synchronized @Nullable Uri getContactUri() { public synchronized @Nullable Uri getContactUri() {
@ -110,13 +141,24 @@ public class Recipient {
} }
public synchronized @Nullable String getName() { public synchronized @Nullable String getName() {
if (this.name == null && isMmsGroupRecipient()) {
List<String> names = new LinkedList<>();
for (Recipient recipient : participants) {
names.add(recipient.toShortString());
}
return Util.join(names, ", ");
}
return this.name; return this.name;
} }
public synchronized @NonNull MaterialColor getColor() { public synchronized @NonNull MaterialColor getColor() {
if (color != null) return color; if (isGroupRecipient()) return MaterialColor.GROUP;
else if (name != null) return ContactColors.generateFor(name); else if (color != null) return color;
else return ContactColors.UNKNOWN_COLOR; else if (name != null) return ContactColors.generateFor(name);
else return ContactColors.UNKNOWN_COLOR;
} }
public void setColor(@NonNull MaterialColor color) { public void setColor(@NonNull MaterialColor color) {
@ -139,22 +181,101 @@ public class Recipient {
return address.isGroup(); return address.isGroup();
} }
public boolean isMmsGroupRecipient() {
return address.isMmsGroup();
}
public boolean isPushGroupRecipient() {
return address.isGroup() && !address.isMmsGroup();
}
public List<Recipient> getParticipants() {
return participants;
}
public synchronized void addListener(RecipientModifiedListener listener) { public synchronized void addListener(RecipientModifiedListener listener) {
if (listeners.isEmpty()) {
for (Recipient recipient : participants) recipient.addListener(this);
}
listeners.add(listener); listeners.add(listener);
} }
public synchronized void removeListener(RecipientModifiedListener listener) { public synchronized void removeListener(RecipientModifiedListener listener) {
listeners.remove(listener); listeners.remove(listener);
if (listeners.isEmpty()) {
for (Recipient recipient : participants) recipient.removeListener(this);
}
} }
public synchronized String toShortString() { public synchronized String toShortString() {
return (name == null ? address.serialize() : name); return (getName() == null ? address.serialize() : getName());
} }
public synchronized @NonNull ContactPhoto getContactPhoto() { public synchronized @NonNull ContactPhoto getContactPhoto() {
return contactPhoto; return contactPhoto;
} }
public synchronized @Nullable Uri getRingtone() {
return ringtone;
}
public void setRingtone(Uri ringtone) {
synchronized (this) {
this.ringtone = ringtone;
}
notifyListeners();
}
public synchronized boolean isMuted() {
return System.currentTimeMillis() <= mutedUntil;
}
public void setMuted(long mutedUntil) {
synchronized (this) {
this.mutedUntil = mutedUntil;
}
notifyListeners();
}
public synchronized boolean isBlocked() {
return blocked;
}
public void setBlocked(boolean blocked) {
synchronized (this) {
this.blocked = blocked;
}
notifyListeners();
}
public synchronized VibrateState getVibrate() {
return vibrate;
}
public void setVibrate(VibrateState vibrate) {
synchronized (this) {
this.vibrate = vibrate;
}
notifyListeners();
}
public synchronized int getExpireMessages() {
return expireMessages;
}
public void setExpireMessages(int expireMessages) {
synchronized (this) {
this.expireMessages = expireMessages;
}
notifyListeners();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@ -182,8 +303,9 @@ public class Recipient {
listener.onModified(this); listener.onModified(this);
} }
public interface RecipientModifiedListener { @Override
public void onModified(Recipient recipient); public void onModified(Recipient recipient) {
notifyListeners();
} }
boolean isStale() { boolean isStale() {

View File

@ -22,37 +22,12 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import java.util.Collection;
public class RecipientFactory { public class RecipientFactory {
public static final String RECIPIENT_CLEAR_ACTION = "org.thoughtcrime.securesms.database.RecipientFactory.CLEAR"; public static final String RECIPIENT_CLEAR_ACTION = "org.thoughtcrime.securesms.database.RecipientFactory.CLEAR";
private static final RecipientProvider provider = new RecipientProvider(); private static final RecipientProvider provider = new RecipientProvider();
public static @NonNull Recipients getRecipientsFor(Context context, Collection<Recipient> recipients, boolean asynchronous) {
Address[] addresses= new Address[recipients.size()];
int i = 0;
for (Recipient recipient : recipients) {
addresses[i++] = recipient.getAddress();
}
return provider.getRecipients(context, addresses, asynchronous);
}
public static Recipients getRecipientsFor(Context context, Recipient recipient, boolean asynchronous) {
Address[] addresses = new Address[1];
addresses[0] = recipient.getAddress();
return provider.getRecipients(context, addresses, 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 static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, boolean asynchronous) { public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
if (address == null) throw new AssertionError(address); if (address == null) throw new AssertionError(address);
return provider.getRecipient(context, address, asynchronous); return provider.getRecipient(context, address, asynchronous);

View File

@ -0,0 +1,6 @@
package org.thoughtcrime.securesms.recipients;
public interface RecipientModifiedListener {
public void onModified(Recipient recipient);
}

View File

@ -28,21 +28,18 @@ import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor; 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.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -55,7 +52,6 @@ class RecipientProvider {
private static final String TAG = RecipientProvider.class.getSimpleName(); private static final String TAG = RecipientProvider.class.getSimpleName();
private static final RecipientCache recipientCache = new RecipientCache(); private static final RecipientCache recipientCache = new RecipientCache();
private static final RecipientsCache recipientsCache = new RecipientsCache();
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor(); private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
private static final String[] CALLER_ID_PROJECTION = new String[] { private static final String[] CALLER_ID_PROJECTION = new String[] {
@ -69,7 +65,7 @@ class RecipientProvider {
private static final Map<String, RecipientDetails> STATIC_DETAILS = new HashMap<String, RecipientDetails>() {{ private static final Map<String, RecipientDetails> STATIC_DETAILS = new HashMap<String, RecipientDetails>() {{
put("262966", new RecipientDetails("Amazon", null, null, put("262966", new RecipientDetails("Amazon", null, null,
ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_amazon), ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_amazon),
ContactColors.UNKNOWN_COLOR)); null, null));
}}; }};
@NonNull Recipient getRecipient(Context context, Address address, boolean asynchronous) { @NonNull Recipient getRecipient(Context context, Address address, boolean asynchronous) {
@ -81,35 +77,15 @@ class RecipientProvider {
if (asynchronous) { if (asynchronous) {
cachedRecipient = new Recipient(address, cachedRecipient, getRecipientDetailsAsync(context, address)); cachedRecipient = new Recipient(address, cachedRecipient, getRecipientDetailsAsync(context, address));
} else { } else {
cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address)); cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address, false));
} }
recipientCache.set(address, cachedRecipient); recipientCache.set(address, cachedRecipient);
return cachedRecipient; return cachedRecipient;
} }
@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<Recipient> recipientList = new LinkedList<>();
for (Address address : recipientAddresses) {
recipientList.add(getRecipient(context, address, asynchronous));
}
if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientAddresses));
else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientAddresses));
recipientsCache.set(new RecipientAddresses(recipientAddresses), cachedRecipients);
return cachedRecipients;
}
void clearCache() { void clearCache() {
recipientCache.reset(); recipientCache.reset();
recipientsCache.reset();
} }
private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context, final @NonNull Address address) private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context, final @NonNull Address address)
@ -117,7 +93,7 @@ class RecipientProvider {
Callable<RecipientDetails> task = new Callable<RecipientDetails>() { Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
@Override @Override
public RecipientDetails call() throws Exception { public RecipientDetails call() throws Exception {
return getRecipientDetailsSync(context, address); return getRecipientDetailsSync(context, address, true);
} }
}; };
@ -126,14 +102,13 @@ class RecipientProvider {
return future; return future;
} }
private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address) { private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, boolean nestedAsynchronous) {
if (address.isGroup()) return getGroupRecipientDetails(context, address); if (address.isGroup()) return getGroupRecipientDetails(context, address, nestedAsynchronous);
else return getIndividualRecipientDetails(context, address); else return getIndividualRecipientDetails(context, address);
} }
private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address) { private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address) {
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new Address[]{address}); Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(address);
MaterialColor color = preferences.isPresent() ? preferences.get().getColor() : null;
if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) { if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
@ -149,7 +124,7 @@ class RecipientProvider {
Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""), Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""),
name); name);
return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, color); return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, preferences.orNull(), null);
} else { } else {
Log.w(TAG, "resultNumber is null"); Log.w(TAG, "resultNumber is null");
} }
@ -161,84 +136,61 @@ class RecipientProvider {
} }
if (STATIC_DETAILS.containsKey(address.serialize())) return STATIC_DETAILS.get(address.serialize()); if (STATIC_DETAILS.containsKey(address.serialize())) return STATIC_DETAILS.get(address.serialize());
else return new RecipientDetails(null, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), color); else return new RecipientDetails(null, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), preferences.orNull(), null);
} }
private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId) { private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId, boolean asynchronous) {
try { GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.toGroupString());
GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context)
.getGroup(GroupUtil.getDecodedId(groupId.toGroupString()));
if (record != null) { if (record != null) {
ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar()); ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar());
String title = record.getTitle(); String title = record.getTitle();
List<Address> memberAddresses = record.getMembers();
List<Recipient> members = new LinkedList<>();
if (title == null) { for (Address memberAddress : memberAddresses) {
title = context.getString(R.string.RecipientProvider_unnamed_group);; members.add(getRecipient(context, memberAddress, asynchronous));
}
return new RecipientDetails(title, null, null, contactPhoto, null);
} }
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null); if (!groupId.isMmsGroup() && title == null) {
} catch (IOException e) { title = context.getString(R.string.RecipientProvider_unnamed_group);;
Log.w("RecipientProvider", e); }
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null);
return new RecipientDetails(title, null, null, contactPhoto, null, members);
} }
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null);
} }
private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, Address[] addresses) { static class RecipientDetails {
return DatabaseFactory.getRecipientPreferenceDatabase(context) @Nullable public final String name;
.getRecipientsPreferences(addresses) @Nullable public final String customLabel;
.orNull(); @NonNull public final ContactPhoto avatar;
} @Nullable public final Uri contactUri;
@Nullable public final MaterialColor color;
private ListenableFutureTask<RecipientsPreferences> getRecipientsPreferencesAsync(final Context context, final Address[] addresses) { @Nullable public final Uri ringtone;
ListenableFutureTask<RecipientsPreferences> task = new ListenableFutureTask<>(new Callable<RecipientsPreferences>() { public final long mutedUntil;
@Override @Nullable public final VibrateState vibrateState;
public RecipientsPreferences call() throws Exception { public final boolean blocked;
return getRecipientsPreferencesSync(context, addresses); public final int expireMessages;
} @NonNull public final List<Recipient> participants;
});
asyncRecipientResolver.execute(task);
return task;
}
public static class RecipientDetails {
@Nullable public final String name;
@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, @Nullable String customLabel, public RecipientDetails(@Nullable String name, @Nullable String customLabel,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar, @Nullable Uri contactUri, @NonNull ContactPhoto avatar,
@Nullable MaterialColor color) @Nullable RecipientsPreferences preferences,
@Nullable List<Recipient> participants)
{ {
this.name = name; this.name = name;
this.customLabel = customLabel; this.customLabel = customLabel;
this.avatar = avatar; this.avatar = avatar;
this.contactUri = contactUri; this.contactUri = contactUri;
this.color = color; this.color = preferences != null ? preferences.getColor() : null;
} this.ringtone = preferences != null ? preferences.getRingtone() : null;
} this.mutedUntil = preferences != null ? preferences.getMuteUntil() : 0;
this.vibrateState = preferences != null ? preferences.getVibrateState() : null;
private static class RecipientAddresses { this.blocked = preferences != null && preferences.isBlocked();
private final Address[] addresses; this.expireMessages = preferences != null ? preferences.getExpireMessages() : 0;
this.participants = participants == null ? new LinkedList<Recipient>() : participants;
private RecipientAddresses(Address[] addresses) {
this.addresses = addresses;
}
public boolean equals(Object other) {
if (other == null || !(other instanceof RecipientAddresses)) return false;
return Arrays.equals(this.addresses, ((RecipientAddresses) other).addresses);
}
public int hashCode() {
return Arrays.hashCode(addresses);
} }
} }
@ -262,26 +214,4 @@ class RecipientProvider {
} }
private static class RecipientsCache {
private final Map<RecipientAddresses,Recipients> cache = new LRUCache<>(1000);
public synchronized Recipients get(RecipientAddresses addresses) {
return cache.get(addresses);
}
public synchronized void set(RecipientAddresses addresses, Recipients recipients) {
cache.put(addresses, recipients);
}
public synchronized void reset() {
for (Recipients recipients : cache.values()) {
recipients.setStale();
}
}
}
} }

View File

@ -1,321 +0,0 @@
/**
* Copyright (C) 2015 Open 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 <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.recipients;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
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.ListenableFutureTask;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
public class Recipients implements Iterable<Recipient>, RecipientModifiedListener {
private static final String TAG = Recipients.class.getSimpleName();
private final Set<RecipientsModifiedListener> listeners = Collections.newSetFromMap(new WeakHashMap<RecipientsModifiedListener, Boolean>());
private final List<Recipient> recipients;
private Uri ringtone = null;
private long mutedUntil = 0;
private boolean blocked = false;
private VibrateState vibrate = VibrateState.DEFAULT;
private int expireMessages = 0;
private boolean stale = false;
Recipients() {
this(new LinkedList<Recipient>(), null);
}
Recipients(List<Recipient> recipients, @Nullable RecipientsPreferences preferences) {
this.recipients = recipients;
if (preferences != null) {
ringtone = preferences.getRingtone();
mutedUntil = preferences.getMuteUntil();
vibrate = preferences.getVibrateState();
blocked = preferences.isBlocked();
expireMessages = preferences.getExpireMessages();
}
}
Recipients(@NonNull List<Recipient> recipients,
@Nullable Recipients stale,
@NonNull ListenableFutureTask<RecipientsPreferences> preferences)
{
this.recipients = recipients;
if (stale != null) {
ringtone = stale.ringtone;
mutedUntil = stale.mutedUntil;
vibrate = stale.vibrate;
blocked = stale.blocked;
expireMessages = stale.expireMessages;
}
preferences.addListener(new FutureTaskListener<RecipientsPreferences>() {
@Override
public void onSuccess(RecipientsPreferences result) {
if (result != null) {
Set<RecipientsModifiedListener> localListeners;
synchronized (Recipients.this) {
ringtone = result.getRingtone();
mutedUntil = result.getMuteUntil();
vibrate = result.getVibrateState();
blocked = result.isBlocked();
expireMessages = result.getExpireMessages();
localListeners = new HashSet<>(listeners);
}
for (RecipientsModifiedListener listener : localListeners) {
listener.onModified(Recipients.this);
}
}
}
@Override
public void onFailure(ExecutionException error) {
Log.w(TAG, error);
}
});
}
public synchronized @Nullable Uri getRingtone() {
return ringtone;
}
public void setRingtone(Uri ringtone) {
synchronized (this) {
this.ringtone = ringtone;
}
notifyListeners();
}
public synchronized boolean isMuted() {
return System.currentTimeMillis() <= mutedUntil;
}
public void setMuted(long mutedUntil) {
synchronized (this) {
this.mutedUntil = mutedUntil;
}
notifyListeners();
}
public synchronized boolean isBlocked() {
return blocked;
}
public void setBlocked(boolean blocked) {
synchronized (this) {
this.blocked = blocked;
}
notifyListeners();
}
public synchronized VibrateState getVibrate() {
return vibrate;
}
public void setVibrate(VibrateState vibrate) {
synchronized (this) {
this.vibrate = vibrate;
}
notifyListeners();
}
public @NonNull ContactPhoto getContactPhoto() {
if (recipients.size() == 1) return recipients.get(0).getContactPhoto();
else return ContactPhotoFactory.getDefaultGroupPhoto();
}
public synchronized @NonNull MaterialColor getColor() {
if (!isSingleRecipient() || isGroupRecipient()) return MaterialColor.GROUP;
else if (isEmpty()) return ContactColors.UNKNOWN_COLOR;
else return recipients.get(0).getColor();
}
public synchronized void setColor(@NonNull MaterialColor color) {
if (!isSingleRecipient() || isGroupRecipient()) throw new AssertionError("Groups don't have colors!");
else if (!isEmpty()) recipients.get(0).setColor(color);
}
public synchronized int getExpireMessages() {
return expireMessages;
}
public void setExpireMessages(int expireMessages) {
synchronized (this) {
this.expireMessages = expireMessages;
}
notifyListeners();
}
public synchronized void addListener(RecipientsModifiedListener listener) {
if (listeners.isEmpty()) {
for (Recipient recipient : recipients) {
recipient.addListener(this);
}
}
listeners.add(listener);
}
public synchronized void removeListener(RecipientsModifiedListener listener) {
listeners.remove(listener);
if (listeners.isEmpty()) {
for (Recipient recipient : recipients) {
recipient.removeListener(this);
}
}
}
public boolean isEmailRecipient() {
for (Recipient recipient : recipients) {
if (recipient.getAddress().isEmail()) {
return true;
}
}
return false;
}
public boolean isGroupRecipient() {
return isSingleRecipient() && recipients.get(0).getAddress().isGroup();
}
public boolean isEmpty() {
return this.recipients.isEmpty();
}
public boolean isSingleRecipient() {
return this.recipients.size() == 1;
}
public @Nullable Recipient getPrimaryRecipient() {
if (!isEmpty())
return this.recipients.get(0);
else
return null;
}
public List<Recipient> getRecipientsList() {
return this.recipients;
}
public Address[] getAddresses() {
Address[] addresses = new Address[recipients.size()];
for (int i=0;i<recipients.size();i++) {
addresses[i] = recipients.get(i).getAddress();
}
Arrays.sort(addresses);
return addresses;
}
public List<Address> getAddressesList() {
List<Address> results = new LinkedList<>();
Collections.addAll(results, getAddresses());
return results;
}
public String toShortString() {
String fromString = "";
for (int i=0;i<recipients.size();i++) {
fromString += recipients.get(i).toShortString();
if (i != recipients.size() -1 )
fromString += ", ";
}
return fromString;
}
@Override
public Iterator<Recipient> iterator() {
return recipients.iterator();
}
@Override
public void onModified(Recipient recipient) {
notifyListeners();
}
private void notifyListeners() {
Set<RecipientsModifiedListener> localListeners;
synchronized (this) {
localListeners = new HashSet<>(listeners);
}
for (RecipientsModifiedListener listener : localListeners) {
listener.onModified(this);
}
}
boolean isStale() {
return stale;
}
void setStale() {
this.stale = true;
}
boolean isResolving() {
for (Recipient recipient : recipients) {
if (recipient.isResolving()) return true;
}
return false;
}
public interface RecipientsModifiedListener {
public void onModified(Recipients recipient);
}
}

View File

@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import java.util.LinkedList; import java.util.LinkedList;
@ -49,17 +49,17 @@ public class DirectShareService extends ChooserTargetService {
ThreadRecord record; ThreadRecord record;
while ((record = reader.getNext()) != null && results.size() < 10) { while ((record = reader.getNext()) != null && results.size() < 10) {
Recipients recipients = RecipientFactory.getRecipientsFor(this, record.getRecipients().getAddresses(), false); Recipient recipient = RecipientFactory.getRecipientFor(this, record.getRecipient().getAddress(), false);
String name = recipients.toShortString(); String name = recipient.toShortString();
Drawable drawable = recipients.getContactPhoto().asDrawable(this, recipients.getColor().toConversationColor(this)); Drawable drawable = recipient.getContactPhoto().asDrawable(this, recipient.getColor().toConversationColor(this));
Bitmap avatar = BitmapUtil.createFromDrawable(drawable, 500, 500); Bitmap avatar = BitmapUtil.createFromDrawable(drawable, 500, 500);
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
parcel.writeTypedArray(recipients.getAddresses(), 0); parcel.writeParcelable(recipient.getAddress(), 0);
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getThreadId()); bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getThreadId());
bundle.putByteArray(ShareActivity.EXTRA_ADDRESSES_MARSHALLED, parcel.marshall()); bundle.putByteArray(ShareActivity.EXTRA_ADDRESS_MARSHALLED, parcel.marshall());
bundle.putInt(ShareActivity.EXTRA_DISTRIBUTION_TYPE, record.getDistributionType()); bundle.putInt(ShareActivity.EXTRA_DISTRIBUTION_TYPE, record.getDistributionType());
bundle.setClassLoader(getClassLoader()); bundle.setClassLoader(getClassLoader());

View File

@ -12,11 +12,8 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.Rfc5724Uri; import org.thoughtcrime.securesms.util.Rfc5724Uri;
@ -49,31 +46,20 @@ public class QuickResponseService extends MasterSecretIntentService {
try { try {
Rfc5724Uri uri = new Rfc5724Uri(intent.getDataString()); Rfc5724Uri uri = new Rfc5724Uri(intent.getDataString());
String content = intent.getStringExtra(Intent.EXTRA_TEXT); String content = intent.getStringExtra(Intent.EXTRA_TEXT);
String numbers = uri.getPath(); String number = uri.getPath();
if (numbers.contains("%")){ if (number.contains("%")){
numbers = URLDecoder.decode(numbers); number = URLDecoder.decode(number);
} }
String[] numbersArray = numbers.split(","); Address address = Address.fromExternal(this, number);
Address[] addresses = new Address[numbersArray.length]; Recipient recipient = RecipientFactory.getRecipientFor(this, address, false);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipient.getAddress());
for (int i=0;i<numbersArray.length;i++) {
addresses[i] = Address.fromExternal(this, numbersArray[i]);
}
Recipients recipients = RecipientFactory.getRecipientsFor(this, addresses, false);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getAddresses());
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0; long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
if (!TextUtils.isEmpty(content)) { if (!TextUtils.isEmpty(content)) {
if (recipients.isSingleRecipient()) { MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipient, content, expiresIn, subscriptionId), -1, false, null);
MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipients, content, expiresIn, subscriptionId), -1, false, null);
} else {
MessageSender.send(this, masterSecret, new OutgoingMediaMessage(recipients, new SlideDeck(), content, System.currentTimeMillis(),
subscriptionId, expiresIn, ThreadDatabase.DistributionTypes.DEFAULT), -1, false, null);
}
} }
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
Toast.makeText(this, R.string.QuickResponseService_problem_sending_message, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.QuickResponseService_problem_sending_message, Toast.LENGTH_LONG).show();

View File

@ -75,7 +75,7 @@ public class IncomingTextMessage implements Parcelable {
this.expiresInMillis = expiresInMillis; this.expiresInMillis = expiresInMillis;
if (group.isPresent()) { if (group.isPresent()) {
this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId())); this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId(), false));
} else { } else {
this.groupId = null; this.groupId = null;
} }

View File

@ -40,7 +40,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.push.AccountManagerFactory; import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -63,13 +63,13 @@ public class MessageSender {
final SmsDatabase.InsertListener insertListener) final SmsDatabase.InsertListener insertListener)
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = message.getRecipients(); Recipient recipient = message.getRecipient();
boolean keyExchange = message.isKeyExchange(); boolean keyExchange = message.isKeyExchange();
long allocatedThreadId; long allocatedThreadId;
if (threadId == -1) { if (threadId == -1) {
allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
} else { } else {
allocatedThreadId = threadId; allocatedThreadId = threadId;
} }
@ -77,7 +77,7 @@ public class MessageSender {
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId, long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId,
message, forceSms, System.currentTimeMillis(), insertListener); message, forceSms, System.currentTimeMillis(), insertListener);
sendTextMessage(context, recipients, forceSms, keyExchange, messageId, message.getExpiresIn()); sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.getExpiresIn());
return allocatedThreadId; return allocatedThreadId;
} }
@ -96,15 +96,15 @@ public class MessageSender {
long allocatedThreadId; long allocatedThreadId;
if (threadId == -1) { if (threadId == -1) {
allocatedThreadId = threadDatabase.getThreadIdFor(message.getRecipients(), message.getDistributionType()); allocatedThreadId = threadDatabase.getThreadIdFor(message.getRecipient(), message.getDistributionType());
} else { } else {
allocatedThreadId = threadId; allocatedThreadId = threadId;
} }
Recipients recipients = message.getRecipients(); Recipient recipient = message.getRecipient();
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), message, allocatedThreadId, forceSms, insertListener); long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), message, allocatedThreadId, forceSms, insertListener);
sendMediaMessage(context, masterSecret, recipients, forceSms, messageId, message.getExpiresIn()); sendMediaMessage(context, masterSecret, recipient, forceSms, messageId, message.getExpiresIn());
return allocatedThreadId; return allocatedThreadId;
} catch (MmsException e) { } catch (MmsException e) {
@ -113,11 +113,9 @@ public class MessageSender {
} }
} }
public static void resendGroupMessage(Context context, MasterSecret masterSecret, MessageRecord messageRecord, Address filterAddress) { public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) {
if (!messageRecord.isMms()) throw new AssertionError("Not Group"); if (!messageRecord.isMms()) throw new AssertionError("Not Group");
sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterAddress);
Recipients recipients = DatabaseFactory.getMmsAddressDatabase(context).getRecipientsForId(messageRecord.getId());
sendGroupPush(context, recipients, messageRecord.getId(), filterAddress);
} }
public static void resend(Context context, MasterSecret masterSecret, MessageRecord messageRecord) { public static void resend(Context context, MasterSecret masterSecret, MessageRecord messageRecord) {
@ -126,13 +124,12 @@ public class MessageSender {
boolean forceSms = messageRecord.isForcedSms(); boolean forceSms = messageRecord.isForcedSms();
boolean keyExchange = messageRecord.isKeyExchange(); boolean keyExchange = messageRecord.isKeyExchange();
long expiresIn = messageRecord.getExpiresIn(); long expiresIn = messageRecord.getExpiresIn();
Recipient recipient = messageRecord.getRecipient();
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
Recipients recipients = DatabaseFactory.getMmsAddressDatabase(context).getRecipientsForId(messageId); sendMediaMessage(context, masterSecret, recipient, forceSms, messageId, expiresIn);
sendMediaMessage(context, masterSecret, recipients, forceSms, messageId, expiresIn);
} else { } else {
Recipients recipients = messageRecord.getRecipients(); sendTextMessage(context, recipient, forceSms, keyExchange, messageId, expiresIn);
sendTextMessage(context, recipients, forceSms, keyExchange, messageId, expiresIn);
} }
} catch (MmsException e) { } catch (MmsException e) {
Log.w(TAG, e); Log.w(TAG, e);
@ -140,31 +137,31 @@ public class MessageSender {
} }
private static void sendMediaMessage(Context context, MasterSecret masterSecret, private static void sendMediaMessage(Context context, MasterSecret masterSecret,
Recipients recipients, boolean forceSms, Recipient recipient, boolean forceSms,
long messageId, long expiresIn) long messageId, long expiresIn)
throws MmsException throws MmsException
{ {
if (!forceSms && isSelfSend(context, recipients)) { if (!forceSms && isSelfSend(context, recipient)) {
sendMediaSelf(context, masterSecret, messageId, expiresIn); sendMediaSelf(context, masterSecret, messageId, expiresIn);
} else if (isGroupPushSend(recipients)) { } else if (isGroupPushSend(recipient)) {
sendGroupPush(context, recipients, messageId, null); sendGroupPush(context, recipient, messageId, null);
} else if (!forceSms && isPushMediaSend(context, recipients)) { } else if (!forceSms && isPushMediaSend(context, recipient)) {
sendMediaPush(context, recipients, messageId); sendMediaPush(context, recipient, messageId);
} else { } else {
sendMms(context, messageId); sendMms(context, messageId);
} }
} }
private static void sendTextMessage(Context context, Recipients recipients, private static void sendTextMessage(Context context, Recipient recipient,
boolean forceSms, boolean keyExchange, boolean forceSms, boolean keyExchange,
long messageId, long expiresIn) long messageId, long expiresIn)
{ {
if (!forceSms && isSelfSend(context, recipients)) { if (!forceSms && isSelfSend(context, recipient)) {
sendTextSelf(context, messageId, expiresIn); sendTextSelf(context, messageId, expiresIn);
} else if (!forceSms && isPushTextSend(context, recipients, keyExchange)) { } else if (!forceSms && isPushTextSend(context, recipient, keyExchange)) {
sendTextPush(context, recipients, messageId); sendTextPush(context, recipient, messageId);
} else { } else {
sendSms(context, recipients, messageId); sendSms(context, recipient, messageId);
} }
} }
@ -200,24 +197,24 @@ public class MessageSender {
} }
} }
private static void sendTextPush(Context context, Recipients recipients, long messageId) { private static void sendTextPush(Context context, Recipient recipient, long messageId) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
jobManager.add(new PushTextSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress())); jobManager.add(new PushTextSendJob(context, messageId, recipient.getAddress()));
} }
private static void sendMediaPush(Context context, Recipients recipients, long messageId) { private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
jobManager.add(new PushMediaSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress())); jobManager.add(new PushMediaSendJob(context, messageId, recipient.getAddress()));
} }
private static void sendGroupPush(Context context, Recipients recipients, long messageId, Address filterAddress) { private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
jobManager.add(new PushGroupSendJob(context, messageId, recipients.getPrimaryRecipient().getAddress(), filterAddress)); jobManager.add(new PushGroupSendJob(context, messageId, recipient.getAddress(), filterAddress));
} }
private static void sendSms(Context context, Recipients recipients, long messageId) { private static void sendSms(Context context, Recipient recipient, long messageId) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
jobManager.add(new SmsSendJob(context, messageId, recipients.getPrimaryRecipient().getName())); jobManager.add(new SmsSendJob(context, messageId, recipient.getName()));
} }
private static void sendMms(Context context, long messageId) { private static void sendMms(Context context, long messageId) {
@ -225,7 +222,7 @@ public class MessageSender {
jobManager.add(new MmsSendJob(context, messageId)); jobManager.add(new MmsSendJob(context, messageId));
} }
private static boolean isPushTextSend(Context context, Recipients recipients, boolean keyExchange) { private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) {
if (!TextSecurePreferences.isPushRegistered(context)) { if (!TextSecurePreferences.isPushRegistered(context)) {
return false; return false;
} }
@ -234,39 +231,36 @@ public class MessageSender {
return false; return false;
} }
return isPushDestination(context, recipients.getPrimaryRecipient().getAddress()); return isPushDestination(context, recipient.getAddress());
} }
private static boolean isPushMediaSend(Context context, Recipients recipients) { private static boolean isPushMediaSend(Context context, Recipient recipient) {
if (!TextSecurePreferences.isPushRegistered(context)) { if (!TextSecurePreferences.isPushRegistered(context)) {
return false; return false;
} }
if (recipients.getRecipientsList().size() > 1) { if (recipient.isGroupRecipient()) {
return false; return false;
} }
return isPushDestination(context, recipients.getPrimaryRecipient().getAddress()); return isPushDestination(context, recipient.getAddress());
} }
private static boolean isGroupPushSend(Recipients recipients) { private static boolean isGroupPushSend(Recipient recipient) {
return recipients.getPrimaryRecipient().getAddress().isGroup(); return recipient.getAddress().isGroup() &&
!recipient.getAddress().isMmsGroup();
} }
private static boolean isSelfSend(Context context, Recipients recipients) { private static boolean isSelfSend(Context context, Recipient recipient) {
if (!TextSecurePreferences.isPushRegistered(context)) { if (!TextSecurePreferences.isPushRegistered(context)) {
return false; return false;
} }
if (!recipients.isSingleRecipient()) { if (recipient.isGroupRecipient()) {
return false; return false;
} }
if (recipients.isGroupRecipient()) { return Util.isOwnNumber(context, recipient.getAddress());
return false;
}
return Util.isOwnNumber(context, recipients.getPrimaryRecipient().getAddress());
} }
private static boolean isPushDestination(Context context, Address destination) { private static boolean isPushDestination(Context context, Address destination) {

View File

@ -1,11 +1,11 @@
package org.thoughtcrime.securesms.sms; package org.thoughtcrime.securesms.sms;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
public class OutgoingEncryptedMessage extends OutgoingTextMessage { public class OutgoingEncryptedMessage extends OutgoingTextMessage {
public OutgoingEncryptedMessage(Recipients recipients, String body, long expiresIn) { public OutgoingEncryptedMessage(Recipient recipient, String body, long expiresIn) {
super(recipients, body, expiresIn, -1); super(recipient, body, expiresIn, -1);
} }
private OutgoingEncryptedMessage(OutgoingEncryptedMessage base, String body) { private OutgoingEncryptedMessage(OutgoingEncryptedMessage base, String body) {

View File

@ -1,16 +1,16 @@
package org.thoughtcrime.securesms.sms; package org.thoughtcrime.securesms.sms;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipient;
public class OutgoingIdentityDefaultMessage extends OutgoingTextMessage { public class OutgoingIdentityDefaultMessage extends OutgoingTextMessage {
public OutgoingIdentityDefaultMessage(Recipients recipients) { public OutgoingIdentityDefaultMessage(Recipient recipient) {
this(recipients, ""); this(recipient, "");
} }
private OutgoingIdentityDefaultMessage(Recipients recipients, String body) { private OutgoingIdentityDefaultMessage(Recipient recipient, String body) {
super(recipients, body, -1); super(recipient, body, -1);
} }
@Override @Override
@ -19,6 +19,6 @@ public class OutgoingIdentityDefaultMessage extends OutgoingTextMessage {
} }
public OutgoingTextMessage withBody(String body) { public OutgoingTextMessage withBody(String body) {
return new OutgoingIdentityDefaultMessage(getRecipients()); return new OutgoingIdentityDefaultMessage(getRecipient());
} }
} }

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