diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 4b34ea130c..03ccb5ab5a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -15,6 +15,7 @@ import org.signal.core.util.requireBlob import org.signal.core.util.requireBoolean import org.signal.core.util.requireInt import org.signal.core.util.requireLong +import org.signal.core.util.requireLongOrNull import org.signal.core.util.requireString import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment @@ -70,6 +71,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray import java.io.Closeable import java.io.IOException +import java.util.HashMap import java.util.LinkedList import java.util.Queue import kotlin.jvm.optionals.getOrNull @@ -96,6 +98,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: */ private val buffer: Queue = LinkedList() + private val revisionMap: HashMap> = HashMap() + override fun hasNext(): Boolean { return buffer.isNotEmpty() || (cursor.count > 0 && !cursor.isLast && !cursor.isAfterLast) } @@ -346,8 +350,20 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: } else -> builder.standardMessage = record.toStandardMessage(reactionsById[id], mentions = mentionsById[id], attachments = attachmentsById[record.id]) } - - buffer += builder.build() + if (record.latestRevisionId == null) { + val previousEdits = revisionMap.remove(record.id) + if (previousEdits != null) { + builder.revisions = previousEdits + } + buffer += builder.build() + } else { + var previousEdits = revisionMap[record.latestRevisionId] + if (previousEdits == null) { + previousEdits = ArrayList() + revisionMap[record.latestRevisionId] = previousEdits + } + previousEdits += builder.build() + } } return if (buffer.isNotEmpty()) { @@ -709,8 +725,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: quoteMissing = this.requireBoolean(MessageTable.QUOTE_MISSING), quoteBodyRanges = this.requireBlob(MessageTable.QUOTE_BODY_RANGES), quoteType = this.requireInt(MessageTable.QUOTE_TYPE), - originalMessageId = this.requireLong(MessageTable.ORIGINAL_MESSAGE_ID), - latestRevisionId = this.requireLong(MessageTable.LATEST_REVISION_ID), + originalMessageId = this.requireLongOrNull(MessageTable.ORIGINAL_MESSAGE_ID), + latestRevisionId = this.requireLongOrNull(MessageTable.LATEST_REVISION_ID), hasDeliveryReceipt = this.requireBoolean(MessageTable.HAS_DELIVERY_RECEIPT), viewed = this.requireBoolean(MessageTable.VIEWED_COLUMN), hasReadReceipt = this.requireBoolean(MessageTable.HAS_READ_RECEIPT), @@ -744,8 +760,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: val quoteMissing: Boolean, val quoteBodyRanges: ByteArray?, val quoteType: Int, - val originalMessageId: Long, - val latestRevisionId: Long, + val originalMessageId: Long?, + val latestRevisionId: Long?, val hasDeliveryReceipt: Boolean, val hasReadReceipt: Boolean, val viewed: Boolean, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 9cfdd2e781..3ceed7ae1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -105,7 +105,9 @@ class ChatItemImportInserter( MessageTable.LINK_PREVIEWS, MessageTable.MESSAGE_RANGES, MessageTable.VIEW_ONCE, - MessageTable.MESSAGE_EXTRAS + MessageTable.MESSAGE_EXTRAS, + MessageTable.ORIGINAL_MESSAGE_ID, + MessageTable.LATEST_REVISION_ID ) private val REACTION_COLUMNS = arrayOf( @@ -157,8 +159,22 @@ class ChatItemImportInserter( Log.w(TAG, "[insert] Could not find a backup recipientId for backup chatId ${chatItem.chatId}! Skipping.") return } + val messageInsert = chatItem.toMessageInsert(fromLocalRecipientId, chatLocalRecipientId, localThreadId) + if (chatItem.revisions.isNotEmpty()) { + val originalId = messageId + val latestRevisionId = originalId + chatItem.revisions.size + val sortedRevisions = chatItem.revisions.sortedBy { it.dateSent }.map { it.toMessageInsert(fromLocalRecipientId, chatLocalRecipientId, localThreadId) } + for (revision in sortedRevisions) { + revision.contentValues.put(MessageTable.ORIGINAL_MESSAGE_ID, originalId) + revision.contentValues.put(MessageTable.LATEST_REVISION_ID, latestRevisionId) + revision.contentValues.put(MessageTable.REVISION_NUMBER, (messageId - originalId)) + buffer.messages += revision + messageId++ + } - buffer.messages += chatItem.toMessageInsert(fromLocalRecipientId, chatLocalRecipientId, localThreadId) + messageInsert.contentValues.put(MessageTable.ORIGINAL_MESSAGE_ID, originalId) + } + buffer.messages += messageInsert buffer.reactions += chatItem.toReactionContentValues(messageId) buffer.groupReceipts += chatItem.toGroupReceiptContentValues(messageId, chatBackupRecipientId) @@ -697,7 +713,11 @@ class ChatItemImportInserter( ?: if (this.contentType == null) null else PointerAttachment.forPointer(quotedAttachment = DataMessage.Quote.QuotedAttachment(contentType = this.contentType, fileName = this.fileName, thumbnail = null)).orNull() } - private class MessageInsert(val contentValues: ContentValues, val followUp: ((Long) -> Unit)?) + private class MessageInsert( + val contentValues: ContentValues, + val followUp: ((Long) -> Unit)?, + val edits: List? = null + ) private class Buffer( val messages: MutableList = mutableListOf(),