mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-26 22:20:20 +00:00
Add backup support for DirectStoryReplyMessages.
This commit is contained in:
@@ -43,6 +43,10 @@ object ExportSkips {
|
||||
return log(sentTimestamp, "Group update record is parseable, but has no updates.")
|
||||
}
|
||||
|
||||
fun directStoryReplyHasNoBody(sentTimestamp: Long): String {
|
||||
return log(sentTimestamp, "Direct story reply has no body.")
|
||||
}
|
||||
|
||||
private fun log(sentTimestamp: Long, message: String): String {
|
||||
return "[SKIP][$sentTimestamp] $message"
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, medi
|
||||
${MessageTable.MISMATCHED_IDENTITIES},
|
||||
${MessageTable.TYPE},
|
||||
${MessageTable.MESSAGE_EXTRAS},
|
||||
${MessageTable.VIEW_ONCE}
|
||||
${MessageTable.VIEW_ONCE},
|
||||
${MessageTable.PARENT_STORY_ID}
|
||||
)
|
||||
""".trimMargin()
|
||||
)
|
||||
@@ -123,7 +124,8 @@ fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, medi
|
||||
MessageTable.MISMATCHED_IDENTITIES,
|
||||
MessageTable.TYPE,
|
||||
MessageTable.MESSAGE_EXTRAS,
|
||||
MessageTable.VIEW_ONCE
|
||||
MessageTable.VIEW_ONCE,
|
||||
MessageTable.PARENT_STORY_ID
|
||||
)
|
||||
.from("${MessageTable.TABLE_NAME} INDEXED BY $dateReceivedIndex")
|
||||
.where("${MessageTable.STORY_TYPE} = 0 AND ${MessageTable.DATE_RECEIVED} >= $lastSeenReceivedTime")
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.ChatItem
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ContactAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ContactMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.DirectStoryReplyMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GenericGroupUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupCall
|
||||
@@ -291,6 +292,10 @@ class ChatItemArchiveExporter(
|
||||
builder.viewOnceMessage = record.toRemoteViewOnceMessage(mediaArchiveEnabled = mediaArchiveEnabled, reactionRecords = extraData.reactionsById[id], attachments = extraData.attachmentsById[id])
|
||||
}
|
||||
|
||||
record.parentStoryId != 0L -> {
|
||||
builder.directStoryReplyMessage = record.toRemoteDirectStoryReplyMessage(mediaArchiveEnabled = mediaArchiveEnabled, reactionRecords = extraData.reactionsById[id], attachments = extraData.attachmentsById[record.id]) ?: continue
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (record.body.isNullOrEmpty() && !extraData.attachmentsById.containsKey(record.id)) {
|
||||
Log.w(TAG, ExportSkips.emptyChatItem(record.dateSent))
|
||||
@@ -852,6 +857,36 @@ private fun Contact.PostalAddress.Type.toRemote(): ContactAttachment.PostalAddre
|
||||
}
|
||||
}
|
||||
|
||||
private fun BackupMessageRecord.toRemoteDirectStoryReplyMessage(mediaArchiveEnabled: Boolean, reactionRecords: List<ReactionRecord>?, attachments: List<DatabaseAttachment>?): DirectStoryReplyMessage? {
|
||||
if (this.body.isNullOrBlank()) {
|
||||
Log.w(TAG, ExportSkips.directStoryReplyHasNoBody(this.dateSent))
|
||||
return null
|
||||
}
|
||||
|
||||
val isReaction = MessageTypes.isStoryReaction(this.type)
|
||||
|
||||
return DirectStoryReplyMessage(
|
||||
storySentTimestamp = this.parentStoryId.takeUnless { it == MessageTable.PARENT_STORY_MISSING_ID },
|
||||
emoji = if (isReaction) {
|
||||
this.body
|
||||
} else {
|
||||
null
|
||||
},
|
||||
textReply = if (!isReaction) {
|
||||
DirectStoryReplyMessage.TextReply(
|
||||
text = Text(
|
||||
body = this.body,
|
||||
bodyRanges = this.bodyRanges?.toRemoteBodyRanges(this.dateSent) ?: emptyList()
|
||||
),
|
||||
longText = attachments?.firstOrNull { it.contentType == MediaUtil.LONG_TEXT }?.toRemoteFilePointer(mediaArchiveEnabled)
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
reactions = reactionRecords.toRemote()
|
||||
)
|
||||
}
|
||||
|
||||
private fun BackupMessageRecord.toRemoteStandardMessage(db: SignalDatabase, mediaArchiveEnabled: Boolean, reactionRecords: List<ReactionRecord>?, mentions: List<Mention>?, attachments: List<DatabaseAttachment>?): StandardMessage {
|
||||
val text = body?.let {
|
||||
Text(
|
||||
@@ -1344,7 +1379,8 @@ private fun Cursor.toBackupMessageRecord(pastIds: Set<Long>, backupStartTime: Lo
|
||||
identityMismatchRecipientIds = this.requireString(MessageTable.MISMATCHED_IDENTITIES).parseIdentityMismatches(),
|
||||
baseType = this.requireLong(MessageTable.TYPE) and MessageTypes.BASE_TYPE_MASK,
|
||||
messageExtras = this.requireBlob(MessageTable.MESSAGE_EXTRAS).parseMessageExtras(),
|
||||
viewOnce = this.requireBoolean(MessageTable.VIEW_ONCE)
|
||||
viewOnce = this.requireBoolean(MessageTable.VIEW_ONCE),
|
||||
parentStoryId = this.requireLong(MessageTable.PARENT_STORY_ID)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1372,6 +1408,7 @@ private class BackupMessageRecord(
|
||||
val quoteBodyRanges: ByteArray?,
|
||||
val quoteType: Int,
|
||||
val originalMessageId: Long?,
|
||||
val parentStoryId: Long,
|
||||
val latestRevisionId: Long?,
|
||||
val hasDeliveryReceipt: Boolean,
|
||||
val hasReadReceipt: Boolean,
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.BodyRange
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatItem
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ContactAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.DirectStoryReplyMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupCall
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.IndividualCall
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.LinkPreview
|
||||
@@ -125,7 +126,8 @@ class ChatItemArchiveImporter(
|
||||
MessageTable.VIEW_ONCE,
|
||||
MessageTable.MESSAGE_EXTRAS,
|
||||
MessageTable.ORIGINAL_MESSAGE_ID,
|
||||
MessageTable.LATEST_REVISION_ID
|
||||
MessageTable.LATEST_REVISION_ID,
|
||||
MessageTable.PARENT_STORY_ID
|
||||
)
|
||||
|
||||
private val REACTION_COLUMNS = arrayOf(
|
||||
@@ -238,10 +240,11 @@ class ChatItemArchiveImporter(
|
||||
private fun ChatItem.toMessageInsert(fromRecipientId: RecipientId, chatRecipientId: RecipientId, threadId: Long): MessageInsert {
|
||||
val contentValues = this.toMessageContentValues(fromRecipientId, chatRecipientId, threadId)
|
||||
|
||||
var followUp: ((Long) -> Unit)? = null
|
||||
val followUps: MutableList<(Long) -> Unit> = mutableListOf()
|
||||
|
||||
if (this.updateMessage != null) {
|
||||
if (this.updateMessage.individualCall != null && this.updateMessage.individualCall.callId != null) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
val values = contentValuesOf(
|
||||
CallTable.CALL_ID to updateMessage.individualCall.callId,
|
||||
CallTable.MESSAGE_ID to messageRowId,
|
||||
@@ -263,7 +266,7 @@ class ChatItemArchiveImporter(
|
||||
db.insert(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values)
|
||||
}
|
||||
} else if (this.updateMessage.groupCall != null && this.updateMessage.groupCall.callId != null) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
val ringer: RecipientId? = this.updateMessage.groupCall.ringerRecipientId?.let { importState.remoteToLocalRecipientId[it] }
|
||||
|
||||
val values = contentValuesOf(
|
||||
@@ -295,7 +298,7 @@ class ChatItemArchiveImporter(
|
||||
}
|
||||
|
||||
if (this.paymentNotification != null) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
val uuid = tryRestorePayment(this, chatRecipientId)
|
||||
if (uuid != null) {
|
||||
db.update(MessageTable.TABLE_NAME)
|
||||
@@ -347,7 +350,7 @@ class ChatItemArchiveImporter(
|
||||
|
||||
if (contact != null) {
|
||||
val contactAttachment: Attachment? = contact.avatarAttachment
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
val attachmentMap = if (contactAttachment != null) {
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, listOf(contactAttachment), emptyList())
|
||||
} else {
|
||||
@@ -365,6 +368,19 @@ class ChatItemArchiveImporter(
|
||||
}
|
||||
}
|
||||
|
||||
if (this.directStoryReplyMessage != null) {
|
||||
val longTextAttachment: Attachment? = this.directStoryReplyMessage.textReply?.longText?.toLocalAttachment(
|
||||
importState = importState,
|
||||
contentType = "text/x-signal-plain"
|
||||
)
|
||||
|
||||
if (longTextAttachment != null) {
|
||||
followUps += { messageRowId ->
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, listOf(longTextAttachment), emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.standardMessage != null) {
|
||||
val bodyRanges = this.standardMessage.text?.bodyRanges
|
||||
if (!bodyRanges.isNullOrEmpty()) {
|
||||
@@ -380,7 +396,7 @@ class ChatItemArchiveImporter(
|
||||
}
|
||||
}
|
||||
if (mentions.isNotEmpty()) {
|
||||
followUp = { messageId ->
|
||||
followUps += { messageId ->
|
||||
SignalDatabase.mentions.insert(threadId, messageId, mentions)
|
||||
}
|
||||
}
|
||||
@@ -391,19 +407,17 @@ class ChatItemArchiveImporter(
|
||||
attachment.toLocalAttachment()
|
||||
}
|
||||
|
||||
val longTextAttachments: List<Attachment> = this.standardMessage.longText?.let { longTextPointer ->
|
||||
longTextPointer.toLocalAttachment(
|
||||
importState = importState,
|
||||
contentType = "text/x-signal-plain"
|
||||
)
|
||||
}?.let { listOf(it) } ?: emptyList()
|
||||
val longTextAttachments: List<Attachment> = this.standardMessage.longText?.toLocalAttachment(
|
||||
importState = importState,
|
||||
contentType = "text/x-signal-plain"
|
||||
)?.let { listOf(it) } ?: emptyList()
|
||||
|
||||
val quoteAttachments: List<Attachment> = this.standardMessage.quote?.toLocalAttachments() ?: emptyList()
|
||||
|
||||
val hasAttachments = attachments.isNotEmpty() || linkPreviewAttachments.isNotEmpty() || quoteAttachments.isNotEmpty() || longTextAttachments.isNotEmpty()
|
||||
|
||||
if (hasAttachments || linkPreviews.isNotEmpty()) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
val attachmentMap = if (hasAttachments) {
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, attachments + linkPreviewAttachments + longTextAttachments, quoteAttachments)
|
||||
} else {
|
||||
@@ -424,7 +438,7 @@ class ChatItemArchiveImporter(
|
||||
val sticker = this.stickerMessage.sticker
|
||||
val attachment = sticker.toLocalAttachment()
|
||||
if (attachment != null) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, listOf(attachment), emptyList())
|
||||
}
|
||||
}
|
||||
@@ -433,12 +447,20 @@ class ChatItemArchiveImporter(
|
||||
if (this.viewOnceMessage != null) {
|
||||
val attachment = this.viewOnceMessage.attachment?.toLocalAttachment()
|
||||
if (attachment != null) {
|
||||
followUp = { messageRowId ->
|
||||
followUps += { messageRowId ->
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, listOf(attachment), emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val followUp: ((Long) -> Unit)? = if (followUps.isNotEmpty()) {
|
||||
{ messageId ->
|
||||
followUps.forEach { it(messageId) }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return MessageInsert(contentValues, followUp)
|
||||
}
|
||||
|
||||
@@ -505,6 +527,7 @@ class ChatItemArchiveImporter(
|
||||
this.paymentNotification != null -> contentValues.addPaymentNotification(this, chatRecipientId)
|
||||
this.giftBadge != null -> contentValues.addGiftBadge(this.giftBadge)
|
||||
this.viewOnceMessage != null -> contentValues.addViewOnce(this.viewOnceMessage)
|
||||
this.directStoryReplyMessage != null -> contentValues.addDirectStoryReply(this.directStoryReplyMessage)
|
||||
}
|
||||
|
||||
return contentValues
|
||||
@@ -548,6 +571,7 @@ class ChatItemArchiveImporter(
|
||||
this.contactMessage != null -> this.contactMessage.reactions
|
||||
this.stickerMessage != null -> this.stickerMessage.reactions
|
||||
this.viewOnceMessage != null -> this.viewOnceMessage.reactions
|
||||
this.directStoryReplyMessage != null -> this.directStoryReplyMessage.reactions
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
@@ -625,6 +649,10 @@ class ChatItemArchiveImporter(
|
||||
type = type or MessageTypes.SPECIAL_TYPE_GIFT_BADGE
|
||||
}
|
||||
|
||||
if (this.directStoryReplyMessage?.emoji != null) {
|
||||
type = type or MessageTypes.SPECIAL_TYPE_STORY_REACTION
|
||||
}
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
@@ -848,6 +876,19 @@ class ChatItemArchiveImporter(
|
||||
put(MessageTable.VIEW_ONCE, true.toInt())
|
||||
}
|
||||
|
||||
private fun ContentValues.addDirectStoryReply(directStoryReply: DirectStoryReplyMessage) {
|
||||
put(MessageTable.PARENT_STORY_ID, directStoryReply.storySentTimestamp?.takeUnless { it == 0L } ?: MessageTable.PARENT_STORY_MISSING_ID)
|
||||
|
||||
if (directStoryReply.emoji != null) {
|
||||
put(MessageTable.BODY, directStoryReply.emoji)
|
||||
}
|
||||
|
||||
if (directStoryReply.textReply != null) {
|
||||
put(MessageTable.BODY, directStoryReply.textReply.text?.body)
|
||||
put(MessageTable.MESSAGE_RANGES, directStoryReply.textReply.text?.bodyRanges?.toLocalBodyRanges()?.encode())
|
||||
}
|
||||
}
|
||||
|
||||
private fun String?.tryParseMoney(): Money? {
|
||||
if (this.isNullOrEmpty()) {
|
||||
return null
|
||||
|
||||
@@ -210,6 +210,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
const val QUOTE_TARGET_MISSING_ID = -1L
|
||||
|
||||
const val ADDRESSABLE_MESSAGE_LIMIT = 5
|
||||
const val PARENT_STORY_MISSING_ID = -1L
|
||||
|
||||
const val CREATE_TABLE = """
|
||||
CREATE TABLE $TABLE_NAME (
|
||||
|
||||
Reference in New Issue
Block a user