Fix resend after safety number change in groups or distribution lists.

This commit is contained in:
Cody Henthorne 2022-04-05 11:56:13 -04:00
parent 2253e25ae1
commit c56ef33833
6 changed files with 123 additions and 59 deletions

View file

@ -32,7 +32,9 @@ import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
final class SafetyNumberChangeRepository { final class SafetyNumberChangeRepository {
@ -153,6 +155,7 @@ final class SafetyNumberChangeRepository {
Log.d(TAG, "processOutgoingMessageRecord"); Log.d(TAG, "processOutgoingMessageRecord");
MessageDatabase smsDatabase = SignalDatabase.sms(); MessageDatabase smsDatabase = SignalDatabase.sms();
MessageDatabase mmsDatabase = SignalDatabase.mms(); MessageDatabase mmsDatabase = SignalDatabase.mms();
Set<RecipientId> resendIds = new HashSet<>();
for (ChangedRecipient changedRecipient : changedRecipients) { for (ChangedRecipient changedRecipient : changedRecipients) {
RecipientId id = changedRecipient.getRecipient().getId(); RecipientId id = changedRecipient.getRecipient().getId();
@ -161,8 +164,8 @@ final class SafetyNumberChangeRepository {
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), id, identityKey); mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), id, identityKey);
if (messageRecord.getRecipient().isPushGroup()) { if (messageRecord.getRecipient().isDistributionList() || messageRecord.getRecipient().isPushGroup()) {
MessageSender.resendGroupMessage(context, messageRecord, id); resendIds.add(id);
} else { } else {
MessageSender.resend(context, messageRecord); MessageSender.resend(context, messageRecord);
} }
@ -172,6 +175,14 @@ final class SafetyNumberChangeRepository {
MessageSender.resend(context, messageRecord); 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 { static final class SafetyNumberChangeState {

View file

@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stories.Stories; import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; 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 org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; 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. * A job that lets us send a message to a distribution list. Currently the only supported message type is a story.
@ -52,30 +56,35 @@ public final class PushDistributionListSendJob extends PushSendJob {
private static final String TAG = Log.tag(PushDistributionListSendJob.class); 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<RecipientId> filterRecipientIds;
public PushDistributionListSendJob(long messageId, @NonNull RecipientId destination, boolean hasMedia) { public PushDistributionListSendJob(long messageId, @NonNull RecipientId destination, boolean hasMedia, @NonNull Set<RecipientId> filterRecipientIds) {
this(new Parameters.Builder() this(new Parameters.Builder()
.setQueue(destination.toQueueKey(hasMedia)) .setQueue(destination.toQueueKey(hasMedia))
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED) .setMaxAttempts(Parameters.UNLIMITED)
.build(), .build(),
messageId); messageId,
filterRecipientIds
);
} }
private PushDistributionListSendJob(@NonNull Parameters parameters, long messageId) { private PushDistributionListSendJob(@NonNull Parameters parameters, long messageId, @NonNull Set<RecipientId> filterRecipientIds) {
super(parameters); super(parameters);
this.messageId = messageId; this.messageId = messageId;
this.filterRecipientIds = filterRecipientIds;
} }
@WorkerThread @WorkerThread
public static void enqueue(@NonNull Context context, public static void enqueue(@NonNull Context context,
@NonNull JobManager jobManager, @NonNull JobManager jobManager,
long messageId, long messageId,
@NonNull RecipientId destination) @NonNull RecipientId destination,
@NonNull Set<RecipientId> filterRecipientIds)
{ {
try { try {
Recipient listRecipient = Recipient.resolved(destination); Recipient listRecipient = Recipient.resolved(destination);
@ -92,7 +101,7 @@ public final class PushDistributionListSendJob extends PushSendJob {
Set<String> attachmentUploadIds = enqueueCompressingAndUploadAttachmentsChains(jobManager, message); Set<String> 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) { } catch (NoSuchMessageException | MmsException e) {
Log.w(TAG, "Failed to enqueue message.", e); Log.w(TAG, "Failed to enqueue message.", e);
SignalDatabase.mms().markAsSentFailed(messageId); SignalDatabase.mms().markAsSentFailed(messageId);
@ -102,7 +111,9 @@ public final class PushDistributionListSendJob extends PushSendJob {
@Override @Override
public @NonNull Data serialize() { 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 @Override
@ -142,15 +153,22 @@ public final class PushDistributionListSendJob extends PushSendJob {
try { try {
log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Attachments: " + buildAttachmentString(message.getAttachments())); log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Attachments: " + buildAttachmentString(message.getAttachments()));
List<Recipient> target; List<Recipient> targets;
if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); if (Util.hasItems(filterRecipientIds)) {
else target = Stream.of(Stories.getRecipientsToSendTo(messageId, message.getSentTimeMillis(), message.getStoryType().isStoryWithReplies())).distinctBy(Recipient::getId).toList(); 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<SendMessageResult> results = deliver(message, target); List<SendMessageResult> results = deliver(message, targets);
Log.i(TAG, JobLogger.format(this, "Finished send.")); 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) { } catch (UntrustedIdentityException | UndeliverableMessageException e) {
warn(TAG, String.valueOf(message.getSentTimeMillis()), e); warn(TAG, String.valueOf(message.getSentTimeMillis()), e);
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
@ -191,7 +209,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
public static class Factory implements Job.Factory<PushDistributionListSendJob> { public static class Factory implements Job.Factory<PushDistributionListSendJob> {
@Override @Override
public @NonNull PushDistributionListSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull PushDistributionListSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new PushDistributionListSendJob(parameters, data.getLong(KEY_MESSAGE_ID)); Set<RecipientId> recipientIds = new HashSet<>(RecipientId.fromSerializedList(data.getStringOrDefault(KEY_FILTERED_RECIPIENT_IDS, "")));
return new PushDistributionListSendJob(parameters, data.getLong(KEY_MESSAGE_ID), recipientIds);
} }
} }
} }

View file

@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.RecipientAccessList; import org.thoughtcrime.securesms.util.RecipientAccessList;
import org.signal.core.util.SetUtil; import org.signal.core.util.SetUtil;
import org.thoughtcrime.securesms.util.SignalLocalMetrics; 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.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult; 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.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
@ -76,27 +79,27 @@ public final class PushGroupSendJob extends PushSendJob {
private static final String TAG = Log.tag(PushGroupSendJob.class); private static final String TAG = Log.tag(PushGroupSendJob.class);
private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_FILTER_RECIPIENT = "filter_recipient"; private static final String KEY_FILTER_RECIPIENTS = "filter_recipient";
private final long messageId; private final long messageId;
private final RecipientId filterRecipient; private final Set<RecipientId> filterRecipients;
public PushGroupSendJob(long messageId, @NonNull RecipientId destination, @Nullable RecipientId filterRecipient, boolean hasMedia) { public PushGroupSendJob(long messageId, @NonNull RecipientId destination, @NonNull Set<RecipientId> filterRecipients, boolean hasMedia) {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.setQueue(destination.toQueueKey(hasMedia)) .setQueue(destination.toQueueKey(hasMedia))
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED) .setMaxAttempts(Parameters.UNLIMITED)
.build(), .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<RecipientId> filterRecipients) {
super(parameters); super(parameters);
this.messageId = messageId; this.messageId = messageId;
this.filterRecipient = filterRecipient; this.filterRecipients = filterRecipients;
} }
@WorkerThread @WorkerThread
@ -104,7 +107,7 @@ public final class PushGroupSendJob extends PushSendJob {
@NonNull JobManager jobManager, @NonNull JobManager jobManager,
long messageId, long messageId,
@NonNull RecipientId destination, @NonNull RecipientId destination,
@Nullable RecipientId filterAddress) @NonNull Set<RecipientId> filterAddresses)
{ {
try { try {
Recipient group = Recipient.resolved(destination); Recipient group = Recipient.resolved(destination);
@ -120,7 +123,7 @@ public final class PushGroupSendJob extends PushSendJob {
throw new MmsException("Inactive group!"); 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) { } catch (NoSuchMessageException | MmsException e) {
Log.w(TAG, "Failed to enqueue message.", e); Log.w(TAG, "Failed to enqueue message.", e);
@ -132,7 +135,7 @@ public final class PushGroupSendJob extends PushSendJob {
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) 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(); .build();
} }
@ -189,8 +192,10 @@ public final class PushGroupSendJob extends PushSendJob {
List<Recipient> target; List<Recipient> target;
List<RecipientId> skipped = new ArrayList<>(); List<RecipientId> skipped = new ArrayList<>();
if (filterRecipient != null) { if (Util.hasItems(filterRecipients)) {
target = Collections.singletonList(Recipient.resolved(filterRecipient)); 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()) { } else if (!existingNetworkFailures.isEmpty()) {
target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList();
} else { } else {
@ -407,7 +412,7 @@ public final class PushGroupSendJob extends PushSendJob {
handleProofRequiredException(context, proofRequired, groupRecipient, threadId, messageId, true); 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); database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message); markAttachmentsUploaded(messageId, message);
@ -429,12 +434,12 @@ public final class PushGroupSendJob extends PushSendJob {
if (message.getStoryType().isStory()) { if (message.getStoryType().isStory()) {
ApplicationDependencies.getExpireStoriesManager().scheduleIfNecessary(); ApplicationDependencies.getExpireStoriesManager().scheduleIfNecessary();
} }
} else if (!identityMismatches.isEmpty()) { } else if (!existingIdentityMismatches.isEmpty()) {
Log.w(TAG, "Failing because there were " + identityMismatches.size() + " identity mismatches."); Log.w(TAG, "Failing because there were " + existingIdentityMismatches.size() + " identity mismatches.");
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId); notifyMediaMessageDeliveryFailed(context, messageId);
Set<RecipientId> mismatchRecipientIds = Stream.of(identityMismatches) Set<RecipientId> mismatchRecipientIds = Stream.of(existingIdentityMismatches)
.map(mismatch -> mismatch.getRecipientId(context)) .map(mismatch -> mismatch.getRecipientId(context))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -489,10 +494,10 @@ public final class PushGroupSendJob extends PushSendJob {
public static class Factory implements Job.Factory<PushGroupSendJob> { public static class Factory implements Job.Factory<PushGroupSendJob> {
@Override @Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {
String raw = data.getString(KEY_FILTER_RECIPIENT); String raw = data.getStringOrDefault(KEY_FILTER_RECIPIENTS, "");
RecipientId filter = raw != null ? RecipientId.from(raw) : null; Set<RecipientId> 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);
} }
} }
} }

View file

@ -44,8 +44,10 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageId;
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.ReactionRecord; import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StoryType;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
@ -86,6 +88,7 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class MessageSender { public class MessageSender {
@ -327,9 +330,9 @@ public class MessageSender {
if (isLocalSelfSend(context, recipient, false)) { if (isLocalSelfSend(context, recipient, false)) {
sendLocalMediaSelf(context, messageId); sendLocalMediaSelf(context, messageId);
} else if (recipient.isPushGroup()) { } 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()) { } 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 { } else {
jobManager.add(new PushMediaSendJob(messageId, recipient, true), messageDependsOnIds, recipient.getId().toQueueKey()); 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<RecipientId> filterRecipientIds) {
if (!messageRecord.isMms()) throw new AssertionError("Not Group"); 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<RecipientId> filterRecipientIds) {
if (!messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getStoryType().isStory()) {
throw new AssertionError("Not a story");
}
sendDistributionList(context, messageRecord.getRecipient(), messageRecord.getId(), filterRecipientIds, Collections.emptyList());
onMessageSent(); onMessageSent();
} }
@ -447,9 +458,9 @@ public class MessageSender {
if (isLocalSelfSend(context, recipient, forceSms)) { if (isLocalSelfSend(context, recipient, forceSms)) {
sendLocalMediaSelf(context, messageId); sendLocalMediaSelf(context, messageId);
} else if (recipient.isPushGroup()) { } else if (recipient.isPushGroup()) {
sendGroupPush(context, recipient, messageId, null, uploadJobIds); sendGroupPush(context, recipient, messageId, Collections.emptySet(), uploadJobIds);
} else if (recipient.isDistributionList()) { } else if (recipient.isDistributionList()) {
sendDistributionList(context, recipient, messageId, uploadJobIds); sendDistributionList(context, recipient, messageId, Collections.emptySet(), uploadJobIds);
} else if (!forceSms && isPushMediaSend(context, recipient)) { } else if (!forceSms && isPushMediaSend(context, recipient)) {
sendMediaPush(context, recipient, messageId, uploadJobIds); sendMediaPush(context, recipient, messageId, uploadJobIds);
} else { } else {
@ -485,25 +496,25 @@ public class MessageSender {
} }
} }
private static void sendGroupPush(Context context, Recipient recipient, long messageId, RecipientId filterRecipientId, @NonNull Collection<String> uploadJobIds) { private static void sendGroupPush(@NonNull Context context, @NonNull Recipient recipient, long messageId, @NonNull Set<RecipientId> filterRecipientIds, @NonNull Collection<String> uploadJobIds) {
JobManager jobManager = ApplicationDependencies.getJobManager(); JobManager jobManager = ApplicationDependencies.getJobManager();
if (uploadJobIds.size() > 0) { 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()); jobManager.add(groupSend, uploadJobIds, uploadJobIds.isEmpty() ? null : recipient.getId().toQueueKey());
} else { } 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<String> uploadJobIds) { private static void sendDistributionList(@NonNull Context context, @NonNull Recipient recipient, long messageId, @NonNull Set<RecipientId> filterRecipientIds, @NonNull Collection<String> uploadJobIds) {
JobManager jobManager = ApplicationDependencies.getJobManager(); JobManager jobManager = ApplicationDependencies.getJobManager();
if (uploadJobIds.size() > 0) { 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()); jobManager.add(groupSend, uploadJobIds, uploadJobIds.isEmpty() ? null : recipient.getId().toQueueKey());
} else { } else {
PushDistributionListSendJob.enqueue(context, jobManager, messageId, recipient.getId()); PushDistributionListSendJob.enqueue(context, jobManager, messageId, recipient.getId(), filterRecipientIds);
} }
} }

View file

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs 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.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
@ -73,8 +74,12 @@ class MyStoriesFragment : DSLSettingsFragment(
distributionStory = conversationMessage, distributionStory = conversationMessage,
onClick = { it, preview -> onClick = { it, preview ->
if (it.distributionStory.messageRecord.isOutgoing && it.distributionStory.messageRecord.isFailed) { if (it.distributionStory.messageRecord.isOutgoing && it.distributionStory.messageRecord.isFailed) {
if (it.distributionStory.messageRecord.isIdentityMismatchFailure) {
SafetyNumberChangeDialog.show(requireContext(), childFragmentManager, it.distributionStory.messageRecord)
} else {
lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe() lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe()
Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show()
}
} else { } else {
val recipientId = if (it.distributionStory.messageRecord.recipient.isGroup) { val recipientId = if (it.distributionStory.messageRecord.recipient.isGroup) {
it.distributionStory.messageRecord.recipient.id it.distributionStory.messageRecord.recipient.id

View file

@ -1,11 +1,17 @@
package org.thoughtcrime.securesms.recipients; package org.thoughtcrime.securesms.recipients;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import org.junit.Test; import org.junit.Test;
import java.util.List;
import static org.hamcrest.CoreMatchers.is; 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; 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)))); assertThat(RecipientId.fromSerializedList("123,456"), is(asList(RecipientId.from(123), RecipientId.from(456))));
} }
@Test
public void fromSerializedList_recipient_serialize() {
List<RecipientId> recipientIds = RecipientId.fromSerializedList(RecipientId.from(123).serialize());
assertThat(recipientIds, hasSize(1));
assertThat(recipientIds, contains(RecipientId.from(123)));
}
@Test @Test
public void serializedListContains_empty_list_does_not_contain_item() { public void serializedListContains_empty_list_does_not_contain_item() {
assertFalse(RecipientId.serializedListContains("", RecipientId.from(456))); assertFalse(RecipientId.serializedListContains("", RecipientId.from(456)));