diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 45496a27d8..72c20837a8 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -559,7 +559,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity .setType(GroupContext.Type.QUIT) .build(); - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null); + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis()); MessageSender.send(self, masterSecret, outgoingMessage, threadId, false); DatabaseFactory.getGroupDatabase(self).remove(groupId, TextSecurePreferences.getLocalNumber(self)); initializeEnabledCheck(); diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index 5520718a31..bf2a981a27 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -476,7 +476,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity { Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar); Attachment avatarAttachment = new UriAttachment(avatarUri, ContentType.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length); - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, context, avatarAttachment); + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, context, avatarAttachment, System.currentTimeMillis()); long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1, false); return new Pair<>(threadId, groupRecipient); diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java index f36191ed11..19c241e071 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -3,35 +3,39 @@ package org.thoughtcrime.securesms.groups; import android.content.Context; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; import com.google.protobuf.ByteString; import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.recipients.RecipientFactory; +import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.textsecure.api.messages.TextSecureAttachment; +import org.whispersystems.textsecure.api.messages.TextSecureDataMessage; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; import org.whispersystems.textsecure.api.messages.TextSecureGroup; -import org.whispersystems.textsecure.api.messages.TextSecureDataMessage; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import ws.com.google.android.mms.MmsException; + import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.whispersystems.textsecure.internal.push.TextSecureProtos.AttachmentPointer; import static org.whispersystems.textsecure.internal.push.TextSecureProtos.GroupContext; @@ -43,7 +47,8 @@ public class GroupMessageProcessor { public static void process(@NonNull Context context, @NonNull MasterSecretUnion masterSecret, @NonNull TextSecureEnvelope envelope, - @NonNull TextSecureDataMessage message) + @NonNull TextSecureDataMessage message, + boolean outgoing) { if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) { Log.w(TAG, "Received group message with no id! Ignoring..."); @@ -56,11 +61,11 @@ public class GroupMessageProcessor { GroupRecord record = database.getGroup(id); if (record != null && group.getType() == TextSecureGroup.Type.UPDATE) { - handleGroupUpdate(context, masterSecret, envelope, group, record); + handleGroupUpdate(context, masterSecret, envelope, group, record, outgoing); } else if (record == null && group.getType() == TextSecureGroup.Type.UPDATE) { - handleGroupCreate(context, masterSecret, envelope, group); + handleGroupCreate(context, masterSecret, envelope, group, outgoing); } else if (record != null && group.getType() == TextSecureGroup.Type.QUIT) { - handleGroupLeave(context, masterSecret, envelope, group, record); + handleGroupLeave(context, masterSecret, envelope, group, record, outgoing); } else { Log.w(TAG, "Received unknown type, ignoring..."); } @@ -69,7 +74,8 @@ public class GroupMessageProcessor { private static void handleGroupCreate(@NonNull Context context, @NonNull MasterSecretUnion masterSecret, @NonNull TextSecureEnvelope envelope, - @NonNull TextSecureGroup group) + @NonNull TextSecureGroup group, + boolean outgoing) { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); byte[] id = group.getGroupId(); @@ -82,14 +88,15 @@ public class GroupMessageProcessor { avatar != null && avatar.isPointer() ? avatar.asPointer() : null, envelope.getRelay()); - storeMessage(context, masterSecret, envelope, group, builder.build()); + storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); } private static void handleGroupUpdate(@NonNull Context context, @NonNull MasterSecretUnion masterSecret, @NonNull TextSecureEnvelope envelope, @NonNull TextSecureGroup group, - @NonNull GroupRecord groupRecord) + @NonNull GroupRecord groupRecord, + boolean outgoing) { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); @@ -132,14 +139,15 @@ public class GroupMessageProcessor { if (!groupRecord.isActive()) database.setActive(id, true); - storeMessage(context, masterSecret, envelope, group, builder.build()); + storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); } private static void handleGroupLeave(@NonNull Context context, @NonNull MasterSecretUnion masterSecret, @NonNull TextSecureEnvelope envelope, @NonNull TextSecureGroup group, - @NonNull GroupRecord record) + @NonNull GroupRecord record, + boolean outgoing) { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); byte[] id = group.getGroupId(); @@ -150,8 +158,9 @@ public class GroupMessageProcessor { if (members.contains(envelope.getSource())) { database.remove(id, envelope.getSource()); + if (outgoing) database.setActive(id, false); - storeMessage(context, masterSecret, envelope, group, builder.build()); + storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); } } @@ -160,20 +169,35 @@ public class GroupMessageProcessor { @NonNull MasterSecretUnion masterSecret, @NonNull TextSecureEnvelope envelope, @NonNull TextSecureGroup group, - @NonNull GroupContext storage) + @NonNull GroupContext storage, + boolean outgoing) { if (group.getAvatar().isPresent()) { ApplicationContext.getInstance(context).getJobManager() .add(new AvatarDownloadJob(context, group.getGroupId())); } - EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); - String body = Base64.encodeBytes(storage.toByteArray()); - IncomingTextMessage incoming = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group)); - IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); + try { + if (outgoing) { + MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); + Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false); + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp()); + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); + long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false); - Pair messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage); - MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second); + mmsDatabase.markAsSent(messageId); + } else { + EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); + String body = Base64.encodeBytes(storage.toByteArray()); + IncomingTextMessage incoming = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group)); + IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); + + Pair messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage); + MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second); + } + } catch (MmsException e) { + Log.w(TAG, e); + } } private static GroupContext.Builder createGroupContext(TextSecureGroup group) { diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4597f92546..41ecfb1aa2 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -143,7 +143,7 @@ public class PushDecryptJob extends ContextJob { } else if (content.getSyncMessage().isPresent()) { TextSecureSyncMessage syncMessage = content.getSyncMessage().get(); - if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(masterSecret, syncMessage.getSent().get(), smsMessageId); + if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(masterSecret, envelope, syncMessage.getSent().get(), smsMessageId); else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(masterSecret, syncMessage.getRequest().get()); } @@ -206,7 +206,7 @@ public class PushDecryptJob extends ContextJob { @NonNull TextSecureDataMessage message, @NonNull Optional smsMessageId) { - GroupMessageProcessor.process(context, masterSecret, envelope, message); + GroupMessageProcessor.process(context, masterSecret, envelope, message, false); if (smsMessageId.isPresent()) { DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); @@ -214,11 +214,14 @@ public class PushDecryptJob extends ContextJob { } private void handleSynchronizeSentMessage(@NonNull MasterSecretUnion masterSecret, + @NonNull TextSecureEnvelope envelope, @NonNull SentTranscriptMessage message, @NonNull Optional smsMessageId) throws MmsException { - if (message.getMessage().getAttachments().isPresent()) { + if (message.getMessage().isGroupUpdate()) { + GroupMessageProcessor.process(context, masterSecret, envelope, message.getMessage(), true); + } else if (message.getMessage().getAttachments().isPresent()) { handleSynchronizeSentMediaMessage(masterSecret, message, smsMessageId); } else { handleSynchronizeSentTextMessage(masterSecret, message, smsMessageId); diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java b/src/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java index b221ad7446..74e5bc8e0d 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java @@ -31,7 +31,8 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { public OutgoingGroupMediaMessage(@NonNull Recipients recipients, @NonNull GroupContext group, - @Nullable final Attachment avatar) + @Nullable final Attachment avatar, + long sentTimeMillis) { super(recipients, Base64.encodeBytes(group.toByteArray()), new LinkedList() {{if (avatar != null) add(avatar);}},