Add text formatting send and receive support for conversations.

This commit is contained in:
Cody Henthorne
2023-01-25 10:31:36 -05:00
committed by Greyson Parrelli
parent aa2075c78f
commit cc490f4b73
73 changed files with 1664 additions and 516 deletions

View File

@@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.database.SentStorySyncManifest;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.StickerTable;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.DatabaseProtosUtil;
import org.thoughtcrime.securesms.database.model.DistributionListId;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -62,6 +63,7 @@ import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.database.model.StoryType;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor;
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost;
@@ -170,6 +172,7 @@ import org.whispersystems.signalservice.api.payments.Money;
import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage;
import java.io.IOException;
@@ -274,7 +277,7 @@ public final class MessageContentProcessor {
if (content.getDataMessage().isPresent()) {
GroupTable groupDatabase = SignalDatabase.groups();
SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent() || message.getMentions().isPresent();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent() || message.getMentions().isPresent() || message.getBodyRanges().isPresent();
Optional<GroupId> groupId = GroupUtil.idFromGroupContext(message.getGroupContext());
boolean isGv2Message = groupId.isPresent() && groupId.get().isV2();
@@ -1550,7 +1553,8 @@ public final class MessageContentProcessor {
content.getServerUuid(),
null,
false,
false);
false,
getBodyRangeList(message.getBodyRanges()));
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -1671,13 +1675,15 @@ public final class MessageContentProcessor {
} else if (SignalDatabase.storySends().canReply(senderRecipient.getId(), storyContext.getSentTimestamp())) {
MmsMessageRecord story = (MmsMessageRecord) database.getMessageRecord(storyMessageId.getId());
String displayText = "";
String displayText = "";
BodyRangeList bodyRanges = null;
if (story.getStoryType().isTextStory()) {
displayText = story.getBody();
bodyRanges = story.getMessageRanges();
}
parentStoryId = new ParentStoryId.DirectReply(storyMessageId.getId());
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, displayText, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL);
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, displayText, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL, bodyRanges);
expiresInMillis = TimeUnit.SECONDS.toMillis(message.getExpiresInSeconds());
} else {
warn(content.getTimestamp(), "Story has reactions disabled. Dropping reaction.");
@@ -1780,12 +1786,14 @@ public final class MessageContentProcessor {
} else if (groupStory || SignalDatabase.storySends().canReply(senderRecipient.getId(), storyContext.getSentTimestamp())) {
parentStoryId = new ParentStoryId.DirectReply(storyMessageId.getId());
String displayText = "";
String displayText = "";
BodyRangeList bodyRanges = null;
if (story.getStoryType().isTextStory()) {
displayText = story.getBody();
bodyRanges = story.getMessageRanges();
}
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, displayText, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL);
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, displayText, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL, bodyRanges);
expiresInMillis = TimeUnit.SECONDS.toMillis(message.getExpiresInSeconds());
} else {
warn(content.getTimestamp(), "Story has replies disabled. Dropping reply.");
@@ -1796,6 +1804,8 @@ public final class MessageContentProcessor {
return null;
}
BodyRangeList bodyRanges = message.getBodyRanges().map(DatabaseProtosUtil::toBodyRangeList).orElse(null);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(senderRecipient.getId(),
content.getTimestamp(),
content.getServerReceivedTimestamp(),
@@ -1819,7 +1829,8 @@ public final class MessageContentProcessor {
content.getServerUuid(),
null,
false,
false);
false,
bodyRanges);
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -1934,6 +1945,7 @@ public final class MessageContentProcessor {
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().orElse(""), false);
Optional<List<Mention>> mentions = getMentions(message.getMentions());
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
BodyRangeList messageRanges = getBodyRangeList(message.getBodyRanges());
handlePossibleExpirationUpdate(content, message, Optional.empty(), senderRecipient, threadRecipient, receivedTime);
@@ -1960,7 +1972,8 @@ public final class MessageContentProcessor {
content.getServerUuid(),
null,
false,
false);
false,
messageRanges);
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -2064,12 +2077,15 @@ public final class MessageContentProcessor {
} else if (groupStory || story.getStoryType().isStoryWithReplies()) {
parentStoryId = new ParentStoryId.DirectReply(storyMessageId.getId());
String quoteBody = "";
String quoteBody = "";
BodyRangeList bodyRanges = null;
if (story.getStoryType().isTextStory()) {
quoteBody = story.getBody();
quoteBody = story.getBody();
bodyRanges = story.getMessageRanges();
}
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, quoteBody, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL);
quoteModel = new QuoteModel(storyContext.getSentTimestamp(), storyAuthorRecipient, quoteBody, false, story.getSlideDeck().asAttachments(), Collections.emptyList(), QuoteModel.Type.NORMAL, bodyRanges);
expiresInMillis = TimeUnit.SECONDS.toMillis(message.getDataMessage().get().getExpiresInSeconds());
} else {
warn(envelopeTimestamp, "Story has replies disabled. Dropping reply.");
@@ -2094,7 +2110,8 @@ public final class MessageContentProcessor {
Collections.emptySet(),
Collections.emptySet(),
null,
true);
true,
null);
if (recipient.getExpiresInSeconds() != message.getDataMessage().get().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(message);
@@ -2213,7 +2230,8 @@ public final class MessageContentProcessor {
Collections.emptySet(),
Collections.emptySet(),
null,
true);
true,
null);
MessageTable messageTable = SignalDatabase.messages();
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
@@ -2310,7 +2328,8 @@ public final class MessageContentProcessor {
Collections.emptySet(),
Collections.emptySet(),
giftBadge.orElse(null),
true);
true,
null);
if (recipients.getExpiresInSeconds() != message.getDataMessage().get().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(message);
@@ -2488,9 +2507,10 @@ public final class MessageContentProcessor {
{
log(envelopeTimestamp, "Synchronize sent text message for " + message.getTimestamp());
Recipient recipient = getSyncMessageDestination(message);
String body = message.getDataMessage().get().getBody().orElse("");
long expiresInMillis = TimeUnit.SECONDS.toMillis(message.getDataMessage().get().getExpiresInSeconds());
Recipient recipient = getSyncMessageDestination(message);
String body = message.getDataMessage().get().getBody().orElse("");
long expiresInMillis = TimeUnit.SECONDS.toMillis(message.getDataMessage().get().getExpiresInSeconds());
BodyRangeList bodyRanges = message.getDataMessage().get().getBodyRanges().map(DatabaseProtosUtil::toBodyRangeList).orElse(null);
if (recipient.getExpiresInSeconds() != message.getDataMessage().get().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(message);
@@ -2513,14 +2533,15 @@ public final class MessageContentProcessor {
StoryType.NONE,
Collections.emptyList(),
Collections.emptyList(),
true);
true,
bodyRanges);
messageId = SignalDatabase.messages().insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null);
database = SignalDatabase.messages();
updateGroupReceiptStatus(message, messageId, recipient.requireGroupId());
} else {
OutgoingMessage outgoingTextMessage = OutgoingMessage.text(recipient, body, expiresInMillis, message.getTimestamp());
OutgoingMessage outgoingTextMessage = OutgoingMessage.text(recipient, body, expiresInMillis, message.getTimestamp(), bodyRanges);
messageId = SignalDatabase.messages().insertMessageOutbox(outgoingTextMessage, threadId, false, null);
database = SignalDatabase.messages();
@@ -3023,7 +3044,7 @@ public final class MessageContentProcessor {
String body = message.isPaymentNotification() ? message.getDisplayBody(context).toString() : message.getBody();
return Optional.of(new QuoteModel(quote.get().getId(), author, body, false, attachments, mentions, QuoteModel.Type.fromDataMessageType(quote.get().getType())));
return Optional.of(new QuoteModel(quote.get().getId(), author, body, false, attachments, mentions, QuoteModel.Type.fromDataMessageType(quote.get().getType()), message.getMessageRanges()));
} else if (message != null) {
warn("Found the target for the quote, but it's flagged as remotely deleted.");
}
@@ -3036,7 +3057,8 @@ public final class MessageContentProcessor {
true,
PointerAttachment.forPointers(quote.get().getAttachments()),
getMentions(quote.get().getMentions()),
QuoteModel.Type.fromDataMessageType(quote.get().getType())));
QuoteModel.Type.fromDataMessageType(quote.get().getType()),
DatabaseProtosUtil.toBodyRangeList(quote.get().getBodyRanges())));
}
private Optional<Attachment> getStickerAttachment(Optional<SignalServiceDataMessage.Sticker> sticker) {
@@ -3124,6 +3146,14 @@ public final class MessageContentProcessor {
return Optional.of(getMentions(signalServiceMentions.get()));
}
private @Nullable BodyRangeList getBodyRangeList(Optional<List<SignalServiceProtos.BodyRange>> bodyRanges) {
if (!bodyRanges.isPresent()) {
return null;
}
return DatabaseProtosUtil.toBodyRangeList(bodyRanges.get());
}
private @NonNull List<Mention> getMentions(@Nullable List<SignalServiceDataMessage.Mention> signalServiceMentions) {
if (signalServiceMentions == null || signalServiceMentions.isEmpty()) {
return Collections.emptyList();