From dda020b2bf250ba26ea113c0898494a4d578d173 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Tue, 9 Dec 2025 10:08:19 -0500 Subject: [PATCH] Allow pinned messages to be resendable. --- .../conversation/v2/ConversationRepository.kt | 18 +++++++------ .../securesms/conversation/v2/PinSendUtil.kt | 25 +++++++++++++------ .../securesms/database/MessageTable.kt | 7 ++++-- .../securesms/jobs/PollVoteJob.kt | 1 + .../securesms/jobs/PushGroupSendJob.java | 5 ++-- .../securesms/jobs/ReactionSendJob.java | 1 + .../securesms/jobs/RemoteDeleteSendJob.java | 1 + .../securesms/jobs/UnpinMessageJob.kt | 1 + .../securesms/messages/GroupSendUtil.java | 5 ++-- 9 files changed, 43 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt index cdc165f313..e87d0e4ec7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt @@ -238,7 +238,7 @@ class ConversationRepository( .distinctBy { it.id } val eligibleTargets: List = RecipientUtil.getEligibleForSending(possibleTargets) - val results = sendEndPoll(threadRecipient, message, eligibleTargets) + val results = sendEndPoll(threadRecipient, message, eligibleTargets, poll.messageId) val sendResults = GroupSendJobHelper.getCompletedSends(eligibleTargets, results) if (sendResults.completed.isNotEmpty() || possibleTargets.isEmpty()) { @@ -271,7 +271,7 @@ class ConversationRepository( } @Throws(IOException::class, GroupNotAMemberException::class, UndeliverableMessageException::class) - fun sendEndPoll(group: Recipient, message: OutgoingMessage, destinations: List): List { + fun sendEndPoll(group: Recipient, message: OutgoingMessage, destinations: List, messageId: Long): List { val groupId = group.requireGroupId().requireV2() val groupRecord: GroupRecord? = SignalDatabase.groups.getGroup(group.requireGroupId()).getOrNull() @@ -291,14 +291,18 @@ class ConversationRepository( .withPollTerminate(SignalServiceDataMessage.PollTerminate(message.messageExtras!!.pollTerminate!!.targetTimestamp)) .build() - return GroupSendUtil.sendUnresendableDataMessage( + return GroupSendUtil.sendResendableDataMessage( applicationContext, groupId, + null, destinations, false, - ContentHint.DEFAULT, + ContentHint.RESENDABLE, + MessageId(messageId), groupMessage, - false + true, + false, + null ) { System.currentTimeMillis() - sentTime > POLL_TERMINATE_TIMEOUT.inWholeMilliseconds } } @@ -336,7 +340,7 @@ class ConversationRepository( } val eligibleTargets = RecipientUtil.getEligibleForSending(possibleTargets) - val results = PinSendUtil.sendPinMessage(applicationContext, threadRecipient, message, eligibleTargets) + val results = PinSendUtil.sendPinMessage(applicationContext, threadRecipient, message, eligibleTargets, messageRecord.id) val sendResults = GroupSendJobHelper.getCompletedSends(eligibleTargets, results) @@ -393,7 +397,7 @@ class ConversationRepository( } val eligibleTargets: List = RecipientUtil.getEligibleForSending(possibleTargets) - val results = PinSendUtil.sendUnpinMessage(applicationContext, threadRecipient, message.fromRecipient.requireServiceId(), message.dateSent, eligibleTargets) + val results = PinSendUtil.sendUnpinMessage(applicationContext, threadRecipient, message.fromRecipient.requireServiceId(), message.dateSent, eligibleTargets, messageId) val sendResults = GroupSendJobHelper.getCompletedSends(eligibleTargets, results) if (sendResults.completed.isNotEmpty() || possibleTargets.isEmpty()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/PinSendUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/PinSendUtil.kt index 5de34f03c5..9de0ad18ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/PinSendUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/PinSendUtil.kt @@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.database.MessageTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.GroupRecord +import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.groups.GroupAccessControl import org.thoughtcrime.securesms.groups.GroupNotAMemberException @@ -30,7 +31,7 @@ object PinSendUtil { private val PIN_TERMINATE_TIMEOUT = 7000.milliseconds @Throws(IOException::class, GroupNotAMemberException::class, UndeliverableMessageException::class) - fun sendPinMessage(applicationContext: Context, threadRecipient: Recipient, message: OutgoingMessage, destinations: List): List { + fun sendPinMessage(applicationContext: Context, threadRecipient: Recipient, message: OutgoingMessage, destinations: List, relatedMessageId: Long): List { val builder = newBuilder() val groupId = if (threadRecipient.isPushV2Group) threadRecipient.requireGroupId().requireV2() else null @@ -57,19 +58,23 @@ object PinSendUtil { ) .build() - return GroupSendUtil.sendUnresendableDataMessage( + return GroupSendUtil.sendResendableDataMessage( applicationContext, groupId, + null, destinations, false, - ContentHint.DEFAULT, + ContentHint.RESENDABLE, + MessageId(relatedMessageId), message, - false + false, + false, + null ) { System.currentTimeMillis() - sentTime > PIN_TERMINATE_TIMEOUT.inWholeMilliseconds } } @Throws(IOException::class, GroupNotAMemberException::class, UndeliverableMessageException::class) - fun sendUnpinMessage(applicationContext: Context, threadRecipient: Recipient, targetAuthor: ServiceId, targetSentTimestamp: Long, destinations: List): List { + fun sendUnpinMessage(applicationContext: Context, threadRecipient: Recipient, targetAuthor: ServiceId, targetSentTimestamp: Long, destinations: List, relatedMessageId: Long): List { val builder = newBuilder() val groupId = if (threadRecipient.isPushV2Group) threadRecipient.requireGroupId().requireV2() else null if (groupId != null) { @@ -93,14 +98,18 @@ object PinSendUtil { ) .build() - return GroupSendUtil.sendUnresendableDataMessage( + return GroupSendUtil.sendResendableDataMessage( applicationContext, groupId, + null, destinations, false, - ContentHint.DEFAULT, + ContentHint.RESENDABLE, + MessageId(relatedMessageId), message, - false + false, + false, + null ) { System.currentTimeMillis() - sentTime > PIN_TERMINATE_TIMEOUT.inWholeMilliseconds } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index 27a0b82ac7..d0ff6855f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -2887,6 +2887,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val (messageId, insertedAttachments) = insertMediaMessage( threadId = threadId, + receivedTime = retrieved.receivedTimeMillis, body = retrieved.body, attachments = retrieved.attachments, quoteAttachments = quoteAttachments, @@ -3342,6 +3343,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val bodyRanges = message.bodyRanges.adjustBodyRanges(updatedBodyAndMentions.bodyAdjustments) val (messageId, insertedAttachments) = insertMediaMessage( threadId = threadId, + receivedTime = dateReceived, body = updatedBodyAndMentions.bodyAsString?.trim(), attachments = message.attachments, quoteAttachments = quoteAttachments, @@ -3453,6 +3455,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat @Throws(MmsException::class) private fun insertMediaMessage( threadId: Long, + receivedTime: Long, body: String?, attachments: List, quoteAttachments: List, @@ -3536,14 +3539,14 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val pinnedUntil = if (pinnedMessage.pinDurationInSeconds == PIN_FOREVER) { PIN_FOREVER } else { - System.currentTimeMillis() + pinnedMessage.pinDurationInSeconds.seconds.inWholeMilliseconds + receivedTime + pinnedMessage.pinDurationInSeconds.seconds.inWholeMilliseconds } val rows = db .update(TABLE_NAME) .values( PINNED_UNTIL to pinnedUntil, PINNING_MESSAGE_ID to messageId, - PINNED_AT to System.currentTimeMillis() + PINNED_AT to receivedTime ) .where("$ID = ?", pinnedMessage.pinnedMessageId) .run() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PollVoteJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/PollVoteJob.kt index 5815810d3d..b7e27762ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PollVoteJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PollVoteJob.kt @@ -155,6 +155,7 @@ class PollVoteJob( dataMessage, true, false, + null, null ) 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 e9b743d4a9..0dc626c98d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -342,7 +342,7 @@ public final class PushGroupSendJob extends PushSendJob { .asGroupMessage(group) .build(); return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId() - .requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId), groupDataMessage, message.isUrgent(), false, null); + .requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId), groupDataMessage, message.isUrgent(), false, null, null); } else { throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!"); } @@ -411,7 +411,8 @@ public final class PushGroupSendJob extends PushSendJob { groupMessage, message.isUrgent(), message.getStoryType().isStory() || message.getParentStoryId() != null, - editMessage); + editMessage, + null); } } catch (ServerRejectedException e) { throw new UndeliverableMessageException(e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java index 118877d6e0..3195b103b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java @@ -237,6 +237,7 @@ public class ReactionSendJob extends BaseJob { dataMessage, true, false, + null, null); if (includesSelf) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java index 0bb740ab15..9ece9bfd35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java @@ -227,6 +227,7 @@ public class RemoteDeleteSendJob extends BaseJob { dataMessage, true, isForStory, + null, null); if (conversationRecipient.isSelf()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/UnpinMessageJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/UnpinMessageJob.kt index cd2887b575..c2d4b5f1e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/UnpinMessageJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/UnpinMessageJob.kt @@ -159,6 +159,7 @@ class UnpinMessageJob( dataMessage, false, false, + null, null ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java index 0790614acb..03b00a719b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java @@ -106,14 +106,15 @@ public final class GroupSendUtil { @NonNull SignalServiceDataMessage message, boolean urgent, boolean isForStory, - @Nullable SignalServiceEditMessage editMessage) + @Nullable SignalServiceEditMessage editMessage, + @Nullable CancelationSignal cancelationSignal) throws IOException, UntrustedIdentityException { Preconditions.checkArgument(groupId == null || distributionListId == null, "Cannot supply both a groupId and a distributionListId!"); DistributionId distributionId = groupId != null ? getDistributionId(groupId) : getDistributionId(distributionListId); - return sendMessage(context, groupId, distributionId, messageId, allTargets, isRecipientUpdate, isForStory, DataSendOperation.resendable(message, contentHint, messageId, urgent, isForStory, editMessage), null); + return sendMessage(context, groupId, distributionId, messageId, allTargets, isRecipientUpdate, isForStory, DataSendOperation.resendable(message, contentHint, messageId, urgent, isForStory, editMessage), cancelationSignal); } /**