mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 02:39:55 +01:00
Generate thumbnails for quote attachments.
This commit is contained in:
@@ -309,7 +309,7 @@ public class InputPanel extends ConstraintLayout
|
||||
quoteView.getAuthor().getId(),
|
||||
quoteView.getBody().toString(),
|
||||
false,
|
||||
quoteView.getAttachments(),
|
||||
quoteView.getAttachment(),
|
||||
quoteView.getMentions(),
|
||||
quoteView.getQuoteType(),
|
||||
quoteView.getBodyRanges()));
|
||||
|
||||
@@ -464,8 +464,13 @@ public class QuoteView extends ConstraintLayout implements RecipientForeverObser
|
||||
return body;
|
||||
}
|
||||
|
||||
public List<Attachment> getAttachments() {
|
||||
return attachments.asAttachments();
|
||||
public @Nullable Attachment getAttachment() {
|
||||
List<Attachment> converted = attachments.asAttachments();
|
||||
if (converted.size() > 0) {
|
||||
return converted.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull QuoteModel.Type getQuoteType() {
|
||||
|
||||
@@ -185,7 +185,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(
|
||||
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i"),
|
||||
threadId = targetThread
|
||||
)
|
||||
).messageId
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
} else {
|
||||
SignalDatabase.messages.insertMessageInbox(
|
||||
@@ -215,7 +215,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(
|
||||
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i", attachments = listOf(attachment)),
|
||||
threadId = targetThread
|
||||
)
|
||||
).messageId
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
SignalDatabase.attachments.getAttachmentsForMessage(id).forEach {
|
||||
SignalDatabase.attachments.debugMakeValidForArchive(it.attachmentId)
|
||||
@@ -249,7 +249,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
|
||||
splitThreadId,
|
||||
false,
|
||||
null
|
||||
)
|
||||
).messageId
|
||||
SignalDatabase.messages.markAsSent(messageId, true)
|
||||
|
||||
SignalDatabase.threads.update(splitThreadId, true)
|
||||
|
||||
@@ -95,12 +95,15 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob
|
||||
import org.thoughtcrime.securesms.jobs.GenerateAudioWaveFormJob
|
||||
import org.thoughtcrime.securesms.mms.DecryptableUri
|
||||
import org.thoughtcrime.securesms.mms.MediaStream
|
||||
import org.thoughtcrime.securesms.mms.MmsException
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||
import org.thoughtcrime.securesms.mms.SentMediaQuality
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||
import org.thoughtcrime.securesms.util.FileUtils
|
||||
import org.thoughtcrime.securesms.util.ImageCompressionUtil
|
||||
import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
@@ -299,6 +302,9 @@ class AttachmentTable(
|
||||
ID, DATA_FILE, DATA_SIZE, DATA_RANDOM, DATA_HASH_START, DATA_HASH_END, TRANSFORM_PROPERTIES, UPLOAD_TIMESTAMP, ARCHIVE_CDN, ARCHIVE_TRANSFER_STATE, THUMBNAIL_FILE, THUMBNAIL_RESTORE_STATE, THUMBNAIL_RANDOM
|
||||
)
|
||||
|
||||
private const val QUOTE_THUMBNAIL_DIMEN = 150
|
||||
private const val QUOTE_IMAGE_QUALITY = 80
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun newDataFile(context: Context): File {
|
||||
@@ -1789,7 +1795,7 @@ class AttachmentTable(
|
||||
try {
|
||||
for (attachment in quoteAttachment) {
|
||||
val attachmentId = when {
|
||||
attachment.uri != null -> insertAttachmentWithData(mmsId, attachment, true)
|
||||
attachment.uri != null -> insertQuoteAttachment(mmsId, attachment)
|
||||
attachment is ArchivedAttachment -> insertArchivedAttachment(mmsId, attachment, true)
|
||||
else -> insertUndownloadedAttachment(mmsId, attachment, true)
|
||||
}
|
||||
@@ -2409,6 +2415,104 @@ class AttachmentTable(
|
||||
return attachmentId
|
||||
}
|
||||
|
||||
/**
|
||||
* When inserting a quote attachment, it looks a lot like a normal attachment insert, but rather than insert the actual data pointed at by the attachment's
|
||||
* URI, we instead want to generate a thumbnail of that attachment and use that instead.
|
||||
*/
|
||||
@Throws(MmsException::class)
|
||||
private fun insertQuoteAttachment(messageId: Long, attachment: Attachment): AttachmentId {
|
||||
Log.d(TAG, "[insertQuoteAttachment] Inserting quote attachment for messageId $messageId.")
|
||||
|
||||
val thumbnail = generateQuoteThumbnail(DecryptableUri(attachment.uri!!), attachment.contentType)
|
||||
if (thumbnail != null) {
|
||||
Log.d(TAG, "[insertQuoteAttachment] Successfully generated quote thumbnail for messageId $messageId.")
|
||||
|
||||
return insertAttachmentWithData(
|
||||
messageId = messageId,
|
||||
dataStream = thumbnail.data.inputStream(),
|
||||
attachment = attachment,
|
||||
quote = true
|
||||
)
|
||||
}
|
||||
|
||||
Log.d(TAG, "[insertQuoteAttachment] Unable to generate quote thumbnail for messageId $messageId. Content type: ${attachment.contentType}")
|
||||
val attachmentId: AttachmentId = writableDatabase.withinTransaction { db ->
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MESSAGE_ID, messageId)
|
||||
put(CONTENT_TYPE, attachment.contentType)
|
||||
put(VOICE_NOTE, attachment.voiceNote.toInt())
|
||||
put(BORDERLESS, attachment.borderless.toInt())
|
||||
put(VIDEO_GIF, attachment.videoGif.toInt())
|
||||
put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE)
|
||||
put(DATA_SIZE, 0)
|
||||
put(WIDTH, attachment.width)
|
||||
put(HEIGHT, attachment.height)
|
||||
put(QUOTE, 1)
|
||||
put(BLUR_HASH, attachment.blurHash?.hash)
|
||||
put(FILE_NAME, attachment.fileName)
|
||||
|
||||
attachment.stickerLocator?.let { sticker ->
|
||||
put(STICKER_PACK_ID, sticker.packId)
|
||||
put(STICKER_PACK_KEY, sticker.packKey)
|
||||
put(STICKER_ID, sticker.stickerId)
|
||||
put(STICKER_EMOJI, sticker.emoji)
|
||||
}
|
||||
}
|
||||
|
||||
val rowId = db.insert(TABLE_NAME, null, contentValues)
|
||||
AttachmentId(rowId)
|
||||
}
|
||||
|
||||
AppDependencies.databaseObserver.notifyAttachmentUpdatedObservers()
|
||||
return attachmentId
|
||||
}
|
||||
|
||||
private fun generateQuoteThumbnail(uri: DecryptableUri, contentType: String?): ImageCompressionUtil.Result? {
|
||||
return try {
|
||||
when {
|
||||
MediaUtil.isImageType(contentType) -> {
|
||||
val hasTransparency = MediaUtil.isPngType(contentType) || MediaUtil.isWebpType(contentType)
|
||||
val outputFormat = if (hasTransparency) MediaUtil.IMAGE_WEBP else MediaUtil.IMAGE_JPEG
|
||||
|
||||
ImageCompressionUtil.compress(
|
||||
context,
|
||||
contentType,
|
||||
outputFormat,
|
||||
uri,
|
||||
QUOTE_THUMBNAIL_DIMEN,
|
||||
QUOTE_IMAGE_QUALITY
|
||||
)
|
||||
}
|
||||
MediaUtil.isVideoType(contentType) -> {
|
||||
val videoThumbnail = MediaUtil.getVideoThumbnail(context, uri.uri)
|
||||
if (videoThumbnail != null) {
|
||||
ImageCompressionUtil.compress(
|
||||
context,
|
||||
MediaUtil.IMAGE_JPEG,
|
||||
MediaUtil.IMAGE_JPEG,
|
||||
uri,
|
||||
QUOTE_THUMBNAIL_DIMEN,
|
||||
QUOTE_IMAGE_QUALITY
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "[generateQuoteThumbnail] Failed to extract video thumbnail")
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Log.w(TAG, "[generateQuoteThumbnail] Unsupported content type for thumbnail generation: $contentType")
|
||||
null
|
||||
}
|
||||
}
|
||||
} catch (e: BitmapDecodingException) {
|
||||
Log.w(TAG, "[generateQuoteThumbnail] Failed to decode image for thumbnail", e)
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "[generateQuoteThumbnail] Failed to generate thumbnail", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachments need records in the database even if they haven't been downloaded yet. That allows us to store the info we need to download it, what message
|
||||
* it's associated with, etc. We treat this case separately from attachments with data (see [insertAttachmentWithData]) because it's much simpler,
|
||||
|
||||
@@ -2548,11 +2548,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
val quoteText = cursor.requireString(QUOTE_BODY)
|
||||
val quoteType = cursor.requireInt(QUOTE_TYPE)
|
||||
val quoteMissing = cursor.requireBoolean(QUOTE_MISSING)
|
||||
val quoteAttachments: List<Attachment> = associatedAttachments.filter { it.quote }.toList()
|
||||
val quoteAttachment: Attachment? = associatedAttachments.filter { it.quote }.firstOrNull()
|
||||
val quoteMentions: List<Mention> = parseQuoteMentions(cursor)
|
||||
val quoteBodyRanges: BodyRangeList? = parseQuoteBodyRanges(cursor)
|
||||
val quote: QuoteModel? = if (quoteId != QUOTE_NOT_PRESENT_ID && quoteAuthor > 0 && (!TextUtils.isEmpty(quoteText) || quoteAttachments.isNotEmpty())) {
|
||||
QuoteModel(quoteId, RecipientId.from(quoteAuthor), quoteText ?: "", quoteMissing, quoteAttachments, quoteMentions, QuoteModel.Type.fromCode(quoteType), quoteBodyRanges)
|
||||
val quote: QuoteModel? = if (quoteId != QUOTE_NOT_PRESENT_ID && quoteAuthor > 0 && (!TextUtils.isEmpty(quoteText) || quoteAttachment != null)) {
|
||||
QuoteModel(quoteId, RecipientId.from(quoteAuthor), quoteText ?: "", quoteMissing, quoteAttachment, quoteMentions, QuoteModel.Type.fromCode(quoteType), quoteBodyRanges)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -2776,7 +2776,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
contentValues.put(QUOTE_BODY_RANGES, quoteBodyRanges.build().encode())
|
||||
}
|
||||
|
||||
quoteAttachments += retrieved.quote.attachments
|
||||
retrieved.quote.attachment?.let { quoteAttachments += it }
|
||||
} else {
|
||||
contentValues.put(QUOTE_ID, 0)
|
||||
contentValues.put(QUOTE_AUTHOR, 0)
|
||||
@@ -2869,7 +2869,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
messageId = messageId,
|
||||
threadId = threadId,
|
||||
threadWasNewlyCreated = threadIdResult.newlyCreated,
|
||||
insertedAttachments = insertedAttachments
|
||||
insertedAttachments = insertedAttachments,
|
||||
quoteAttachmentId = quoteAttachments.firstOrNull()?.let { insertedAttachments?.get(it) }
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -2982,7 +2983,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
threadId: Long,
|
||||
forceSms: Boolean = false,
|
||||
insertListener: InsertListener? = null
|
||||
): Long {
|
||||
): InsertResult {
|
||||
return insertMessageOutbox(
|
||||
message = message,
|
||||
threadId = threadId,
|
||||
@@ -2999,7 +3000,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
forceSms: Boolean,
|
||||
defaultReceiptStatus: Int,
|
||||
insertListener: InsertListener?
|
||||
): Long {
|
||||
): InsertResult {
|
||||
var type = MessageTypes.BASE_SENDING_TYPE
|
||||
var hasSpecialType = false
|
||||
|
||||
@@ -3218,7 +3219,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
}
|
||||
|
||||
if (editedMessage == null) {
|
||||
quoteAttachments += message.outgoingQuote.attachments
|
||||
message.outgoingQuote.attachment?.let { quoteAttachments += it }
|
||||
}
|
||||
} else {
|
||||
contentValues.put(QUOTE_ID, 0)
|
||||
@@ -3320,7 +3321,13 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
|
||||
TrimThreadJob.enqueueAsync(threadId)
|
||||
|
||||
return messageId
|
||||
return InsertResult(
|
||||
messageId = messageId,
|
||||
threadId = threadId,
|
||||
threadWasNewlyCreated = false,
|
||||
insertedAttachments = insertedAttachments,
|
||||
quoteAttachmentId = quoteAttachments.firstOrNull()?.let { insertedAttachments?.get(it) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun hasAudioAttachment(attachments: List<Attachment>): Boolean {
|
||||
@@ -5255,7 +5262,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
timetamp = this.requireLong(DATE_SENT)
|
||||
),
|
||||
expirationInfo = null,
|
||||
storyType = StoryType.fromCode(this.requireInt(STORY_TYPE)),
|
||||
storyType = fromCode(this.requireInt(STORY_TYPE)),
|
||||
dateReceived = this.requireLong(DATE_RECEIVED)
|
||||
)
|
||||
}
|
||||
@@ -5406,7 +5413,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
val messageId: Long,
|
||||
val threadId: Long,
|
||||
val threadWasNewlyCreated: Boolean,
|
||||
val insertedAttachments: Map<Attachment, AttachmentId>? = null
|
||||
val insertedAttachments: Map<Attachment, AttachmentId>? = null,
|
||||
val quoteAttachmentId: AttachmentId? = null
|
||||
)
|
||||
|
||||
data class MessageReceiptUpdate(
|
||||
|
||||
@@ -1291,7 +1291,7 @@ final class GroupManagerV2 {
|
||||
} else {
|
||||
long threadId = SignalDatabase.threads().getOrCreateValidThreadId(outgoingMessage.getThreadRecipient(), -1, outgoingMessage.getDistributionType());
|
||||
try {
|
||||
long messageId = SignalDatabase.messages().insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
long messageId = SignalDatabase.messages().insertMessageOutbox(outgoingMessage, threadId, false, null).getMessageId();
|
||||
SignalDatabase.messages().markAsSent(messageId, true);
|
||||
SignalDatabase.threads().update(threadId, true, true);
|
||||
} catch (MmsException e) {
|
||||
|
||||
@@ -676,7 +676,7 @@ class GroupsV2StateProcessor private constructor(
|
||||
|
||||
try {
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(groupRecipient)
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(leaveMessage, threadId, false, null)
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(leaveMessage, threadId, false, null).messageId
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
SignalDatabase.drafts.clearDrafts(threadId)
|
||||
SignalDatabase.threads.update(threadId, unarchive = false, allowDeletion = false)
|
||||
@@ -733,7 +733,7 @@ class GroupsV2StateProcessor private constructor(
|
||||
val recipient = Recipient.resolved(recipientId)
|
||||
val outgoingMessage = OutgoingMessage.groupUpdateMessage(recipient, updateDescription, timestamp)
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
val messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, null)
|
||||
val messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, null).messageId
|
||||
|
||||
SignalDatabase.messages.markAsSent(messageId, true)
|
||||
SignalDatabase.threads.update(threadId, unarchive = false, allowDeletion = false)
|
||||
|
||||
@@ -77,6 +77,7 @@ import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulRespons
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||
import org.whispersystems.signalservice.internal.push.AttachmentPointer;
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -363,48 +364,19 @@ public abstract class PushSendJob extends SendJob {
|
||||
List<BodyRange> bodyRanges = getBodyRanges(message.getOutgoingQuote().getBodyRanges());
|
||||
QuoteModel.Type quoteType = message.getOutgoingQuote().getType();
|
||||
List<SignalServiceDataMessage.Quote.QuotedAttachment> quoteAttachments = new LinkedList<>();
|
||||
Optional<Attachment> localQuoteAttachment = message.getOutgoingQuote()
|
||||
.getAttachments()
|
||||
.stream()
|
||||
.filter(a -> !MediaUtil.isViewOnceType(a.contentType))
|
||||
.findFirst();
|
||||
Optional<Attachment> localQuoteAttachment = Optional.ofNullable(message.getOutgoingQuote()).map(QuoteModel::getAttachment);
|
||||
|
||||
if (localQuoteAttachment.isPresent() && MediaUtil.isViewOnceType(localQuoteAttachment.get().contentType)) {
|
||||
localQuoteAttachment = Optional.empty();
|
||||
}
|
||||
|
||||
if (localQuoteAttachment.isPresent()) {
|
||||
Attachment attachment = localQuoteAttachment.get();
|
||||
Attachment attachment = localQuoteAttachment.get();
|
||||
SignalServiceAttachment quoteAttachmentPointer = getAttachmentPointerFor(localQuoteAttachment.get());
|
||||
|
||||
ImageCompressionUtil.Result thumbnailData = null;
|
||||
SignalServiceAttachment thumbnail = null;
|
||||
|
||||
try {
|
||||
if (MediaUtil.isImageType(attachment.contentType) && attachment.getUri() != null) {
|
||||
thumbnailData = ImageCompressionUtil.compress(context, attachment.contentType, attachment.contentType, new DecryptableUri(attachment.getUri()), 100, 50);
|
||||
} else if (Build.VERSION.SDK_INT >= 23 && MediaUtil.isVideoType(attachment.contentType) && attachment.getUri() != null) {
|
||||
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000);
|
||||
|
||||
if (bitmap != null) {
|
||||
thumbnailData = ImageCompressionUtil.compress(context, attachment.contentType, attachment.contentType, new DecryptableUri(attachment.getUri()), 100, 50);
|
||||
}
|
||||
}
|
||||
|
||||
if (thumbnailData != null) {
|
||||
SignalServiceAttachment.Builder builder = SignalServiceAttachment.newStreamBuilder()
|
||||
.withContentType(thumbnailData.getMimeType())
|
||||
.withWidth(thumbnailData.getWidth())
|
||||
.withHeight(thumbnailData.getHeight())
|
||||
.withLength(thumbnailData.getData().length)
|
||||
.withStream(new ByteArrayInputStream(thumbnailData.getData()))
|
||||
.withResumableUploadSpec(AppDependencies.getSignalServiceMessageSender().getResumableUploadSpec())
|
||||
.withUuid(UUID.randomUUID());
|
||||
|
||||
thumbnail = builder.build();
|
||||
}
|
||||
|
||||
quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.videoGif ? MediaUtil.IMAGE_GIF : attachment.contentType,
|
||||
attachment.fileName,
|
||||
thumbnail));
|
||||
} catch (BitmapDecodingException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.videoGif ? MediaUtil.IMAGE_GIF : attachment.contentType,
|
||||
attachment.fileName,
|
||||
quoteAttachmentPointer));
|
||||
}
|
||||
|
||||
Recipient quoteAuthorRecipient = Recipient.resolved(quoteAuthor);
|
||||
|
||||
@@ -52,8 +52,8 @@ public abstract class SendJob extends BaseJob {
|
||||
attachments.addAll(Stream.of(message.getLinkPreviews()).map(lp -> lp.getThumbnail().orElse(null)).withoutNulls().toList());
|
||||
attachments.addAll(Stream.of(message.getSharedContacts()).map(Contact::getAvatarAttachment).withoutNulls().toList());
|
||||
|
||||
if (message.getOutgoingQuote() != null) {
|
||||
attachments.addAll(message.getOutgoingQuote().getAttachments());
|
||||
if (message.getOutgoingQuote() != null && message.getOutgoingQuote().getAttachment() != null) {
|
||||
attachments.add(message.getOutgoingQuote().getAttachment());
|
||||
}
|
||||
|
||||
AttachmentTable database = SignalDatabase.attachments();
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ object EditMessageProcessor {
|
||||
targetQuote.author,
|
||||
targetQuote.displayText.toString(),
|
||||
targetQuote.isOriginalMissing,
|
||||
emptyList(),
|
||||
null,
|
||||
null,
|
||||
targetQuote.quoteType,
|
||||
null
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class QuoteModel(
|
||||
val author: RecipientId,
|
||||
val text: String,
|
||||
val isOriginalMissing: Boolean,
|
||||
val attachments: List<Attachment>,
|
||||
val attachment: Attachment?,
|
||||
mentions: List<Mention>?,
|
||||
val type: Type,
|
||||
val bodyRanges: BodyRangeList?
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable;
|
||||
import org.thoughtcrime.securesms.database.MessageTable;
|
||||
import org.thoughtcrime.securesms.database.MessageTable.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.MessageTable.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||
@@ -63,6 +64,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
@@ -76,6 +78,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -114,7 +117,7 @@ public class MessageSender {
|
||||
|
||||
for (OutgoingMessage message : messages) {
|
||||
long allocatedThreadId = threadTable.getOrCreateValidThreadId(message.getThreadRecipient(), -1L, message.getDistributionType());
|
||||
long messageId = database.insertMessageOutbox(message.stripAttachments(), allocatedThreadId, false, insertListener);
|
||||
long messageId = database.insertMessageOutbox(message.stripAttachments(), allocatedThreadId, false, insertListener).getMessageId();
|
||||
|
||||
messageIds.add(messageId);
|
||||
threads.add(allocatedThreadId);
|
||||
@@ -199,6 +202,7 @@ public class MessageSender {
|
||||
recipient,
|
||||
SendType.SIGNAL,
|
||||
messageId,
|
||||
null,
|
||||
jobDependencyIds
|
||||
);
|
||||
}
|
||||
@@ -222,9 +226,11 @@ public class MessageSender {
|
||||
ThreadTable threadTable = SignalDatabase.threads();
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
|
||||
long allocatedThreadId = threadTable.getOrCreateValidThreadId(message.getThreadRecipient(), threadId, message.getDistributionType());
|
||||
Recipient recipient = message.getThreadRecipient();
|
||||
long messageId = database.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, recipient, message, allocatedThreadId), allocatedThreadId, sendType != SendType.SIGNAL, insertListener);
|
||||
long allocatedThreadId = threadTable.getOrCreateValidThreadId(message.getThreadRecipient(), threadId, message.getDistributionType());
|
||||
Recipient recipient = message.getThreadRecipient();
|
||||
InsertResult insertResult = database.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, recipient, message, allocatedThreadId), allocatedThreadId, sendType != SendType.SIGNAL, insertListener);
|
||||
long messageId = insertResult.getMessageId();
|
||||
|
||||
|
||||
if (message.getThreadRecipient().isGroup()) {
|
||||
if (message.getAttachments().isEmpty() && message.getLinkPreviews().isEmpty() && message.getSharedContacts().isEmpty()) {
|
||||
@@ -236,7 +242,7 @@ public class MessageSender {
|
||||
SignalLocalMetrics.IndividualMessageSend.onInsertedIntoDatabase(messageId, metricId);
|
||||
}
|
||||
|
||||
sendMessageInternal(context, recipient, sendType, messageId, Collections.emptyList());
|
||||
sendMessageInternal(context, recipient, sendType, messageId, insertResult.getQuoteAttachmentId(), Collections.emptyList());
|
||||
onMessageSent();
|
||||
threadTable.update(allocatedThreadId, true, true);
|
||||
|
||||
@@ -271,10 +277,11 @@ public class MessageSender {
|
||||
return false;
|
||||
}
|
||||
|
||||
long messageId = mmsDatabase.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, recipient, message, allocatedThreadId),
|
||||
allocatedThreadId,
|
||||
false,
|
||||
insertListener);
|
||||
InsertResult insertResult = mmsDatabase.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, recipient, message, allocatedThreadId),
|
||||
allocatedThreadId,
|
||||
false,
|
||||
insertListener);
|
||||
long messageId = insertResult.getMessageId();
|
||||
|
||||
for (AttachmentId attachmentId: attachmentIds) {
|
||||
boolean wasPreuploaded = SignalDatabase.attachments().getMessageId(attachmentId) == AttachmentTable.PREUPLOAD_MESSAGE_ID;
|
||||
@@ -286,7 +293,7 @@ public class MessageSender {
|
||||
|
||||
attachmentDatabase.updateMessageId(attachmentIds, messageId, message.getStoryType().isStory());
|
||||
|
||||
sendMessageInternal(context, recipient, SendType.SIGNAL, messageId, jobIds);
|
||||
sendMessageInternal(context, recipient, SendType.SIGNAL, messageId, insertResult.getQuoteAttachmentId(), jobIds);
|
||||
onMessageSent();
|
||||
threadTable.update(allocatedThreadId, true, true);
|
||||
|
||||
@@ -324,7 +331,7 @@ public class MessageSender {
|
||||
long primaryMessageId = mmsDatabase.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, primaryMessage.getThreadRecipient(), primaryMessage, primaryThreadId),
|
||||
primaryThreadId,
|
||||
false,
|
||||
null);
|
||||
null).getMessageId();
|
||||
|
||||
attachmentDatabase.updateMessageId(preUploadAttachmentIds, primaryMessageId, primaryMessage.getStoryType().isStory());
|
||||
if (primaryMessage.getStoryType() != StoryType.NONE) {
|
||||
@@ -352,7 +359,7 @@ public class MessageSender {
|
||||
long messageId = mmsDatabase.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, secondaryMessage.getThreadRecipient(), secondaryMessage, allocatedThreadId),
|
||||
allocatedThreadId,
|
||||
false,
|
||||
null);
|
||||
null).getMessageId();
|
||||
List<AttachmentId> attachmentIds = new ArrayList<>(preUploadAttachmentIds.size());
|
||||
|
||||
for (int i = 0; i < preUploadAttachments.size(); i++) {
|
||||
@@ -517,7 +524,15 @@ public class MessageSender {
|
||||
sendType = SendType.SIGNAL;
|
||||
}
|
||||
|
||||
sendMessageInternal(context, recipient, sendType, messageId, Collections.emptyList());
|
||||
AttachmentId quoteAttachmentId = SignalDatabase.attachments()
|
||||
.getAttachmentsForMessage(messageId)
|
||||
.stream()
|
||||
.filter(it -> it.quote)
|
||||
.findFirst()
|
||||
.map(it -> it.attachmentId)
|
||||
.orElse(null);
|
||||
|
||||
sendMessageInternal(context, recipient, sendType, messageId, quoteAttachmentId, Collections.emptyList());
|
||||
|
||||
onMessageSent();
|
||||
}
|
||||
@@ -542,14 +557,23 @@ public class MessageSender {
|
||||
Recipient recipient,
|
||||
SendType sendType,
|
||||
long messageId,
|
||||
@Nullable AttachmentId quoteAttachmentId,
|
||||
@NonNull Collection<String> uploadJobIds)
|
||||
{
|
||||
Set<String> finalUploadJobIds = new HashSet<>(uploadJobIds);
|
||||
|
||||
if (quoteAttachmentId != null) {
|
||||
Job uploadJob = new AttachmentUploadJob(quoteAttachmentId);
|
||||
AppDependencies.getJobManager().add(uploadJob);
|
||||
finalUploadJobIds.add(uploadJob.getId());
|
||||
}
|
||||
|
||||
if (recipient.isPushGroup()) {
|
||||
sendGroupPush(context, recipient, messageId, Collections.emptySet(), uploadJobIds);
|
||||
sendGroupPush(context, recipient, messageId, Collections.emptySet(), finalUploadJobIds);
|
||||
} else if (recipient.isDistributionList()) {
|
||||
sendDistributionList(context, recipient, messageId, Collections.emptySet(), uploadJobIds);
|
||||
sendDistributionList(context, recipient, messageId, Collections.emptySet(), finalUploadJobIds);
|
||||
} else if (sendType == SendType.SIGNAL && isPushMediaSend(context, recipient)) {
|
||||
sendMediaPush(context, recipient, messageId, uploadJobIds);
|
||||
sendMediaPush(context, recipient, messageId, finalUploadJobIds);
|
||||
} else {
|
||||
Log.w(TAG, "Unknown send type!");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class StoryDirectReplyRepository(context: Context) {
|
||||
expiresIn = TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong()),
|
||||
parentStoryId = ParentStoryId.DirectReply(storyId),
|
||||
isStoryReaction = isReaction,
|
||||
outgoingQuote = QuoteModel(message.dateSent, quoteAuthor.id, message.body, false, message.slideDeck.asAttachments(), null, QuoteModel.Type.NORMAL, message.messageRanges),
|
||||
outgoingQuote = QuoteModel(message.dateSent, quoteAuthor.id, message.body, false, message.slideDeck.asAttachments().firstOrNull(), null, QuoteModel.Type.NORMAL, message.messageRanges),
|
||||
bodyRanges = bodyRangeList,
|
||||
isSecure = true
|
||||
),
|
||||
|
||||
@@ -351,6 +351,10 @@ public class MediaUtil {
|
||||
return !TextUtils.isEmpty(contentType) && contentType.trim().equals(IMAGE_WEBP);
|
||||
}
|
||||
|
||||
public static boolean isPngType(String contentType) {
|
||||
return !TextUtils.isEmpty(contentType) && contentType.trim().equals(IMAGE_PNG);
|
||||
}
|
||||
|
||||
public static boolean isFile(Attachment attachment) {
|
||||
return !isGif(attachment) && !isImage(attachment) && !isAudio(attachment) && !isVideo(attachment);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user