Generate thumbnails for quote attachments.

This commit is contained in:
Greyson Parrelli
2025-08-26 12:54:16 -04:00
parent 71dd1d9d8b
commit d4c1c39179
22 changed files with 276 additions and 148 deletions

View File

@@ -443,7 +443,7 @@ object DataMessageProcessor {
}
parentStoryId = DirectReply(storyId)
quoteModel = QuoteModel(sentTimestamp, authorRecipientId, displayText, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
quoteModel = QuoteModel(sentTimestamp, authorRecipientId, displayText, false, story.slideDeck.asAttachments().firstOrNull(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
expiresIn = message.expireTimerDuration
} else {
warn(envelope.timestamp!!, "Story has reactions disabled. Dropping reaction.")
@@ -769,7 +769,7 @@ object DataMessageProcessor {
bodyRanges = story.messageRanges
}
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipientId, displayText, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipientId, displayText, false, story.slideDeck.asAttachments().firstOrNull(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
expiresInMillis = message.expireTimerDuration
} else {
warn(envelope.timestamp!!, "Story has replies disabled. Dropping reply.")
@@ -898,7 +898,7 @@ object DataMessageProcessor {
SignalDatabase.messages.beginTransaction()
try {
val quote: QuoteModel? = getValidatedQuote(context, envelope.timestamp!!, message, senderRecipient, threadRecipient)
val quoteModel: QuoteModel? = getValidatedQuote(context, envelope.timestamp!!, message, senderRecipient, threadRecipient)
val contacts: List<Contact> = getContacts(message)
val linkPreviews: List<LinkPreview> = getLinkPreviews(message.preview, message.body ?: "", false)
val mentions: List<Mention> = getMentions(message.bodyRanges.take(BODY_RANGE_PROCESSING_LIMIT))
@@ -920,7 +920,7 @@ object DataMessageProcessor {
body = message.body?.ifEmpty { null },
groupId = groupId,
attachments = attachments + if (sticker != null) listOf(sticker) else emptyList(),
quote = quote,
quote = quoteModel,
sharedContacts = contacts,
linkPreviews = linkPreviews,
mentions = mentions,
@@ -1092,24 +1092,31 @@ object DataMessageProcessor {
if (quotedMessage != null && isSenderValid(quotedMessage, timestamp, senderRecipient, threadRecipient) && !quotedMessage.isRemoteDelete) {
log(timestamp, "Found matching message record...")
val attachments: MutableList<Attachment> = mutableListOf()
val mentions: MutableList<Mention> = mutableListOf()
var thumbnailAttachment: Attachment? = null
val targetMessageAttachments = SignalDatabase.attachments.getAttachmentsForMessage(quotedMessage.id)
val mentions: List<Mention> = SignalDatabase.mentions.getMentionsForMessage(quotedMessage.id)
quotedMessage = quotedMessage.withAttachments(SignalDatabase.attachments.getAttachmentsForMessage(quotedMessage.id))
mentions.addAll(SignalDatabase.mentions.getMentionsForMessage(quotedMessage.id))
// We want our thumbnail attachment to be the first "thumbnailable" item from the target message.
// That means we want to pick the earliest image/video that has data.
thumbnailAttachment = targetMessageAttachments
.sortedBy { it.displayOrder }
.sortedBy {
if (MediaUtil.isImageType(it.contentType) || MediaUtil.isVideoType(it.contentType)) {
0
} else {
1
}
}
.firstOrNull { it.hasData }
if (quotedMessage.isViewOnce) {
attachments.add(TombstoneAttachment(MediaUtil.VIEW_ONCE, true))
} else {
attachments += quotedMessage.slideDeck.asAttachments()
if (attachments.isEmpty()) {
attachments += quotedMessage
.linkPreviews
.filter { it.thumbnail.isPresent }
.map { it.thumbnail.get() }
}
thumbnailAttachment = TombstoneAttachment(MediaUtil.VIEW_ONCE, true)
} else if (thumbnailAttachment == null) {
thumbnailAttachment = quotedMessage
.linkPreviews
.filter { it.thumbnail.isPresent }
.map { it.thumbnail.get() }
.firstOrNull()
}
if (quotedMessage.isPaymentNotification) {
@@ -1119,14 +1126,14 @@ object DataMessageProcessor {
val body = if (quotedMessage.isPaymentNotification) quotedMessage.getDisplayBody(context).toString() else quotedMessage.body
return QuoteModel(
quote.id!!,
authorId,
body,
false,
attachments,
mentions,
QuoteModel.Type.fromProto(quote.type),
quotedMessage.messageRanges
id = quote.id!!,
author = authorId,
text = body,
isOriginalMissing = false,
attachment = thumbnailAttachment,
mentions = mentions,
type = QuoteModel.Type.fromProto(quote.type),
bodyRanges = quotedMessage.messageRanges
)
} else if (quotedMessage != null && quotedMessage.isRemoteDelete) {
warn(timestamp, "Found the target for the quote, but it's flagged as remotely deleted.")
@@ -1134,14 +1141,14 @@ object DataMessageProcessor {
warn(timestamp, "Didn't find matching message record...")
return QuoteModel(
quote.id!!,
authorId,
quote.text ?: "",
true,
quote.attachments.mapNotNull { PointerAttachment.forPointer(it).orNull() },
getMentions(quote.bodyRanges),
QuoteModel.Type.fromProto(quote.type),
quote.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
id = quote.id!!,
author = authorId,
text = quote.text ?: "",
isOriginalMissing = true,
attachment = quote.attachments.firstNotNullOfOrNull { PointerAttachment.forPointer(it).orNull() },
mentions = getMentions(quote.bodyRanges),
type = QuoteModel.Type.fromProto(quote.type),
bodyRanges = quote.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
)
}

View File

@@ -128,7 +128,7 @@ object EditMessageProcessor {
targetQuote.author,
targetQuote.displayText.toString(),
targetQuote.isOriginalMissing,
emptyList(),
null,
null,
targetQuote.quoteType,
null

View File

@@ -372,7 +372,7 @@ object SyncMessageProcessor {
messageToEdit = targetMessage.id
)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
updateGroupReceiptStatus(sent, messageId, toRecipient.requireGroupId())
} else {
val outgoingTextMessage = OutgoingMessage(
@@ -386,7 +386,7 @@ object SyncMessageProcessor {
bodyRanges = bodyRanges,
messageToEdit = targetMessage.id
)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingTextMessage, threadId, false, null)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingTextMessage, threadId, false, null).messageId
SignalDatabase.messages.markUnidentified(messageId, sent.isUnidentified(toRecipient.serviceId.orNull()))
}
@@ -414,14 +414,14 @@ object SyncMessageProcessor {
val targetQuote = (targetMessage as? MmsMessageRecord)?.quote
val quote: QuoteModel? = if (targetQuote != null && message.quote != null) {
QuoteModel(
targetQuote.id,
targetQuote.author,
targetQuote.displayText.toString(),
targetQuote.isOriginalMissing,
emptyList(),
null,
targetQuote.quoteType,
null
id = targetQuote.id,
author = targetQuote.author,
text = targetQuote.displayText.toString(),
isOriginalMissing = targetQuote.isOriginalMissing,
attachment = null,
mentions = null,
type = targetQuote.quoteType,
bodyRanges = null
)
} else {
null
@@ -455,7 +455,7 @@ object SyncMessageProcessor {
messageToEdit = targetMessage.id
)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
if (toRecipient.isGroup) {
updateGroupReceiptStatus(sent, messageId, toRecipient.requireGroupId())
@@ -558,7 +558,7 @@ object SyncMessageProcessor {
)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNDELIVERED, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNDELIVERED, null).messageId
if (groupId != null) {
updateGroupReceiptStatus(sent, messageId, recipient.requireGroupId())
@@ -655,7 +655,7 @@ object SyncMessageProcessor {
threadId,
false,
null
)
).messageId
SignalDatabase.messages.markAsSent(messageId, true)
}
@@ -703,14 +703,14 @@ object SyncMessageProcessor {
if (sent.message?.expireTimerVersion == null) {
// TODO [expireVersion] After unsupported builds expire, we can remove this branch
SignalDatabase.recipients.setExpireMessagesWithoutIncrementingVersion(recipient.id, sent.message!!.expireTimerDuration.inWholeSeconds.toInt())
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(expirationUpdateMessage, threadId, false, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(expirationUpdateMessage, threadId, false, null).messageId
SignalDatabase.messages.markAsSent(messageId, true)
} else if (sent.message!!.expireTimerVersion!! >= recipient.expireTimerVersion) {
SignalDatabase.recipients.setExpireMessages(recipient.id, sent.message!!.expireTimerDuration.inWholeSeconds.toInt(), sent.message!!.expireTimerVersion!!)
if (sent.message!!.expireTimerDuration != recipient.expiresInSeconds.seconds) {
log(sent.timestamp!!, "Not inserted update message as timer value did not change")
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(expirationUpdateMessage, threadId, false, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(expirationUpdateMessage, threadId, false, null).messageId
SignalDatabase.messages.markAsSent(messageId, true)
}
} else {
@@ -762,7 +762,7 @@ object SyncMessageProcessor {
quoteBody = story.body
bodyBodyRanges = story.messageRanges
}
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipient, quoteBody, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyBodyRanges)
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipient, quoteBody, false, story.slideDeck.asAttachments().firstOrNull(), emptyList(), QuoteModel.Type.NORMAL, bodyBodyRanges)
expiresInMillis = dataMessage.expireTimerDuration.inWholeMilliseconds
} else {
warn(envelopeTimestamp, "Story has replies disabled. Dropping reply.")
@@ -787,7 +787,7 @@ object SyncMessageProcessor {
}
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
if (recipient.isGroup) {
updateGroupReceiptStatus(sent, messageId, recipient.requireGroupId())
@@ -821,7 +821,7 @@ object SyncMessageProcessor {
val recipient: Recipient = getSyncMessageDestination(sent)
val dataMessage: DataMessage = sent.message!!
val quote: QuoteModel? = DataMessageProcessor.getValidatedQuote(context, envelopeTimestamp, dataMessage, senderRecipient, threadRecipient)
val quoteModel: QuoteModel? = DataMessageProcessor.getValidatedQuote(context, envelopeTimestamp, dataMessage, senderRecipient, threadRecipient)
val sticker: Attachment? = DataMessageProcessor.getStickerAttachment(envelopeTimestamp, dataMessage)
val sharedContacts: List<Contact> = DataMessageProcessor.getContacts(dataMessage)
val previews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(dataMessage.preview, dataMessage.body ?: "", false)
@@ -838,7 +838,7 @@ object SyncMessageProcessor {
timestamp = sent.timestamp!!,
expiresIn = dataMessage.expireTimerDuration.inWholeMilliseconds,
viewOnce = viewOnce,
quote = quote,
quote = quoteModel,
contacts = sharedContacts,
previews = previews,
mentions = mentions,
@@ -852,7 +852,7 @@ object SyncMessageProcessor {
}
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
log(envelopeTimestamp, "Inserted sync message as messageId $messageId")
if (recipient.isGroup) {
@@ -913,11 +913,11 @@ object SyncMessageProcessor {
bodyRanges = bodyRanges
)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
updateGroupReceiptStatus(sent, messageId, recipient.requireGroupId())
} else {
val outgoingTextMessage = OutgoingMessage.text(threadRecipient = recipient, body = body, expiresIn = expiresInMillis, sentTimeMillis = sent.timestamp!!, bodyRanges = bodyRanges)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingTextMessage, threadId, false, null)
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingTextMessage, threadId, false, null).messageId
SignalDatabase.messages.markUnidentified(messageId, sent.isUnidentified(recipient.serviceId.orNull()))
}