Get shared backup tests working.
This commit is contained in:
parent
36640edfee
commit
7b0badef19
30 changed files with 184 additions and 108 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -587,7 +587,7 @@ class ImportExportTest {
|
|||
)
|
||||
)
|
||||
import(importData)
|
||||
val exported = BackupRepository.export()
|
||||
val exported = BackupRepository.debugExport()
|
||||
val expected = exportFrames(
|
||||
*standardFrames,
|
||||
alexa
|
||||
|
@ -1012,7 +1012,7 @@ class ImportExportTest {
|
|||
expirationNotStarted
|
||||
)
|
||||
import(importData)
|
||||
val exported = BackupRepository.export()
|
||||
val exported = BackupRepository.debugExport()
|
||||
val expected = exportFrames(
|
||||
*standardFrames,
|
||||
alice,
|
||||
|
@ -1649,7 +1649,7 @@ class ImportExportTest {
|
|||
|
||||
import(originalBackupData)
|
||||
|
||||
val generatedBackupData = BackupRepository.export()
|
||||
val generatedBackupData = BackupRepository.debugExport()
|
||||
compare(originalBackupData, generatedBackupData)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.github.difflib.DiffUtils
|
|||
import com.github.difflib.UnifiedDiffUtils
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
@ -26,7 +25,6 @@ import java.io.ByteArrayInputStream
|
|||
import java.util.UUID
|
||||
import kotlin.random.Random
|
||||
|
||||
@Ignore("Not passing yet")
|
||||
@RunWith(Parameterized::class)
|
||||
class ImportExportTestSuite(private val path: String) {
|
||||
companion object {
|
||||
|
@ -41,8 +39,10 @@ class ImportExportTestSuite(private val path: String) {
|
|||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun data(): Collection<Array<String>> {
|
||||
val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER)
|
||||
return testFiles?.map { arrayOf(it) }!!.toList()
|
||||
val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER)!!
|
||||
return testFiles
|
||||
.map { arrayOf(it) }
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,8 +65,11 @@ class ImportExportTestSuite(private val path: String) {
|
|||
assertTrue(importResult is ImportResult.Success)
|
||||
val success = importResult as ImportResult.Success
|
||||
|
||||
val generatedBackupData = BackupRepository.export(plaintext = true, currentTime = success.backupTime)
|
||||
compare(binProtoBytes, generatedBackupData)
|
||||
val generatedBackupData = BackupRepository.debugExport(plaintext = true, currentTime = success.backupTime)
|
||||
|
||||
// TODO [backup] Currently fails, need to look into it
|
||||
// assertPassesValidator(generatedBackupData)
|
||||
assertEquivalent(binProtoBytes, generatedBackupData)
|
||||
}
|
||||
|
||||
private fun import(importData: ByteArray): ImportResult {
|
||||
|
@ -78,7 +81,15 @@ class ImportExportTestSuite(private val path: String) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun compare(import: ByteArray, export: ByteArray) {
|
||||
private fun assertPassesValidator(generatedBackupData: ByteArray) {
|
||||
BackupRepository.validate(
|
||||
length = generatedBackupData.size.toLong(),
|
||||
inputStreamFactory = { ByteArrayInputStream(generatedBackupData) },
|
||||
selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, SELF_PROFILE_KEY)
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertEquivalent(import: ByteArray, export: ByteArray) {
|
||||
val importComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, import.inputStream(), import.size.toLong())
|
||||
val exportComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, export.inputStream(), import.size.toLong())
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ object MessageTableTestUtils {
|
|||
isProfileChange:${type == MessageTypes.PROFILE_CHANGE_TYPE}
|
||||
isGroupV1MigrationEvent:${type == MessageTypes.GV1_MIGRATION_TYPE}
|
||||
isChangeNumber:${type == MessageTypes.CHANGE_NUMBER_TYPE}
|
||||
isBoostRequest:${type == MessageTypes.BOOST_REQUEST_TYPE}
|
||||
isDonationChannelDonationRequest:${type == MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE}
|
||||
isThreadMerge:${type == MessageTypes.THREAD_MERGE_TYPE}
|
||||
isSmsExport:${type == MessageTypes.SMS_EXPORT_TYPE}
|
||||
isGroupV2LeaveOnly:${type and MessageTypes.GROUP_V2_LEAVE_BITS == MessageTypes.GROUP_V2_LEAVE_BITS}
|
||||
|
|
|
@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFe
|
|||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider
|
||||
import org.thoughtcrime.securesms.database.DistributionListTables
|
||||
import org.thoughtcrime.securesms.database.KeyValueDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
|
@ -249,19 +248,15 @@ object BackupRepository {
|
|||
}
|
||||
}
|
||||
|
||||
fun export(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray {
|
||||
/**
|
||||
* Exports to a blob in memory. Should only be used for testing.
|
||||
*/
|
||||
fun debugExport(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
export(outputStream = outputStream, append = { mac -> outputStream.write(mac) }, plaintext = plaintext, currentTime = currentTime)
|
||||
return outputStream.toByteArray()
|
||||
}
|
||||
|
||||
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
|
||||
val masterKey = SignalStore.svr.getOrCreateMasterKey()
|
||||
val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
|
||||
|
||||
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The time the backup was created, or null if the backup could not be read.
|
||||
*/
|
||||
|
@ -306,9 +301,6 @@ object BackupRepository {
|
|||
SignalDatabase.recipients.setProfileKey(selfId, selfData.profileKey)
|
||||
SignalDatabase.recipients.setProfileSharing(selfId, true)
|
||||
|
||||
// Add back my story after clearing data
|
||||
DistributionListTables.insertInitialDistributionListAtCreationTime(it)
|
||||
|
||||
eventTimer.emit("setup")
|
||||
val backupState = BackupState(backupKey)
|
||||
val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(backupState)
|
||||
|
@ -373,6 +365,13 @@ object BackupRepository {
|
|||
return ImportResult.Success(backupTime = header.backupTimeMs)
|
||||
}
|
||||
|
||||
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
|
||||
val masterKey = SignalStore.svr.getOrCreateMasterKey()
|
||||
val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
|
||||
|
||||
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
|
||||
}
|
||||
|
||||
fun listRemoteMediaObjects(limit: Int, cursor: String? = null): NetworkResult<ArchiveGetMediaItemsResponse> {
|
||||
val api = AppDependencies.signalServiceAccountManager.archiveApi
|
||||
val backupKey = SignalStore.svr.getOrCreateMasterKey().deriveBackupKey()
|
||||
|
|
|
@ -158,8 +158,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
|||
MessageTypes.isChangeNumber(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHANGE_NUMBER)
|
||||
}
|
||||
MessageTypes.isBoostRequest(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BOOST_REQUEST)
|
||||
MessageTypes.isReleaseChannelDonationRequest(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST)
|
||||
}
|
||||
MessageTypes.isEndSessionType(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.END_SESSION)
|
||||
|
@ -176,6 +176,12 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
|||
MessageTypes.isPaymentsRequestToActivate(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST)
|
||||
}
|
||||
MessageTypes.isUnsupportedMessageType(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE)
|
||||
}
|
||||
MessageTypes.isReportedSpam(record.type) -> {
|
||||
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.REPORTED_SPAM)
|
||||
}
|
||||
MessageTypes.isExpirationTimerUpdate(record.type) -> {
|
||||
builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn.toInt()))
|
||||
builder.expiresInMs = 0
|
||||
|
@ -265,7 +271,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
|||
expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0
|
||||
revisions = emptyList()
|
||||
sms = record.type.isSmsType()
|
||||
if (MessageTypes.isCallLog(record.type)) {
|
||||
if (record.type.isDirectionlessType()) {
|
||||
directionless = ChatItem.DirectionlessMessageDetails()
|
||||
} else if (MessageTypes.isOutgoingMessageType(record.type)) {
|
||||
outgoing = ChatItem.OutgoingMessageDetails(
|
||||
|
@ -999,6 +1005,27 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
|
|||
return MessageTypes.isOutgoingMessageType(this) || MessageTypes.isInboxType(this)
|
||||
}
|
||||
|
||||
private fun Long.isDirectionlessType(): Boolean {
|
||||
return MessageTypes.isCallLog(this) ||
|
||||
MessageTypes.isExpirationTimerUpdate(this) ||
|
||||
MessageTypes.isThreadMergeType(this) ||
|
||||
MessageTypes.isSessionSwitchoverType(this) ||
|
||||
MessageTypes.isProfileChange(this) ||
|
||||
MessageTypes.isJoinedType(this) ||
|
||||
MessageTypes.isIdentityUpdate(this) ||
|
||||
MessageTypes.isIdentityVerified(this) ||
|
||||
MessageTypes.isIdentityDefault(this) ||
|
||||
MessageTypes.isReleaseChannelDonationRequest(this) ||
|
||||
MessageTypes.isChangeNumber(this) ||
|
||||
MessageTypes.isEndSessionType(this) ||
|
||||
MessageTypes.isChatSessionRefresh(this) ||
|
||||
MessageTypes.isBadDecryptType(this) ||
|
||||
MessageTypes.isPaymentsActivated(this) ||
|
||||
MessageTypes.isPaymentsRequestToActivate(this) ||
|
||||
MessageTypes.isUnsupportedMessageType(this) ||
|
||||
MessageTypes.isReportedSpam(this)
|
||||
}
|
||||
|
||||
private fun String.e164ToLong(): Long? {
|
||||
val fixed = if (this.startsWith("+")) {
|
||||
this.substring(1)
|
||||
|
|
|
@ -624,13 +624,14 @@ class ChatItemImportInserter(
|
|||
SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT or typeWithoutBase
|
||||
SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT or typeWithoutBase
|
||||
SimpleChatUpdate.Type.CHANGE_NUMBER -> MessageTypes.CHANGE_NUMBER_TYPE
|
||||
SimpleChatUpdate.Type.BOOST_REQUEST -> MessageTypes.BOOST_REQUEST_TYPE
|
||||
SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST -> MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE
|
||||
SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT or typeWithoutBase
|
||||
SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT or typeWithoutBase
|
||||
SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE or typeWithoutBase
|
||||
SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED or typeWithoutBase
|
||||
SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST or typeWithoutBase
|
||||
SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE -> MessageTypes.UNSUPPORTED_MESSAGE_TYPE or typeWithoutBase
|
||||
SimpleChatUpdate.Type.REPORTED_SPAM -> MessageTypes.SPECIAL_TYPE_REPORTED_SPAM or typeWithoutBase
|
||||
}
|
||||
}
|
||||
updateMessage.expirationTimerChange != null -> {
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
package org.thoughtcrime.securesms.backup.v2.database
|
||||
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.CursorUtil
|
||||
import org.signal.core.util.deleteAll
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.readToList
|
||||
import org.signal.core.util.requireBoolean
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.requireNonNullString
|
||||
import org.signal.core.util.requireObject
|
||||
import org.signal.core.util.select
|
||||
import org.signal.core.util.withinTransaction
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.DistributionList
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem
|
||||
|
@ -36,7 +37,6 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
|
|||
val records = readableDatabase
|
||||
.select()
|
||||
.from(DistributionListTables.ListTable.TABLE_NAME)
|
||||
.where(DistributionListTables.ListTable.IS_NOT_DELETED)
|
||||
.run()
|
||||
.readToList { cursor ->
|
||||
val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID))
|
||||
|
@ -48,11 +48,11 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
|
|||
id = id,
|
||||
name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME),
|
||||
distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)),
|
||||
allowsReplies = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.ALLOWS_REPLIES),
|
||||
allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES),
|
||||
rawMembers = getRawMembers(id, privacyMode),
|
||||
members = getMembers(id),
|
||||
deletedAtTimestamp = 0L,
|
||||
isUnknown = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.IS_UNKNOWN),
|
||||
members = getMembersForBackup(id),
|
||||
deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP),
|
||||
isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN),
|
||||
privacyMode = privacyMode
|
||||
)
|
||||
)
|
||||
|
@ -82,7 +82,37 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
|
|||
}
|
||||
}
|
||||
|
||||
fun DistributionListTables.getMembersForBackup(id: DistributionListId): List<RecipientId> {
|
||||
lateinit var privacyMode: DistributionListPrivacyMode
|
||||
lateinit var rawMembers: List<RecipientId>
|
||||
|
||||
readableDatabase.withinTransaction {
|
||||
privacyMode = getPrivacyMode(id)
|
||||
rawMembers = getRawMembers(id, privacyMode)
|
||||
}
|
||||
|
||||
return when (privacyMode) {
|
||||
DistributionListPrivacyMode.ALL -> emptyList()
|
||||
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
|
||||
DistributionListPrivacyMode.ALL_EXCEPT -> rawMembers
|
||||
}
|
||||
}
|
||||
|
||||
fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, backupState: BackupState): RecipientId? {
|
||||
if (dlistItem.deletionTimestamp != null && dlistItem.deletionTimestamp > 0) {
|
||||
val dlistId = createList(
|
||||
name = "",
|
||||
members = emptyList(),
|
||||
distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)),
|
||||
allowsReplies = false,
|
||||
deletionTimestamp = dlistItem.deletionTimestamp,
|
||||
storageId = null,
|
||||
privacyMode = DistributionListPrivacyMode.ONLY_WITH
|
||||
)!!
|
||||
|
||||
return SignalDatabase.distributionLists.getRecipientId(dlistId)!!
|
||||
}
|
||||
|
||||
val dlist = dlistItem.distributionList ?: return null
|
||||
val members: List<RecipientId> = dlist.memberRecipientIds
|
||||
.mapNotNull { backupState.backupToLocalRecipientId[it] }
|
||||
|
@ -94,32 +124,22 @@ fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, ba
|
|||
val distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId))
|
||||
val privacyMode = dlist.privacyMode.toLocalPrivacyMode()
|
||||
|
||||
val dlistId = if (distributionId == DistributionId.MY_STORY) {
|
||||
setPrivacyMode(DistributionListId.MY_STORY, privacyMode)
|
||||
members.forEach { addMemberToList(DistributionListId.MY_STORY, privacyMode, it) }
|
||||
setAllowsReplies(DistributionListId.MY_STORY, dlist.allowReplies)
|
||||
DistributionListId.MY_STORY
|
||||
} else {
|
||||
createList(
|
||||
name = dlist.name,
|
||||
members = members,
|
||||
distributionId = distributionId,
|
||||
allowsReplies = dlist.allowReplies,
|
||||
deletionTimestamp = dlistItem.deletionTimestamp ?: 0,
|
||||
storageId = null,
|
||||
privacyMode = privacyMode
|
||||
)!!
|
||||
}
|
||||
val dlistId = createList(
|
||||
name = dlist.name,
|
||||
members = members,
|
||||
distributionId = distributionId,
|
||||
allowsReplies = dlist.allowReplies,
|
||||
deletionTimestamp = dlistItem.deletionTimestamp ?: 0,
|
||||
storageId = null,
|
||||
privacyMode = privacyMode
|
||||
)!!
|
||||
|
||||
return SignalDatabase.distributionLists.getRecipientId(dlistId)!!
|
||||
}
|
||||
|
||||
fun DistributionListTables.clearAllDataForBackupRestore() {
|
||||
writableDatabase
|
||||
.deleteAll(DistributionListTables.ListTable.TABLE_NAME)
|
||||
|
||||
writableDatabase
|
||||
.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME)
|
||||
writableDatabase.deleteAll(DistributionListTables.ListTable.TABLE_NAME)
|
||||
writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME)
|
||||
}
|
||||
|
||||
private fun DistributionListPrivacyMode.toBackupPrivacyMode(): BackupDistributionList.PrivacyMode {
|
||||
|
|
|
@ -45,6 +45,7 @@ fun ThreadTable.getThreadsForBackup(): ChatIterator {
|
|||
${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}
|
||||
FROM ${ThreadTable.TABLE_NAME}
|
||||
LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID}
|
||||
WHERE ${ThreadTable.ACTIVE} = 1
|
||||
"""
|
||||
val cursor = readableDatabase.query(query)
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|||
import org.whispersystems.signalservice.api.push.UsernameLinkComponents
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import org.whispersystems.signalservice.api.util.toByteArray
|
||||
import java.util.Currency
|
||||
|
||||
object AccountDataProcessor {
|
||||
|
@ -48,6 +49,15 @@ object AccountDataProcessor {
|
|||
familyName = selfRecord.signalProfileName.familyName,
|
||||
avatarUrlPath = selfRecord.signalProfileAvatar ?: "",
|
||||
username = selfRecord.username,
|
||||
usernameLink = if (signalStore.accountValues.usernameLink != null) {
|
||||
AccountData.UsernameLink(
|
||||
entropy = signalStore.accountValues.usernameLink?.entropy?.toByteString() ?: EMPTY,
|
||||
serverId = signalStore.accountValues.usernameLink?.serverId?.toByteArray()?.toByteString() ?: EMPTY,
|
||||
color = signalStore.miscValues.usernameQrCodeColorScheme.toBackupUsernameColor() ?: AccountData.UsernameLink.Color.BLUE
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
accountSettings = AccountData.AccountSettings(
|
||||
storyViewReceiptsEnabled = signalStore.storyValues.viewedReceiptsEnabled,
|
||||
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context),
|
||||
|
@ -73,12 +83,6 @@ object AccountDataProcessor {
|
|||
)
|
||||
}
|
||||
|
||||
private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData {
|
||||
val subscriberId = subscriberId.bytes.toByteString()
|
||||
val currencyCode = currency.currencyCode
|
||||
return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled)
|
||||
}
|
||||
|
||||
fun import(accountData: AccountData, selfId: RecipientId) {
|
||||
SignalDatabase.recipients.restoreSelfFromBackup(accountData, selfId)
|
||||
|
||||
|
@ -181,4 +185,23 @@ object AccountDataProcessor {
|
|||
else -> UsernameQrCodeColorScheme.Blue
|
||||
}
|
||||
}
|
||||
|
||||
private fun UsernameQrCodeColorScheme.toBackupUsernameColor(): AccountData.UsernameLink.Color {
|
||||
return when (this) {
|
||||
UsernameQrCodeColorScheme.Blue -> AccountData.UsernameLink.Color.BLUE
|
||||
UsernameQrCodeColorScheme.White -> AccountData.UsernameLink.Color.WHITE
|
||||
UsernameQrCodeColorScheme.Grey -> AccountData.UsernameLink.Color.GREY
|
||||
UsernameQrCodeColorScheme.Tan -> AccountData.UsernameLink.Color.OLIVE
|
||||
UsernameQrCodeColorScheme.Green -> AccountData.UsernameLink.Color.GREEN
|
||||
UsernameQrCodeColorScheme.Orange -> AccountData.UsernameLink.Color.ORANGE
|
||||
UsernameQrCodeColorScheme.Pink -> AccountData.UsernameLink.Color.PINK
|
||||
UsernameQrCodeColorScheme.Purple -> AccountData.UsernameLink.Color.PURPLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData {
|
||||
val subscriberId = subscriberId.bytes.toByteString()
|
||||
val currencyCode = currency.currencyCode
|
||||
return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ object RecipientBackupProcessor {
|
|||
val selfId = db.recipientTable.getByAci(signalStore.accountValues.aci!!).get().toLong()
|
||||
val releaseChannelId = signalStore.releaseChannelValues.releaseChannelRecipientId
|
||||
if (releaseChannelId != null) {
|
||||
state.recipientIds.add(releaseChannelId.toLong())
|
||||
emitter.emit(
|
||||
Frame(
|
||||
recipient = BackupRecipient(
|
||||
|
|
|
@ -57,7 +57,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
|
|||
_state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS)
|
||||
val plaintext = _state.value.plaintext
|
||||
|
||||
disposables += Single.fromCallable { BackupRepository.export(plaintext = plaintext) }
|
||||
disposables += Single.fromCallable { BackupRepository.debugExport(plaintext = plaintext) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { data ->
|
||||
|
|
|
@ -186,7 +186,7 @@ public final class ConversationUpdateItem extends FrameLayout
|
|||
shouldCollapse(messageRecord, nextMessageRecord),
|
||||
hasWallpaper);
|
||||
|
||||
presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isBoostRequest());
|
||||
presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isReleaseChannelDonationRequest());
|
||||
|
||||
updateSelectedState();
|
||||
}
|
||||
|
@ -538,7 +538,7 @@ public final class ConversationUpdateItem extends FrameLayout
|
|||
eventListener.onBlockJoinRequest(conversationMessage.getMessageRecord().getFromRecipient());
|
||||
}
|
||||
});
|
||||
} else if (conversationMessage.getMessageRecord().isBoostRequest()) {
|
||||
} else if (conversationMessage.getMessageRecord().isReleaseChannelDonationRequest()) {
|
||||
actionButton.setVisibility(VISIBLE);
|
||||
actionButton.setOnClickListener(v -> {
|
||||
if (batchSelected.isEmpty() && eventListener != null) {
|
||||
|
|
|
@ -650,7 +650,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_could_not_be_processed), defaultTint);
|
||||
} else if (MessageTypes.isProfileChange(thread.getType())) {
|
||||
return emphasisAdded(context, "", defaultTint);
|
||||
} else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isBoostRequest(thread.getType())) {
|
||||
} else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isReleaseChannelDonationRequest(thread.getType())) {
|
||||
return emphasisAdded(context, "", defaultTint);
|
||||
} else if (MessageTypes.isBadDecryptType(thread.getType())) {
|
||||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint);
|
||||
|
|
|
@ -411,11 +411,8 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
|
|||
}
|
||||
|
||||
return when (privacyMode) {
|
||||
DistributionListPrivacyMode.ALL -> {
|
||||
SignalDatabase.recipients
|
||||
.getSignalContacts(false)!!
|
||||
.readToList { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
|
||||
}
|
||||
DistributionListPrivacyMode.ALL -> emptyList()
|
||||
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
|
||||
DistributionListPrivacyMode.ALL_EXCEPT -> {
|
||||
SignalDatabase.recipients
|
||||
.getSignalContacts(false)!!
|
||||
|
@ -424,7 +421,6 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
|
|||
mapper = { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
|
||||
)
|
||||
}
|
||||
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,7 +473,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
|
|||
}
|
||||
}
|
||||
|
||||
private fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode {
|
||||
fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode {
|
||||
return readableDatabase
|
||||
.select(ListTable.PRIVACY_MODE)
|
||||
.from(ListTable.TABLE_NAME)
|
||||
|
|
|
@ -420,7 +420,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
${MessageTypes.PROFILE_CHANGE_TYPE},
|
||||
${MessageTypes.GV1_MIGRATION_TYPE},
|
||||
${MessageTypes.CHANGE_NUMBER_TYPE},
|
||||
${MessageTypes.BOOST_REQUEST_TYPE},
|
||||
${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE},
|
||||
${MessageTypes.SMS_EXPORT_TYPE}
|
||||
)
|
||||
ORDER BY $DATE_RECEIVED DESC LIMIT 1
|
||||
|
@ -1237,7 +1237,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
DATE_RECEIVED to System.currentTimeMillis(),
|
||||
DATE_SENT to System.currentTimeMillis(),
|
||||
READ to 1,
|
||||
TYPE to MessageTypes.BOOST_REQUEST_TYPE,
|
||||
TYPE to MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE,
|
||||
THREAD_ID to threadId,
|
||||
BODY to null
|
||||
)
|
||||
|
@ -1882,7 +1882,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
$TYPE != ${MessageTypes.PROFILE_CHANGE_TYPE} AND
|
||||
$TYPE != ${MessageTypes.CHANGE_NUMBER_TYPE} AND
|
||||
$TYPE != ${MessageTypes.SMS_EXPORT_TYPE} AND
|
||||
$TYPE != ${MessageTypes.BOOST_REQUEST_TYPE} AND
|
||||
$TYPE != ${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE} AND
|
||||
$TYPE & ${MessageTypes.GROUP_V2_LEAVE_BITS} != ${MessageTypes.GROUP_V2_LEAVE_BITS} AND
|
||||
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND
|
||||
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED}
|
||||
|
|
|
@ -33,24 +33,24 @@ public interface MessageTypes {
|
|||
// Base Types
|
||||
long BASE_TYPE_MASK = 0x1F;
|
||||
|
||||
long INCOMING_AUDIO_CALL_TYPE = 1;
|
||||
long OUTGOING_AUDIO_CALL_TYPE = 2;
|
||||
long MISSED_AUDIO_CALL_TYPE = 3;
|
||||
long JOINED_TYPE = 4;
|
||||
long UNSUPPORTED_MESSAGE_TYPE = 5;
|
||||
long INVALID_MESSAGE_TYPE = 6;
|
||||
long PROFILE_CHANGE_TYPE = 7;
|
||||
long MISSED_VIDEO_CALL_TYPE = 8;
|
||||
long GV1_MIGRATION_TYPE = 9;
|
||||
long INCOMING_VIDEO_CALL_TYPE = 10;
|
||||
long OUTGOING_VIDEO_CALL_TYPE = 11;
|
||||
long GROUP_CALL_TYPE = 12;
|
||||
long BAD_DECRYPT_TYPE = 13;
|
||||
long CHANGE_NUMBER_TYPE = 14;
|
||||
long BOOST_REQUEST_TYPE = 15;
|
||||
long THREAD_MERGE_TYPE = 16;
|
||||
long SMS_EXPORT_TYPE = 17;
|
||||
long SESSION_SWITCHOVER_TYPE = 18;
|
||||
long INCOMING_AUDIO_CALL_TYPE = 1;
|
||||
long OUTGOING_AUDIO_CALL_TYPE = 2;
|
||||
long MISSED_AUDIO_CALL_TYPE = 3;
|
||||
long JOINED_TYPE = 4;
|
||||
long UNSUPPORTED_MESSAGE_TYPE = 5;
|
||||
long INVALID_MESSAGE_TYPE = 6;
|
||||
long PROFILE_CHANGE_TYPE = 7;
|
||||
long MISSED_VIDEO_CALL_TYPE = 8;
|
||||
long GV1_MIGRATION_TYPE = 9;
|
||||
long INCOMING_VIDEO_CALL_TYPE = 10;
|
||||
long OUTGOING_VIDEO_CALL_TYPE = 11;
|
||||
long GROUP_CALL_TYPE = 12;
|
||||
long BAD_DECRYPT_TYPE = 13;
|
||||
long CHANGE_NUMBER_TYPE = 14;
|
||||
long RELEASE_CHANNEL_DONATION_REQUEST_TYPE = 15;
|
||||
long THREAD_MERGE_TYPE = 16;
|
||||
long SMS_EXPORT_TYPE = 17;
|
||||
long SESSION_SWITCHOVER_TYPE = 18;
|
||||
|
||||
long BASE_INBOX_TYPE = 20;
|
||||
long BASE_OUTBOX_TYPE = 21;
|
||||
|
@ -59,7 +59,7 @@ public interface MessageTypes {
|
|||
long BASE_SENT_FAILED_TYPE = 24;
|
||||
long BASE_PENDING_SECURE_SMS_FALLBACK = 25;
|
||||
long BASE_PENDING_INSECURE_SMS_FALLBACK = 26;
|
||||
long BASE_DRAFT_TYPE = 27;
|
||||
long BASE_DRAFT_TYPE = 27;
|
||||
|
||||
long[] OUTGOING_MESSAGE_TYPES = { BASE_OUTBOX_TYPE, BASE_SENT_TYPE,
|
||||
BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE,
|
||||
|
@ -351,8 +351,8 @@ public interface MessageTypes {
|
|||
return type == CHANGE_NUMBER_TYPE;
|
||||
}
|
||||
|
||||
static boolean isBoostRequest(long type) {
|
||||
return type == BOOST_REQUEST_TYPE;
|
||||
static boolean isReleaseChannelDonationRequest(long type) {
|
||||
return type == RELEASE_CHANNEL_DONATION_REQUEST_TYPE;
|
||||
}
|
||||
|
||||
static boolean isSmsExport(long type) {
|
||||
|
|
|
@ -1788,10 +1788,6 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
|||
deactivateThread(query = null)
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.deactivateThread(threadId: Long) {
|
||||
deactivateThread(SqlUtil.Query("$ID = ?", SqlUtil.buildArgs(threadId)))
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.deactivateThread(query: SqlUtil.Query?) {
|
||||
val contentValues = contentValuesOf(
|
||||
DATE to 0,
|
||||
|
@ -1820,13 +1816,13 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
|||
)
|
||||
|
||||
if (query != null) {
|
||||
writableDatabase
|
||||
this
|
||||
.update(TABLE_NAME)
|
||||
.values(contentValues)
|
||||
.where(query.where, query.whereArgs)
|
||||
.run()
|
||||
} else {
|
||||
writableDatabase
|
||||
this
|
||||
.updateAll(TABLE_NAME)
|
||||
.values(contentValues)
|
||||
.run()
|
||||
|
@ -1979,7 +1975,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
|||
return MessageTypes.isProfileChange(type) ||
|
||||
MessageTypes.isGroupV1MigrationEvent(type) ||
|
||||
MessageTypes.isChangeNumber(type) ||
|
||||
MessageTypes.isBoostRequest(type) ||
|
||||
MessageTypes.isReleaseChannelDonationRequest(type) ||
|
||||
MessageTypes.isGroupV2LeaveOnly(type) ||
|
||||
MessageTypes.isThreadMergeType(type)
|
||||
}
|
||||
|
|
|
@ -196,8 +196,8 @@ public abstract class DisplayRecord {
|
|||
return MessageTypes.isChangeNumber(type);
|
||||
}
|
||||
|
||||
public boolean isBoostRequest() {
|
||||
return MessageTypes.isBoostRequest(type);
|
||||
public boolean isReleaseChannelDonationRequest() {
|
||||
return MessageTypes.isReleaseChannelDonationRequest(type);
|
||||
}
|
||||
|
||||
public int getDeliveryStatus() {
|
||||
|
|
|
@ -232,7 +232,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return getProfileChangeDescription(context);
|
||||
} else if (isChangeNumber()) {
|
||||
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_changed_their_phone_number, r.getDisplayName(context)), R.drawable.ic_phone_16);
|
||||
} else if (isBoostRequest()) {
|
||||
} else if (isReleaseChannelDonationRequest()) {
|
||||
return staticUpdateDescription(context.getString(R.string.MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation), 0);
|
||||
} else if (isEndSession()) {
|
||||
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), R.drawable.ic_update_info_16);
|
||||
|
@ -684,7 +684,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
||||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
||||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
|
||||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
|
||||
isChangeNumber() || isReleaseChannelDonationRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
|
||||
isPaymentsRequestToActivate() || isPaymentsActivated() || isReportedSpam() || isMessageRequestAccepted();
|
||||
}
|
||||
|
||||
|
|
|
@ -740,13 +740,14 @@ message SimpleChatUpdate {
|
|||
IDENTITY_VERIFIED = 3;
|
||||
IDENTITY_DEFAULT = 4; // marking as unverified
|
||||
CHANGE_NUMBER = 5;
|
||||
BOOST_REQUEST = 6;
|
||||
RELEASE_CHANNEL_DONATION_REQUEST = 6;
|
||||
END_SESSION = 7;
|
||||
CHAT_SESSION_REFRESH = 8;
|
||||
BAD_DECRYPT = 9;
|
||||
PAYMENTS_ACTIVATED = 10;
|
||||
PAYMENT_ACTIVATION_REQUEST = 11;
|
||||
UNSUPPORTED_PROTOCOL_MESSAGE = 12;
|
||||
REPORTED_SPAM = 13;
|
||||
}
|
||||
|
||||
Type type = 1;
|
||||
|
|
|
@ -81,7 +81,7 @@ class SmsDatabaseTest {
|
|||
TestSms.insert(db, type = MessageTypes.CHANGE_NUMBER_TYPE)
|
||||
assertFalse(messageTable.hasMeaningfulMessage(1))
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.BOOST_REQUEST_TYPE)
|
||||
TestSms.insert(db, type = MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE)
|
||||
assertFalse(messageTable.hasMeaningfulMessage(1))
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.SMS_EXPORT_TYPE)
|
||||
|
|
Loading…
Add table
Reference in a new issue