mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Add tests for text messages with mentions, quotes, reactions, and ranges.
This commit is contained in:
@@ -9,9 +9,12 @@ import okio.ByteString.Companion.toByteString
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.signal.libsignal.messagebackup.MessageBackup
|
||||
import org.signal.libsignal.messagebackup.MessageBackupKey
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.AccountData
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.BodyRange
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Call
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Chat
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatItem
|
||||
@@ -19,10 +22,15 @@ import org.thoughtcrime.securesms.backup.v2.proto.Contact
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.DistributionList
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Frame
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Group
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Quote
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Reaction
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Recipient
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ReleaseNotes
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Self
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.SendStatus
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.StickerPack
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Text
|
||||
import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupReader
|
||||
import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupWriter
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
@@ -34,6 +42,7 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.ArrayList
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
@@ -53,7 +62,7 @@ class ImportExportTest {
|
||||
val releaseNotes = Recipient(id = 2, releaseNotes = ReleaseNotes())
|
||||
val standardAccountData = AccountData(
|
||||
profileKey = SELF_PROFILE_KEY.serialize().toByteString(),
|
||||
username = "testusername",
|
||||
username = "self.01",
|
||||
usernameLink = null,
|
||||
givenName = "Peter",
|
||||
familyName = "Parker",
|
||||
@@ -81,6 +90,24 @@ class ImportExportTest {
|
||||
preferredReactionEmoji = listOf("a", "b", "c")
|
||||
)
|
||||
)
|
||||
val alice = Recipient(
|
||||
id = 3,
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = false,
|
||||
hidden = false,
|
||||
registered = Contact.Registered.REGISTERED,
|
||||
unregisteredTimestamp = 0L,
|
||||
profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
|
||||
profileSharing = true,
|
||||
profileGivenName = "Alexa",
|
||||
profileFamilyName = "Kim",
|
||||
hideStory = true
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* When using standardFrames you must start recipient ids at 3.
|
||||
@@ -111,7 +138,7 @@ class ImportExportTest {
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "coolusername",
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = true,
|
||||
hidden = true,
|
||||
@@ -181,7 +208,7 @@ class ImportExportTest {
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "coolusername",
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = true,
|
||||
hidden = true,
|
||||
@@ -251,7 +278,7 @@ class ImportExportTest {
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "coolusername",
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = true,
|
||||
hidden = true,
|
||||
@@ -296,7 +323,7 @@ class ImportExportTest {
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "coolusername",
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = false,
|
||||
hidden = false,
|
||||
@@ -398,7 +425,7 @@ class ImportExportTest {
|
||||
contact = Contact(
|
||||
aci = TestRecipientUtils.nextAci().toByteString(),
|
||||
pni = TestRecipientUtils.nextPni().toByteString(),
|
||||
username = "coolusername",
|
||||
username = "cool.01",
|
||||
e164 = 141255501234,
|
||||
blocked = false,
|
||||
hidden = false,
|
||||
@@ -426,6 +453,397 @@ class ImportExportTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageWithOnlyText() {
|
||||
var dateSent = System.currentTimeMillis()
|
||||
val sendStatuses = enumerateSendStatuses(alice.id)
|
||||
val incomingMessageDetails = enumerateIncomingMessageDetails(dateSent + 200)
|
||||
val outgoingMessages = ArrayList<ChatItem>()
|
||||
val incomingMessages = ArrayList<ChatItem>()
|
||||
for (sendStatus in sendStatuses) {
|
||||
outgoingMessages.add(
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = selfRecipient.id,
|
||||
dateSent = dateSent++,
|
||||
expireStartDate = dateSent + 1000,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
sms = false,
|
||||
outgoing = ChatItem.OutgoingMessageDetails(
|
||||
sendStatus = listOf(sendStatus)
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Text only body"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
dateSent++
|
||||
for (incomingDetail in incomingMessageDetails) {
|
||||
incomingMessages.add(
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = alice.id,
|
||||
dateSent = dateSent++,
|
||||
expireStartDate = dateSent + 1000,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
sms = false,
|
||||
incoming = incomingDetail,
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Text only body"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
importExport(
|
||||
*standardFrames,
|
||||
alice,
|
||||
buildChat(alice, 1),
|
||||
*outgoingMessages.toArray(),
|
||||
*incomingMessages.toArray()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageWithTextMentionsBodyRangesAndReactions() {
|
||||
val time = System.currentTimeMillis()
|
||||
importExport(
|
||||
*standardFrames,
|
||||
alice,
|
||||
buildChat(alice, 1),
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = selfRecipient.id,
|
||||
dateSent = 100,
|
||||
expireStartDate = time,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 105,
|
||||
dateServerSent = 104,
|
||||
read = true,
|
||||
sealedSender = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Hey check this out I love spans!",
|
||||
bodyRanges = listOf(
|
||||
BodyRange(
|
||||
start = 6,
|
||||
length = 3,
|
||||
style = BodyRange.Style.BOLD
|
||||
),
|
||||
BodyRange(
|
||||
start = 10,
|
||||
length = 3,
|
||||
style = BodyRange.Style.ITALIC
|
||||
),
|
||||
BodyRange(
|
||||
start = 14,
|
||||
length = 3,
|
||||
style = BodyRange.Style.SPOILER
|
||||
),
|
||||
BodyRange(
|
||||
start = 18,
|
||||
length = 3,
|
||||
style = BodyRange.Style.STRIKETHROUGH
|
||||
),
|
||||
BodyRange(
|
||||
start = 22,
|
||||
length = 3,
|
||||
style = BodyRange.Style.MONOSPACE
|
||||
),
|
||||
BodyRange(
|
||||
start = 4,
|
||||
length = 0,
|
||||
mentionAci = alice.contact!!.aci
|
||||
)
|
||||
)
|
||||
),
|
||||
reactions = listOf(
|
||||
Reaction(emoji = "F", authorId = selfRecipient.id, sentTimestamp = 302, receivedTimestamp = 303),
|
||||
Reaction(emoji = "F", authorId = alice.id, sentTimestamp = 301, receivedTimestamp = 302)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageWithTextAndQuotes() {
|
||||
val spans = listOf(
|
||||
BodyRange(
|
||||
start = 6,
|
||||
length = 3,
|
||||
style = BodyRange.Style.BOLD
|
||||
),
|
||||
BodyRange(
|
||||
start = 10,
|
||||
length = 3,
|
||||
style = BodyRange.Style.ITALIC
|
||||
),
|
||||
BodyRange(
|
||||
start = 14,
|
||||
length = 3,
|
||||
style = BodyRange.Style.SPOILER
|
||||
),
|
||||
BodyRange(
|
||||
start = 18,
|
||||
length = 3,
|
||||
style = BodyRange.Style.STRIKETHROUGH
|
||||
),
|
||||
BodyRange(
|
||||
start = 22,
|
||||
length = 3,
|
||||
style = BodyRange.Style.MONOSPACE
|
||||
)
|
||||
)
|
||||
val time = System.currentTimeMillis()
|
||||
importExport(
|
||||
*standardFrames,
|
||||
alice,
|
||||
buildChat(alice, 1),
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = selfRecipient.id,
|
||||
dateSent = 100,
|
||||
expireStartDate = time,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 105,
|
||||
dateServerSent = 104,
|
||||
read = true,
|
||||
sealedSender = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Hey check this out I love spans!",
|
||||
bodyRanges = spans
|
||||
)
|
||||
)
|
||||
),
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = selfRecipient.id,
|
||||
dateSent = 101,
|
||||
expireStartDate = time,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 105,
|
||||
dateServerSent = 104,
|
||||
read = true,
|
||||
sealedSender = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "I quoted an existing message"
|
||||
),
|
||||
quote = Quote(
|
||||
targetSentTimestamp = 100,
|
||||
authorId = alice.id,
|
||||
type = Quote.Type.NORMAL,
|
||||
text = "Hey check this out I love spans!",
|
||||
bodyRanges = spans
|
||||
)
|
||||
)
|
||||
),
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = selfRecipient.id,
|
||||
dateSent = 102,
|
||||
expireStartDate = time,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(2),
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 105,
|
||||
dateServerSent = 104,
|
||||
read = true,
|
||||
sealedSender = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "I quoted an non-existing message"
|
||||
),
|
||||
quote = Quote(
|
||||
targetSentTimestamp = 60,
|
||||
authorId = alice.id,
|
||||
type = Quote.Type.NORMAL,
|
||||
text = "Hey check this out I love spans!",
|
||||
bodyRanges = spans
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messagesNearExpirationNotExported() {
|
||||
val chat = buildChat(alice, 1)
|
||||
val expirationNotStarted = ChatItem(
|
||||
chatId = 1,
|
||||
authorId = alice.id,
|
||||
dateSent = 101,
|
||||
expireStartDate = null,
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(1),
|
||||
sms = false,
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 100,
|
||||
dateServerSent = 100,
|
||||
read = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Expiration not started but less than or equal to 1 day"
|
||||
)
|
||||
)
|
||||
)
|
||||
import(
|
||||
*standardFrames,
|
||||
alice,
|
||||
chat,
|
||||
ChatItem(
|
||||
chatId = 1,
|
||||
authorId = alice.id,
|
||||
dateSent = 100,
|
||||
expireStartDate = System.currentTimeMillis(),
|
||||
expiresInMs = TimeUnit.DAYS.toMillis(1),
|
||||
sms = false,
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateReceived = 100,
|
||||
dateServerSent = 100,
|
||||
read = true
|
||||
),
|
||||
standardMessage = StandardMessage(
|
||||
text = Text(
|
||||
body = "Near expiration"
|
||||
)
|
||||
)
|
||||
),
|
||||
expirationNotStarted
|
||||
)
|
||||
val exported = export()
|
||||
val expected = exportFrames(
|
||||
*standardFrames,
|
||||
alice,
|
||||
chat,
|
||||
expirationNotStarted
|
||||
)
|
||||
compare(expected, exported)
|
||||
}
|
||||
|
||||
fun enumerateIncomingMessageDetails(dateSent: Long): List<ChatItem.IncomingMessageDetails> {
|
||||
val details = mutableListOf<ChatItem.IncomingMessageDetails>()
|
||||
details.add(
|
||||
ChatItem.IncomingMessageDetails(
|
||||
dateReceived = dateSent + 1,
|
||||
dateServerSent = dateSent,
|
||||
read = true,
|
||||
sealedSender = true
|
||||
)
|
||||
)
|
||||
details.add(
|
||||
ChatItem.IncomingMessageDetails(
|
||||
dateReceived = dateSent + 1,
|
||||
dateServerSent = dateSent,
|
||||
read = true,
|
||||
sealedSender = false
|
||||
)
|
||||
)
|
||||
details.add(
|
||||
ChatItem.IncomingMessageDetails(
|
||||
dateReceived = dateSent + 1,
|
||||
dateServerSent = dateSent,
|
||||
read = false,
|
||||
sealedSender = true
|
||||
)
|
||||
)
|
||||
details.add(
|
||||
ChatItem.IncomingMessageDetails(
|
||||
dateReceived = dateSent + 1,
|
||||
dateServerSent = dateSent,
|
||||
read = false,
|
||||
sealedSender = false
|
||||
)
|
||||
)
|
||||
return details
|
||||
}
|
||||
|
||||
fun enumerateSendStatuses(recipientId: Long): List<SendStatus> {
|
||||
val statuses = ArrayList<SendStatus>()
|
||||
val sealedSenderStates = listOf(true, false)
|
||||
for (sealedSender in sealedSenderStates) {
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.DELIVERED,
|
||||
sealedSender = sealedSender,
|
||||
lastStatusUpdateTimestamp = -1
|
||||
)
|
||||
)
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.PENDING,
|
||||
sealedSender = sealedSender,
|
||||
lastStatusUpdateTimestamp = -1,
|
||||
networkFailure = true
|
||||
)
|
||||
)
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.SENT,
|
||||
sealedSender = sealedSender,
|
||||
lastStatusUpdateTimestamp = -1
|
||||
)
|
||||
)
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.READ,
|
||||
sealedSender = sealedSender,
|
||||
lastStatusUpdateTimestamp = -1
|
||||
)
|
||||
)
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.PENDING,
|
||||
sealedSender = sealedSender,
|
||||
networkFailure = true,
|
||||
lastStatusUpdateTimestamp = -1
|
||||
)
|
||||
)
|
||||
statuses.add(
|
||||
SendStatus(
|
||||
recipientId = recipientId,
|
||||
deliveryStatus = SendStatus.Status.FAILED,
|
||||
sealedSender = sealedSender,
|
||||
identityKeyMismatch = true,
|
||||
lastStatusUpdateTimestamp = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
return statuses
|
||||
}
|
||||
|
||||
private fun buildChat(recipient: Recipient, id: Long): Chat {
|
||||
return Chat(
|
||||
id = id,
|
||||
recipientId = recipient.id,
|
||||
archived = false,
|
||||
pinnedOrder = 0,
|
||||
expirationTimerMs = 0,
|
||||
muteUntilMs = 0,
|
||||
markedUnread = false,
|
||||
dontNotifyForMentionsIfMuted = false,
|
||||
wallpaper = null
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Export passed in frames as a backup. Does not automatically include
|
||||
* any standard frames (e.g. backup header).
|
||||
@@ -468,7 +886,18 @@ class ImportExportTest {
|
||||
/**
|
||||
* Export our current database as a backup.
|
||||
*/
|
||||
private fun export() = BackupRepository.export()
|
||||
private fun export(): ByteArray {
|
||||
val exportData = BackupRepository.export()
|
||||
return exportData
|
||||
}
|
||||
|
||||
private fun validate(importData: ByteArray): MessageBackup.ValidationResult {
|
||||
val factory = { ByteArrayInputStream(importData) }
|
||||
val masterKey = SignalStore.svr().getOrCreateMasterKey()
|
||||
val key = MessageBackupKey(masterKey.serialize(), org.signal.libsignal.protocol.ServiceId.Aci.parseFromBinary(SELF_ACI.toByteArray()))
|
||||
|
||||
return MessageBackup.validate(key, factory, importData.size.toLong())
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the passed in frames and then exports them.
|
||||
@@ -552,7 +981,7 @@ class ImportExportTest {
|
||||
prettyAssertEquals(accountImported, accountExported)
|
||||
prettyAssertEquals(recipientsImported, recipientsExported) { it.id }
|
||||
prettyAssertEquals(chatsImported, chatsExported) { it.id }
|
||||
prettyAssertEquals(chatItemsImported, chatItemsExported)
|
||||
prettyAssertEquals(chatItemsImported, chatItemsExported) { it.dateSent }
|
||||
prettyAssertEquals(callsImported, callsExported) { it.callId }
|
||||
prettyAssertEquals(stickersImported, stickersExported) { it.packId }
|
||||
}
|
||||
|
||||
@@ -70,13 +70,13 @@ object BackupRepository {
|
||||
)
|
||||
}
|
||||
|
||||
val exportState = ExportState()
|
||||
val exportState = ExportState(System.currentTimeMillis())
|
||||
|
||||
writer.use {
|
||||
writer.write(
|
||||
BackupInfo(
|
||||
version = VERSION,
|
||||
backupTimeMs = System.currentTimeMillis()
|
||||
backupTimeMs = exportState.backupTime
|
||||
)
|
||||
)
|
||||
// Note: Without a transaction, we may export inconsistent state. But because we have a transaction,
|
||||
@@ -396,7 +396,7 @@ object BackupRepository {
|
||||
}
|
||||
}
|
||||
|
||||
class ExportState {
|
||||
class ExportState(val backupTime: Long) {
|
||||
val recipientIds = HashSet<Long>()
|
||||
val threadIds = HashSet<Long>()
|
||||
}
|
||||
|
||||
@@ -188,10 +188,10 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
||||
} else {
|
||||
when {
|
||||
MessageTypes.isMissedAudioCall(record.type) -> {
|
||||
builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_AUDIO_CALL)))
|
||||
builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_INCOMING_AUDIO_CALL)))
|
||||
}
|
||||
MessageTypes.isMissedVideoCall(record.type) -> {
|
||||
builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_VIDEO_CALL)))
|
||||
builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_INCOMING_VIDEO_CALL)))
|
||||
}
|
||||
MessageTypes.isIncomingAudioCall(record.type) -> {
|
||||
builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.INCOMING_AUDIO_CALL)))
|
||||
@@ -268,7 +268,6 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
||||
chatId = record.threadId
|
||||
authorId = record.fromRecipientId
|
||||
dateSent = record.dateSent
|
||||
sealedSender = record.sealedSender
|
||||
expireStartDate = if (record.expireStarted > 0) record.expireStarted else null
|
||||
expiresInMs = if (record.expiresIn > 0) record.expiresIn else null
|
||||
revisions = emptyList()
|
||||
@@ -282,7 +281,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
||||
incoming = ChatItem.IncomingMessageDetails(
|
||||
dateServerSent = record.dateServer,
|
||||
dateReceived = record.dateReceived,
|
||||
read = record.read
|
||||
read = record.read,
|
||||
sealedSender = record.sealedSender
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ class ChatItemImportInserter(
|
||||
contentValues.put(MessageTable.VIEWED_COLUMN, 0)
|
||||
contentValues.put(MessageTable.HAS_READ_RECEIPT, 0)
|
||||
contentValues.put(MessageTable.HAS_DELIVERY_RECEIPT, 0)
|
||||
contentValues.put(MessageTable.UNIDENTIFIED, this.sealedSender?.toInt())
|
||||
contentValues.put(MessageTable.UNIDENTIFIED, this.incoming?.sealedSender?.toInt() ?: 0)
|
||||
contentValues.put(MessageTable.READ, this.incoming?.read?.toInt() ?: 0)
|
||||
contentValues.put(MessageTable.NOTIFIED, 1)
|
||||
}
|
||||
@@ -428,8 +428,10 @@ class ChatItemImportInserter(
|
||||
IndividualCallChatUpdate.Type.INCOMING_VIDEO_CALL -> MessageTypes.INCOMING_VIDEO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.OUTGOING_AUDIO_CALL -> MessageTypes.OUTGOING_AUDIO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.OUTGOING_VIDEO_CALL -> MessageTypes.OUTGOING_VIDEO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.MISSED_AUDIO_CALL -> MessageTypes.MISSED_AUDIO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.MISSED_VIDEO_CALL -> MessageTypes.MISSED_VIDEO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.MISSED_INCOMING_AUDIO_CALL -> MessageTypes.MISSED_AUDIO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.MISSED_INCOMING_VIDEO_CALL -> MessageTypes.MISSED_VIDEO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.UNANSWERED_OUTGOING_AUDIO_CALL -> MessageTypes.OUTGOING_AUDIO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.UNANSWERED_OUTGOING_VIDEO_CALL -> MessageTypes.OUTGOING_VIDEO_CALL_TYPE
|
||||
IndividualCallChatUpdate.Type.UNKNOWN -> typeFlags
|
||||
}
|
||||
}
|
||||
@@ -508,7 +510,7 @@ class ChatItemImportInserter(
|
||||
}
|
||||
|
||||
return BodyRangeList(
|
||||
ranges = this.map { bodyRange ->
|
||||
ranges = this.filter { it.mentionAci == null }.map { bodyRange ->
|
||||
BodyRangeList.BodyRange(
|
||||
mentionUuid = bodyRange.mentionAci?.let { UuidUtil.fromByteString(it) }?.toString(),
|
||||
style = bodyRange.style?.let {
|
||||
|
||||
@@ -11,11 +11,12 @@ import org.signal.core.util.select
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupState
|
||||
import org.thoughtcrime.securesms.database.MessageTable
|
||||
import org.thoughtcrime.securesms.database.MessageTypes
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private val TAG = Log.tag(MessageTable::class.java)
|
||||
private const val BASE_TYPE = "base_type"
|
||||
|
||||
fun MessageTable.getMessagesForBackup(): ChatItemExportIterator {
|
||||
fun MessageTable.getMessagesForBackup(backupTime: Long): ChatItemExportIterator {
|
||||
val cursor = readableDatabase
|
||||
.select(
|
||||
MessageTable.ID,
|
||||
@@ -53,13 +54,19 @@ fun MessageTable.getMessagesForBackup(): ChatItemExportIterator {
|
||||
.from(MessageTable.TABLE_NAME)
|
||||
.where(
|
||||
"""
|
||||
$BASE_TYPE IN (
|
||||
($BASE_TYPE IN (
|
||||
${MessageTypes.BASE_INBOX_TYPE},
|
||||
${MessageTypes.BASE_OUTBOX_TYPE},
|
||||
${MessageTypes.BASE_SENT_TYPE},
|
||||
${MessageTypes.BASE_SENDING_TYPE},
|
||||
${MessageTypes.BASE_SENT_FAILED_TYPE}
|
||||
) OR ${MessageTable.IS_CALL_TYPE_CLAUSE}
|
||||
) OR ${MessageTable.IS_CALL_TYPE_CLAUSE})
|
||||
AND
|
||||
(
|
||||
${MessageTable.EXPIRE_STARTED} = 0
|
||||
OR
|
||||
(${MessageTable.EXPIRES_IN} > 0 AND (${MessageTable.EXPIRE_STARTED} + ${MessageTable.EXPIRES_IN}) > $backupTime + ${TimeUnit.DAYS.toMillis(1)})
|
||||
)
|
||||
"""
|
||||
)
|
||||
.orderBy("${MessageTable.DATE_RECEIVED} ASC")
|
||||
|
||||
@@ -19,7 +19,7 @@ object ChatItemBackupProcessor {
|
||||
val TAG = Log.tag(ChatItemBackupProcessor::class.java)
|
||||
|
||||
fun export(exportState: ExportState, emitter: BackupFrameEmitter) {
|
||||
SignalDatabase.messages.getMessagesForBackup().use { chatItems ->
|
||||
SignalDatabase.messages.getMessagesForBackup(exportState.backupTime).use { chatItems ->
|
||||
for (chatItem in chatItems) {
|
||||
if (exportState.threadIds.contains(chatItem.chatId)) {
|
||||
emitter.emit(Frame(chatItem = chatItem))
|
||||
|
||||
@@ -48,21 +48,20 @@ message AccountData {
|
||||
bool readReceipts = 1;
|
||||
bool sealedSenderIndicators = 2;
|
||||
bool typingIndicators = 3;
|
||||
bool noteToSelfMarkedUnread = 4;
|
||||
bool linkPreviews = 5;
|
||||
bool notDiscoverableByPhoneNumber = 6;
|
||||
bool preferContactAvatars = 7;
|
||||
uint32 universalExpireTimer = 8; // 0 means no universal expire timer.
|
||||
repeated string preferredReactionEmoji = 9;
|
||||
bool displayBadgesOnProfile = 10;
|
||||
bool keepMutedChatsArchived = 11;
|
||||
bool hasSetMyStoriesPrivacy = 12;
|
||||
bool hasViewedOnboardingStory = 13;
|
||||
bool storiesDisabled = 14;
|
||||
optional bool storyViewReceiptsEnabled = 15;
|
||||
bool hasSeenGroupStoryEducationSheet = 16;
|
||||
bool hasCompletedUsernameOnboarding = 17;
|
||||
PhoneNumberSharingMode phoneNumberSharingMode = 18;
|
||||
bool linkPreviews = 4;
|
||||
bool notDiscoverableByPhoneNumber = 5;
|
||||
bool preferContactAvatars = 6;
|
||||
uint32 universalExpireTimer = 7; // 0 means no universal expire timer.
|
||||
repeated string preferredReactionEmoji = 8;
|
||||
bool displayBadgesOnProfile = 9;
|
||||
bool keepMutedChatsArchived = 10;
|
||||
bool hasSetMyStoriesPrivacy = 11;
|
||||
bool hasViewedOnboardingStory = 12;
|
||||
bool storiesDisabled = 13;
|
||||
optional bool storyViewReceiptsEnabled = 14;
|
||||
bool hasSeenGroupStoryEducationSheet = 15;
|
||||
bool hasCompletedUsernameOnboarding = 16;
|
||||
PhoneNumberSharingMode phoneNumberSharingMode = 17;
|
||||
}
|
||||
|
||||
bytes profileKey = 1;
|
||||
@@ -196,6 +195,7 @@ message ChatItem {
|
||||
uint64 dateReceived = 1;
|
||||
uint64 dateServerSent = 2;
|
||||
bool read = 3;
|
||||
bool sealedSender = 4;
|
||||
}
|
||||
|
||||
message OutgoingMessageDetails {
|
||||
@@ -208,24 +208,23 @@ message ChatItem {
|
||||
uint64 chatId = 1; // conversation id
|
||||
uint64 authorId = 2; // recipient id
|
||||
uint64 dateSent = 3;
|
||||
bool sealedSender = 4;
|
||||
optional uint64 expireStartDate = 5; // timestamp of when expiration timer started ticking down
|
||||
optional uint64 expiresInMs = 6; // how long timer of message is (ms)
|
||||
repeated ChatItem revisions = 7; // ordered from oldest to newest
|
||||
bool sms = 8;
|
||||
optional uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down
|
||||
optional uint64 expiresInMs = 5; // how long timer of message is (ms)
|
||||
repeated ChatItem revisions = 6; // ordered from oldest to newest
|
||||
bool sms = 7;
|
||||
|
||||
oneof directionalDetails {
|
||||
IncomingMessageDetails incoming = 9;
|
||||
OutgoingMessageDetails outgoing = 10;
|
||||
DirectionlessMessageDetails directionless = 11;
|
||||
IncomingMessageDetails incoming = 8;
|
||||
OutgoingMessageDetails outgoing = 9;
|
||||
DirectionlessMessageDetails directionless = 10;
|
||||
}
|
||||
|
||||
oneof item {
|
||||
StandardMessage standardMessage = 13;
|
||||
ContactMessage contactMessage = 14;
|
||||
StickerMessage stickerMessage = 15;
|
||||
RemoteDeletedMessage remoteDeletedMessage = 16;
|
||||
ChatUpdateMessage updateMessage = 17;
|
||||
StandardMessage standardMessage = 11;
|
||||
ContactMessage contactMessage = 12;
|
||||
StickerMessage stickerMessage = 13;
|
||||
RemoteDeletedMessage remoteDeletedMessage = 14;
|
||||
ChatUpdateMessage updateMessage = 15;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,8 +506,10 @@ message IndividualCallChatUpdate {
|
||||
INCOMING_VIDEO_CALL = 2;
|
||||
OUTGOING_AUDIO_CALL = 3;
|
||||
OUTGOING_VIDEO_CALL = 4;
|
||||
MISSED_AUDIO_CALL = 5;
|
||||
MISSED_VIDEO_CALL = 6;
|
||||
MISSED_INCOMING_AUDIO_CALL = 5;
|
||||
MISSED_INCOMING_VIDEO_CALL = 6;
|
||||
UNANSWERED_OUTGOING_AUDIO_CALL = 7;
|
||||
UNANSWERED_OUTGOING_VIDEO_CALL = 8;
|
||||
}
|
||||
|
||||
Type type = 1;
|
||||
|
||||
Reference in New Issue
Block a user