From c56ef33833e939ae294d2011b8f912125038e118 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 5 Apr 2022 11:56:13 -0400 Subject: [PATCH] Fix resend after safety number change in groups or distribution lists. --- .../error/SafetyNumberChangeRepository.java | 19 +++++-- .../jobs/PushDistributionListSendJob.java | 51 ++++++++++++------ .../securesms/jobs/PushGroupSendJob.java | 53 ++++++++++--------- .../securesms/sms/MessageSender.java | 35 +++++++----- .../securesms/stories/my/MyStoriesFragment.kt | 9 +++- .../RecipientIdSerializationTest.java | 15 +++++- 6 files changed, 123 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java index 3094265f73..827e199bcb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java @@ -32,7 +32,9 @@ import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; final class SafetyNumberChangeRepository { @@ -151,8 +153,9 @@ final class SafetyNumberChangeRepository { @WorkerThread private void processOutgoingMessageRecord(@NonNull List changedRecipients, @NonNull MessageRecord messageRecord) { Log.d(TAG, "processOutgoingMessageRecord"); - MessageDatabase smsDatabase = SignalDatabase.sms(); - MessageDatabase mmsDatabase = SignalDatabase.mms(); + MessageDatabase smsDatabase = SignalDatabase.sms(); + MessageDatabase mmsDatabase = SignalDatabase.mms(); + Set resendIds = new HashSet<>(); for (ChangedRecipient changedRecipient : changedRecipients) { RecipientId id = changedRecipient.getRecipient().getId(); @@ -161,8 +164,8 @@ final class SafetyNumberChangeRepository { if (messageRecord.isMms()) { mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), id, identityKey); - if (messageRecord.getRecipient().isPushGroup()) { - MessageSender.resendGroupMessage(context, messageRecord, id); + if (messageRecord.getRecipient().isDistributionList() || messageRecord.getRecipient().isPushGroup()) { + resendIds.add(id); } else { MessageSender.resend(context, messageRecord); } @@ -172,6 +175,14 @@ final class SafetyNumberChangeRepository { MessageSender.resend(context, messageRecord); } } + + if (Util.hasItems(resendIds)) { + if (messageRecord.getRecipient().isPushGroup()) { + MessageSender.resendGroupMessage(context, messageRecord, resendIds); + } else { + MessageSender.resendDistributionList(context, messageRecord, resendIds); + } + } } static final class SafetyNumberChangeState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java index a6af08570c..4eca00129b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java @@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.stories.Stories; import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; @@ -37,10 +38,13 @@ import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * A job that lets us send a message to a distribution list. Currently the only supported message type is a story. @@ -51,31 +55,36 @@ public final class PushDistributionListSendJob extends PushSendJob { private static final String TAG = Log.tag(PushDistributionListSendJob.class); - private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_FILTERED_RECIPIENT_IDS = "filtered_recipient_ids"; - private final long messageId; + private final long messageId; + private final Set filterRecipientIds; - public PushDistributionListSendJob(long messageId, @NonNull RecipientId destination, boolean hasMedia) { + public PushDistributionListSendJob(long messageId, @NonNull RecipientId destination, boolean hasMedia, @NonNull Set filterRecipientIds) { this(new Parameters.Builder() .setQueue(destination.toQueueKey(hasMedia)) .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) .build(), - messageId); - + messageId, + filterRecipientIds + ); } - private PushDistributionListSendJob(@NonNull Parameters parameters, long messageId) { + private PushDistributionListSendJob(@NonNull Parameters parameters, long messageId, @NonNull Set filterRecipientIds) { super(parameters); - this.messageId = messageId; + this.messageId = messageId; + this.filterRecipientIds = filterRecipientIds; } @WorkerThread public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, - @NonNull RecipientId destination) + @NonNull RecipientId destination, + @NonNull Set filterRecipientIds) { try { Recipient listRecipient = Recipient.resolved(destination); @@ -92,7 +101,7 @@ public final class PushDistributionListSendJob extends PushSendJob { Set attachmentUploadIds = enqueueCompressingAndUploadAttachmentsChains(jobManager, message); - jobManager.add(new PushDistributionListSendJob(messageId, destination, !attachmentUploadIds.isEmpty()), attachmentUploadIds, attachmentUploadIds.isEmpty() ? null : destination.toQueueKey()); + jobManager.add(new PushDistributionListSendJob(messageId, destination, !attachmentUploadIds.isEmpty(), filterRecipientIds), attachmentUploadIds, attachmentUploadIds.isEmpty() ? null : destination.toQueueKey()); } catch (NoSuchMessageException | MmsException e) { Log.w(TAG, "Failed to enqueue message.", e); SignalDatabase.mms().markAsSentFailed(messageId); @@ -102,7 +111,9 @@ public final class PushDistributionListSendJob extends PushSendJob { @Override public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId).build(); + return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) + .putString(KEY_FILTERED_RECIPIENT_IDS, RecipientId.toSerializedList(filterRecipientIds)) + .build(); } @Override @@ -142,15 +153,22 @@ public final class PushDistributionListSendJob extends PushSendJob { try { log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Attachments: " + buildAttachmentString(message.getAttachments())); - List target; + List targets; - if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); - else target = Stream.of(Stories.getRecipientsToSendTo(messageId, message.getSentTimeMillis(), message.getStoryType().isStoryWithReplies())).distinctBy(Recipient::getId).toList(); + if (Util.hasItems(filterRecipientIds)) { + targets = new ArrayList<>(filterRecipientIds.size() + existingNetworkFailures.size()); + targets.addAll(filterRecipientIds.stream().map(Recipient::resolved).collect(Collectors.toList())); + targets.addAll(existingNetworkFailures.stream().map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).collect(Collectors.toList())); + } else if (!existingNetworkFailures.isEmpty()) { + targets = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); + } else { + targets = Stream.of(Stories.getRecipientsToSendTo(messageId, message.getSentTimeMillis(), message.getStoryType().isStoryWithReplies())).distinctBy(Recipient::getId).toList(); + } - List results = deliver(message, target); + List results = deliver(message, targets); Log.i(TAG, JobLogger.format(this, "Finished send.")); - PushGroupSendJob.processGroupMessageResults(context, messageId, -1, null, message, results, target, Collections.emptyList(), existingNetworkFailures, existingIdentityMismatches); + PushGroupSendJob.processGroupMessageResults(context, messageId, -1, null, message, results, targets, Collections.emptyList(), existingNetworkFailures, existingIdentityMismatches); } catch (UntrustedIdentityException | UndeliverableMessageException e) { warn(TAG, String.valueOf(message.getSentTimeMillis()), e); database.markAsSentFailed(messageId); @@ -191,7 +209,8 @@ public final class PushDistributionListSendJob extends PushSendJob { public static class Factory implements Job.Factory { @Override public @NonNull PushDistributionListSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new PushDistributionListSendJob(parameters, data.getLong(KEY_MESSAGE_ID)); + Set recipientIds = new HashSet<>(RecipientId.fromSerializedList(data.getStringOrDefault(KEY_FILTERED_RECIPIENT_IDS, ""))); + return new PushDistributionListSendJob(parameters, data.getLong(KEY_MESSAGE_ID), recipientIds); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 45aac6d9a8..5c19d9be01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.RecipientAccessList; import org.signal.core.util.SetUtil; import org.thoughtcrime.securesms.util.SignalLocalMetrics; +import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SendMessageResult; @@ -63,6 +64,8 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupC import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -75,28 +78,28 @@ public final class PushGroupSendJob extends PushSendJob { private static final String TAG = Log.tag(PushGroupSendJob.class); - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_FILTER_RECIPIENT = "filter_recipient"; + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_FILTER_RECIPIENTS = "filter_recipient"; - private final long messageId; - private final RecipientId filterRecipient; + private final long messageId; + private final Set filterRecipients; - public PushGroupSendJob(long messageId, @NonNull RecipientId destination, @Nullable RecipientId filterRecipient, boolean hasMedia) { + public PushGroupSendJob(long messageId, @NonNull RecipientId destination, @NonNull Set filterRecipients, boolean hasMedia) { this(new Job.Parameters.Builder() .setQueue(destination.toQueueKey(hasMedia)) .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) .build(), - messageId, filterRecipient); + messageId, filterRecipients); } - private PushGroupSendJob(@NonNull Job.Parameters parameters, long messageId, @Nullable RecipientId filterRecipient) { + private PushGroupSendJob(@NonNull Job.Parameters parameters, long messageId, @NonNull Set filterRecipients) { super(parameters); - this.messageId = messageId; - this.filterRecipient = filterRecipient; + this.messageId = messageId; + this.filterRecipients = filterRecipients; } @WorkerThread @@ -104,7 +107,7 @@ public final class PushGroupSendJob extends PushSendJob { @NonNull JobManager jobManager, long messageId, @NonNull RecipientId destination, - @Nullable RecipientId filterAddress) + @NonNull Set filterAddresses) { try { Recipient group = Recipient.resolved(destination); @@ -120,7 +123,7 @@ public final class PushGroupSendJob extends PushSendJob { throw new MmsException("Inactive group!"); } - jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress, !attachmentUploadIds.isEmpty()), attachmentUploadIds, attachmentUploadIds.isEmpty() ? null : destination.toQueueKey()); + jobManager.add(new PushGroupSendJob(messageId, destination, filterAddresses, !attachmentUploadIds.isEmpty()), attachmentUploadIds, attachmentUploadIds.isEmpty() ? null : destination.toQueueKey()); } catch (NoSuchMessageException | MmsException e) { Log.w(TAG, "Failed to enqueue message.", e); @@ -132,7 +135,7 @@ public final class PushGroupSendJob extends PushSendJob { @Override public @NonNull Data serialize() { return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_FILTER_RECIPIENT, filterRecipient != null ? filterRecipient.serialize() : null) + .putString(KEY_FILTER_RECIPIENTS, RecipientId.toSerializedList(filterRecipients)) .build(); } @@ -189,8 +192,10 @@ public final class PushGroupSendJob extends PushSendJob { List target; List skipped = new ArrayList<>(); - if (filterRecipient != null) { - target = Collections.singletonList(Recipient.resolved(filterRecipient)); + if (Util.hasItems(filterRecipients)) { + target = new ArrayList<>(filterRecipients.size() + existingNetworkFailures.size()); + target.addAll(Stream.of(filterRecipients).map(Recipient::resolved).toList()); + target.addAll(Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList()); } else if (!existingNetworkFailures.isEmpty()) { target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); } else { @@ -230,9 +235,9 @@ public final class PushGroupSendJob extends PushSendJob { try { rotateSenderCertificateIfNecessary(); - GroupId.Push groupId = groupRecipient.requireGroupId().requirePush(); - Optional profileKey = getProfileKey(groupRecipient); - Optional sticker = getStickerFor(message); + GroupId.Push groupId = groupRecipient.requireGroupId().requirePush(); + Optional profileKey = getProfileKey(groupRecipient); + Optional sticker = getStickerFor(message); List sharedContacts = getSharedContactsFor(message); List previews = getPreviewsFor(message); List mentions = getMentionsFor(message.getMentions()); @@ -407,7 +412,7 @@ public final class PushGroupSendJob extends PushSendJob { handleProofRequiredException(context, proofRequired, groupRecipient, threadId, messageId, true); } - if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) { + if (existingNetworkFailures.isEmpty() && existingIdentityMismatches.isEmpty()) { database.markAsSent(messageId, true); markAttachmentsUploaded(messageId, message); @@ -429,12 +434,12 @@ public final class PushGroupSendJob extends PushSendJob { if (message.getStoryType().isStory()) { ApplicationDependencies.getExpireStoriesManager().scheduleIfNecessary(); } - } else if (!identityMismatches.isEmpty()) { - Log.w(TAG, "Failing because there were " + identityMismatches.size() + " identity mismatches."); + } else if (!existingIdentityMismatches.isEmpty()) { + Log.w(TAG, "Failing because there were " + existingIdentityMismatches.size() + " identity mismatches."); database.markAsSentFailed(messageId); notifyMediaMessageDeliveryFailed(context, messageId); - Set mismatchRecipientIds = Stream.of(identityMismatches) + Set mismatchRecipientIds = Stream.of(existingIdentityMismatches) .map(mismatch -> mismatch.getRecipientId(context)) .collect(Collectors.toSet()); @@ -489,10 +494,10 @@ public final class PushGroupSendJob extends PushSendJob { public static class Factory implements Job.Factory { @Override public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - String raw = data.getString(KEY_FILTER_RECIPIENT); - RecipientId filter = raw != null ? RecipientId.from(raw) : null; + String raw = data.getStringOrDefault(KEY_FILTER_RECIPIENTS, ""); + Set filters = raw != null ? new HashSet<>(RecipientId.fromSerializedList(raw)) : Collections.emptySet(); - return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter); + return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filters); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java index a330be2b23..d73bd5df2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java @@ -44,8 +44,10 @@ import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.database.model.StoryType; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; @@ -86,6 +88,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; public class MessageSender { @@ -327,9 +330,9 @@ public class MessageSender { if (isLocalSelfSend(context, recipient, false)) { sendLocalMediaSelf(context, messageId); } else if (recipient.isPushGroup()) { - jobManager.add(new PushGroupSendJob(messageId, recipient.getId(), null, true), messageDependsOnIds, recipient.getId().toQueueKey()); + jobManager.add(new PushGroupSendJob(messageId, recipient.getId(), Collections.emptySet(), true), messageDependsOnIds, recipient.getId().toQueueKey()); } else if (recipient.isDistributionList()) { - jobManager.add(new PushDistributionListSendJob(messageId, recipient.getId(), true), messageDependsOnIds, recipient.getId().toQueueKey()); + jobManager.add(new PushDistributionListSendJob(messageId, recipient.getId(), true, Collections.emptySet()), messageDependsOnIds, recipient.getId().toQueueKey()); } else { jobManager.add(new PushMediaSendJob(messageId, recipient, true), messageDependsOnIds, recipient.getId().toQueueKey()); } @@ -403,9 +406,17 @@ public class MessageSender { } } - public static void resendGroupMessage(Context context, MessageRecord messageRecord, RecipientId filterRecipientId) { + public static void resendGroupMessage(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull Set filterRecipientIds) { if (!messageRecord.isMms()) throw new AssertionError("Not Group"); - sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterRecipientId, Collections.emptyList()); + sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterRecipientIds, Collections.emptyList()); + onMessageSent(); + } + + public static void resendDistributionList(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull Set filterRecipientIds) { + if (!messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getStoryType().isStory()) { + throw new AssertionError("Not a story"); + } + sendDistributionList(context, messageRecord.getRecipient(), messageRecord.getId(), filterRecipientIds, Collections.emptyList()); onMessageSent(); } @@ -447,9 +458,9 @@ public class MessageSender { if (isLocalSelfSend(context, recipient, forceSms)) { sendLocalMediaSelf(context, messageId); } else if (recipient.isPushGroup()) { - sendGroupPush(context, recipient, messageId, null, uploadJobIds); + sendGroupPush(context, recipient, messageId, Collections.emptySet(), uploadJobIds); } else if (recipient.isDistributionList()) { - sendDistributionList(context, recipient, messageId, uploadJobIds); + sendDistributionList(context, recipient, messageId, Collections.emptySet(), uploadJobIds); } else if (!forceSms && isPushMediaSend(context, recipient)) { sendMediaPush(context, recipient, messageId, uploadJobIds); } else { @@ -485,25 +496,25 @@ public class MessageSender { } } - private static void sendGroupPush(Context context, Recipient recipient, long messageId, RecipientId filterRecipientId, @NonNull Collection uploadJobIds) { + private static void sendGroupPush(@NonNull Context context, @NonNull Recipient recipient, long messageId, @NonNull Set filterRecipientIds, @NonNull Collection uploadJobIds) { JobManager jobManager = ApplicationDependencies.getJobManager(); if (uploadJobIds.size() > 0) { - Job groupSend = new PushGroupSendJob(messageId, recipient.getId(), filterRecipientId, !uploadJobIds.isEmpty()); + Job groupSend = new PushGroupSendJob(messageId, recipient.getId(), filterRecipientIds, !uploadJobIds.isEmpty()); jobManager.add(groupSend, uploadJobIds, uploadJobIds.isEmpty() ? null : recipient.getId().toQueueKey()); } else { - PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getId(), filterRecipientId); + PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getId(), filterRecipientIds); } } - private static void sendDistributionList(Context context, Recipient recipient, long messageId, @NonNull Collection uploadJobIds) { + private static void sendDistributionList(@NonNull Context context, @NonNull Recipient recipient, long messageId, @NonNull Set filterRecipientIds, @NonNull Collection uploadJobIds) { JobManager jobManager = ApplicationDependencies.getJobManager(); if (uploadJobIds.size() > 0) { - Job groupSend = new PushDistributionListSendJob(messageId, recipient.getId(), !uploadJobIds.isEmpty()); + Job groupSend = new PushDistributionListSendJob(messageId, recipient.getId(), !uploadJobIds.isEmpty(), filterRecipientIds); jobManager.add(groupSend, uploadJobIds, uploadJobIds.isEmpty() ? null : recipient.getId().toQueueKey()); } else { - PushDistributionListSendJob.enqueue(context, jobManager, messageId, recipient.getId()); + PushDistributionListSendJob.enqueue(context, jobManager, messageId, recipient.getId(), filterRecipientIds); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt index c8519f05e1..d81455876d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs +import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu @@ -73,8 +74,12 @@ class MyStoriesFragment : DSLSettingsFragment( distributionStory = conversationMessage, onClick = { it, preview -> if (it.distributionStory.messageRecord.isOutgoing && it.distributionStory.messageRecord.isFailed) { - lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe() - Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show() + if (it.distributionStory.messageRecord.isIdentityMismatchFailure) { + SafetyNumberChangeDialog.show(requireContext(), childFragmentManager, it.distributionStory.messageRecord) + } else { + lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe() + Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show() + } } else { val recipientId = if (it.distributionStory.messageRecord.recipient.isGroup) { it.distributionStory.messageRecord.recipient.id diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientIdSerializationTest.java b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientIdSerializationTest.java index 1da168d384..b4b1f41576 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientIdSerializationTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientIdSerializationTest.java @@ -1,11 +1,17 @@ package org.thoughtcrime.securesms.recipients; +import org.hamcrest.Matcher; +import org.hamcrest.MatcherAssert; import org.junit.Test; +import java.util.List; + import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -43,6 +49,13 @@ public final class RecipientIdSerializationTest { assertThat(RecipientId.fromSerializedList("123,456"), is(asList(RecipientId.from(123), RecipientId.from(456)))); } + @Test + public void fromSerializedList_recipient_serialize() { + List recipientIds = RecipientId.fromSerializedList(RecipientId.from(123).serialize()); + assertThat(recipientIds, hasSize(1)); + assertThat(recipientIds, contains(RecipientId.from(123))); + } + @Test public void serializedListContains_empty_list_does_not_contain_item() { assertFalse(RecipientId.serializedListContains("", RecipientId.from(456)));