diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt index bf942d0dc8..455ec15a83 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.ChatItem import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage import org.thoughtcrime.securesms.backup.v2.proto.Contact import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.FilePointer import org.thoughtcrime.securesms.backup.v2.proto.Frame @@ -85,9 +86,11 @@ class ImportExportTest { givenName = "Peter", familyName = "Parker", avatarUrlPath = "https://example.com/", - subscriberId = SubscriberId.generate().bytes.toByteString(), - subscriberCurrencyCode = "USD", - subscriptionManuallyCancelled = true, + donationSubscriberData = AccountData.SubscriberData( + subscriberId = SubscriberId.generate().bytes.toByteString(), + currencyCode = "USD", + manuallyCancelled = true + ), accountSettings = AccountData.AccountSettings( readReceipts = true, sealedSenderIndicators = true, @@ -116,9 +119,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = false, - hidden = false, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -171,9 +173,8 @@ class ImportExportTest { username = "rec$i.01", e164 = 14125550000 + i, blocked = false, - hidden = false, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Test", @@ -237,9 +238,8 @@ class ImportExportTest { username = if (random.trueWithProbability(0.2f)) "rec$i.01" else null, e164 = 14125550000 + i, blocked = random.trueWithProbability(0.1f), - hidden = random.trueWithProbability(0.1f), - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = if (random.trueWithProbability(0.1f)) Contact.Visibility.HIDDEN else Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = random.trueWithProbability(0.9f), profileGivenName = "Test", @@ -390,9 +390,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = true, - hidden = true, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -408,9 +407,8 @@ class ImportExportTest { username = null, e164 = 141255501235, blocked = true, - hidden = true, - registered = Contact.Registered.NOT_REGISTERED, - unregisteredTimestamp = 1234568927398L, + visibility = Contact.Visibility.HIDDEN, + notRegistered = Contact.NotRegistered(unregisteredTimestamp = 1234568927398L), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = false, profileGivenName = "Peter", @@ -470,9 +468,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = true, - hidden = true, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.HIDDEN, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -488,9 +485,8 @@ class ImportExportTest { username = null, e164 = 141255501235, blocked = true, - hidden = true, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.HIDDEN, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Peter", @@ -506,9 +502,8 @@ class ImportExportTest { username = null, e164 = 141255501236, blocked = true, - hidden = true, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.HIDDEN, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Father", @@ -518,13 +513,14 @@ class ImportExportTest { ), Recipient( id = 6, - distributionList = DistributionList( - name = "Kim Family", + distributionList = DistributionListItem( distributionId = DistributionId.create().asUuid().toByteArray().toByteString(), - allowReplies = true, - deletionTimestamp = 0L, - privacyMode = DistributionList.PrivacyMode.ONLY_WITH, - memberRecipientIds = listOf(3, 4, 5) + distributionList = DistributionList( + name = "Kim Family", + allowReplies = true, + privacyMode = DistributionList.PrivacyMode.ONLY_WITH, + memberRecipientIds = listOf(3, 4, 5) + ) ) ) ) @@ -540,9 +536,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = true, - hidden = true, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.HIDDEN, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -555,13 +550,9 @@ class ImportExportTest { alexa, Recipient( id = 6, - distributionList = DistributionList( - name = "Deleted list", + distributionList = DistributionListItem( distributionId = DistributionId.create().asUuid().toByteArray().toByteString(), - allowReplies = true, - deletionTimestamp = 12345L, - privacyMode = DistributionList.PrivacyMode.ONLY_WITH, - memberRecipientIds = listOf(3) + deletionTimestamp = 12345L ) ) ) @@ -587,9 +578,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = false, - hidden = false, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -614,8 +604,7 @@ class ImportExportTest { expirationTimerMs = 1.days.inWholeMilliseconds, muteUntilMs = System.currentTimeMillis(), markedUnread = true, - dontNotifyForMentionsIfMuted = true, - wallpaper = null + dontNotifyForMentionsIfMuted = true ) ) } @@ -689,9 +678,8 @@ class ImportExportTest { username = "cool.01", e164 = 141255501234, blocked = false, - hidden = false, - registered = Contact.Registered.REGISTERED, - unregisteredTimestamp = 0L, + visibility = Contact.Visibility.VISIBLE, + registered = Contact.Registered(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileSharing = true, profileGivenName = "Alexa", @@ -716,8 +704,7 @@ class ImportExportTest { expirationTimerMs = 1.days.inWholeMilliseconds, muteUntilMs = System.currentTimeMillis(), markedUnread = true, - dontNotifyForMentionsIfMuted = true, - wallpaper = null + dontNotifyForMentionsIfMuted = true ), *individualCalls.toArray() ) @@ -1361,8 +1348,7 @@ class ImportExportTest { expirationTimerMs = 0, muteUntilMs = 0, markedUnread = false, - dontNotifyForMentionsIfMuted = false, - wallpaper = null + dontNotifyForMentionsIfMuted = false ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt index 8e2060df94..defe7c8cec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt @@ -38,7 +38,7 @@ fun CallTable.restoreCallLogFromBackup(call: AdHocCall, backupState: BackupState CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), CallTable.EVENT to CallTable.Event.serialize(event), - CallTable.TIMESTAMP to call.startedCallTimestamp + CallTable.TIMESTAMP to call.callTimestamp ) writableDatabase.insert(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values) @@ -64,7 +64,7 @@ class CallLogIterator(private val cursor: Cursor) : Iterator, Closea callId = callId, recipientId = cursor.requireLong(CallTable.PEER), state = AdHocCall.State.GENERIC, - startedCallTimestamp = cursor.requireLong(CallTable.TIMESTAMP) + callTimestamp = cursor.requireLong(CallTable.TIMESTAMP) ) } 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 3ee23698a9..06cfce41ff 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 @@ -538,6 +538,7 @@ class ChatItemImportInserter( 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 } } updateMessage.expirationTimerChange != null -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt index 596b868211..3786ae4e3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt @@ -15,6 +15,8 @@ import org.signal.core.util.requireNonNullString import org.signal.core.util.requireObject import org.signal.core.util.select import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem import org.thoughtcrime.securesms.database.DistributionListTables import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.DistributionListId @@ -60,19 +62,28 @@ fun DistributionListTables.getAllForBackup(): List { .map { recipient -> BackupRecipient( id = recipient.id.toLong(), - distributionList = BackupDistributionList( - name = recipient.record.name, - distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), - allowReplies = recipient.record.allowsReplies, - deletionTimestamp = recipient.record.deletedAtTimestamp, - privacyMode = recipient.record.privacyMode.toBackupPrivacyMode(), - memberRecipientIds = recipient.record.members.map { it.toLong() } - ) + distributionList = if (recipient.record.deletedAtTimestamp != 0L) { + DistributionListItem( + distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), + deletionTimestamp = recipient.record.deletedAtTimestamp + ) + } else { + DistributionListItem( + distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), + distributionList = DistributionList( + name = recipient.record.name, + allowReplies = recipient.record.allowsReplies, + privacyMode = recipient.record.privacyMode.toBackupPrivacyMode(), + memberRecipientIds = recipient.record.members.map { it.toLong() } + ) + ) + } ) } } -fun DistributionListTables.restoreFromBackup(dlist: BackupDistributionList, backupState: BackupState): RecipientId { +fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, backupState: BackupState): RecipientId? { + val dlist = dlistItem.distributionList ?: return null val members: List = dlist.memberRecipientIds .mapNotNull { backupState.backupToLocalRecipientId[it] } @@ -83,9 +94,9 @@ fun DistributionListTables.restoreFromBackup(dlist: BackupDistributionList, back val dlistId = this.createList( name = dlist.name, members = members, - distributionId = DistributionId.from(UuidUtil.fromByteString(dlist.distributionId)), + distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)), allowsReplies = dlist.allowReplies, - deletionTimestamp = dlist.deletionTimestamp, + deletionTimestamp = dlistItem.deletionTimestamp ?: 0, storageId = null, privacyMode = dlist.privacyMode.toLocalPrivacyMode() )!! diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt index baf611721c..3434b9c821 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.database import android.content.ContentValues import android.database.Cursor +import androidx.core.content.contentValuesOf import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64 import org.signal.core.util.SqlUtil @@ -175,23 +176,30 @@ fun RecipientTable.restoreContactFromBackup(contact: Contact): RecipientId { ) val profileKey = contact.profileKey?.toByteArray() + val values = contentValuesOf( + RecipientTable.BLOCKED to contact.blocked, + RecipientTable.HIDDEN to (contact.visibility == Contact.Visibility.HIDDEN), + RecipientTable.TYPE to RecipientTable.RecipientType.INDIVIDUAL.id, + RecipientTable.PROFILE_FAMILY_NAME to contact.profileFamilyName.nullIfBlank(), + RecipientTable.PROFILE_GIVEN_NAME to contact.profileGivenName.nullIfBlank(), + RecipientTable.PROFILE_JOINED_NAME to ProfileName.fromParts(contact.profileGivenName.nullIfBlank(), contact.profileFamilyName.nullIfBlank()).toString().nullIfBlank(), + RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey), + RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(), + RecipientTable.USERNAME to contact.username, + RecipientTable.EXTRAS to contact.toLocalExtras().encode() + ) + + if (contact.registered != null) { + values.put(RecipientTable.UNREGISTERED_TIMESTAMP, 0L) + values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) + } else if (contact.notRegistered != null) { + values.put(RecipientTable.UNREGISTERED_TIMESTAMP, contact.notRegistered.unregisteredTimestamp) + values.put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.NOT_REGISTERED.id) + } writableDatabase .update(RecipientTable.TABLE_NAME) - .values( - RecipientTable.BLOCKED to contact.blocked, - RecipientTable.HIDDEN to contact.hidden, - RecipientTable.TYPE to RecipientTable.RecipientType.INDIVIDUAL.id, - RecipientTable.PROFILE_FAMILY_NAME to contact.profileFamilyName.nullIfBlank(), - RecipientTable.PROFILE_GIVEN_NAME to contact.profileGivenName.nullIfBlank(), - RecipientTable.PROFILE_JOINED_NAME to ProfileName.fromParts(contact.profileGivenName.nullIfBlank(), contact.profileFamilyName.nullIfBlank()).toString().nullIfBlank(), - RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey), - RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(), - RecipientTable.REGISTERED to contact.registered.toLocalRegisteredState().id, - RecipientTable.USERNAME to contact.username, - RecipientTable.UNREGISTERED_TIMESTAMP to contact.unregisteredTimestamp, - RecipientTable.EXTRAS to contact.toLocalExtras().encode() - ) + .values(values) .where("${RecipientTable.ID} = ?", id) .run() @@ -418,23 +426,28 @@ class BackupContactIterator(private val cursor: Cursor, private val selfId: Long return null } + val contactBuilder = Contact.Builder() + .aci(aci?.rawUuid?.toByteArray()?.toByteString()) + .pni(pni?.rawUuid?.toByteArray()?.toByteString()) + .username(cursor.requireString(RecipientTable.USERNAME)) + .e164(cursor.requireString(RecipientTable.E164)?.e164ToLong()) + .blocked(cursor.requireBoolean(RecipientTable.BLOCKED)) + .visibility(if (cursor.requireBoolean(RecipientTable.HIDDEN)) Contact.Visibility.HIDDEN else Contact.Visibility.VISIBLE) + .profileKey(if (profileKey != null) Base64.decode(profileKey).toByteString() else null) + .profileSharing(cursor.requireBoolean(RecipientTable.PROFILE_SHARING)) + .profileGivenName(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME).nullIfBlank()) + .profileFamilyName(cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME).nullIfBlank()) + .hideStory(extras?.hideStory() ?: false) + + if (registeredState == RecipientTable.RegisteredState.REGISTERED) { + contactBuilder.registered = Contact.Registered() + } else { + contactBuilder.notRegistered = Contact.NotRegistered(unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP)) + } + return BackupRecipient( id = id, - contact = Contact( - aci = aci?.rawUuid?.toByteArray()?.toByteString(), - pni = pni?.rawUuid?.toByteArray()?.toByteString(), - username = cursor.requireString(RecipientTable.USERNAME), - e164 = cursor.requireString(RecipientTable.E164)?.e164ToLong(), - blocked = cursor.requireBoolean(RecipientTable.BLOCKED), - hidden = cursor.requireBoolean(RecipientTable.HIDDEN), - registered = registeredState.toContactRegisteredState(), - unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP), - profileKey = if (profileKey != null) Base64.decode(profileKey).toByteString() else null, - profileSharing = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), - profileGivenName = cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME).nullIfBlank(), - profileFamilyName = cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME).nullIfBlank(), - hideStory = extras?.hideStory() ?: false - ) + contact = contactBuilder.build() ) } @@ -489,22 +502,6 @@ private fun String.e164ToLong(): Long? { return fixed.toLongOrNull() } -private fun RecipientTable.RegisteredState.toContactRegisteredState(): Contact.Registered { - return when (this) { - RecipientTable.RegisteredState.REGISTERED -> Contact.Registered.REGISTERED - RecipientTable.RegisteredState.NOT_REGISTERED -> Contact.Registered.NOT_REGISTERED - RecipientTable.RegisteredState.UNKNOWN -> Contact.Registered.UNKNOWN - } -} - -private fun Contact.Registered.toLocalRegisteredState(): RecipientTable.RegisteredState { - return when (this) { - Contact.Registered.REGISTERED -> RecipientTable.RegisteredState.REGISTERED - Contact.Registered.NOT_REGISTERED -> RecipientTable.RegisteredState.NOT_REGISTERED - Contact.Registered.UNKNOWN -> RecipientTable.RegisteredState.UNKNOWN - } -} - private fun GroupTable.ShowAsStoryState.toGroupStorySendMode(): Group.StorySendMode { return when (this) { GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt index 8333e296f3..2f0eb126a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt @@ -49,10 +49,7 @@ object AccountDataProcessor { givenName = self.profileName.givenName, familyName = self.profileName.familyName, avatarUrlPath = self.profileAvatar ?: "", - subscriptionManuallyCancelled = InAppPaymentsRepository.isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION), username = self.username.getOrNull(), - subscriberId = subscriber?.subscriberId?.bytes?.toByteString() ?: defaultAccountRecord.subscriberId, - subscriberCurrencyCode = subscriber?.currency?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode, accountSettings = AccountData.AccountSettings( storyViewReceiptsEnabled = SignalStore.storyValues().viewedReceiptsEnabled, typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context), @@ -71,6 +68,11 @@ object AccountDataProcessor { displayBadgesOnProfile = SignalStore.donationsValues().getDisplayBadgesOnProfile(), hasSeenGroupStoryEducationSheet = SignalStore.storyValues().userHasSeenGroupStoryEducationSheet, hasCompletedUsernameOnboarding = SignalStore.uiHints().hasCompletedUsernameOnboarding() + ), + donationSubscriberData = AccountData.SubscriberData( + subscriberId = subscriber?.subscriberId?.bytes?.toByteString() ?: defaultAccountRecord.subscriberId, + currencyCode = subscriber?.currency?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode, + manuallyCancelled = InAppPaymentsRepository.isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION) ) ) ) @@ -103,23 +105,25 @@ object AccountDataProcessor { SignalStore.storyValues().userHasSeenGroupStoryEducationSheet = settings.hasSeenGroupStoryEducationSheet SignalStore.storyValues().viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts - if (accountData.subscriberId.size > 0) { - val remoteSubscriberId = SubscriberId.fromBytes(accountData.subscriberId.toByteArray()) - val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) + if (accountData.donationSubscriberData != null) { + if (accountData.donationSubscriberData.subscriberId.size > 0) { + val remoteSubscriberId = SubscriberId.fromBytes(accountData.donationSubscriberData.subscriberId.toByteArray()) + val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) - val subscriber = InAppPaymentSubscriberRecord( - remoteSubscriberId, - Currency.getInstance(accountData.subscriberCurrencyCode), - InAppPaymentSubscriberRecord.Type.DONATION, - localSubscriber?.requiresCancel ?: false, - InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION) - ) + val subscriber = InAppPaymentSubscriberRecord( + remoteSubscriberId, + Currency.getInstance(accountData.donationSubscriberData.currencyCode), + InAppPaymentSubscriberRecord.Type.DONATION, + localSubscriber?.requiresCancel ?: accountData.donationSubscriberData.manuallyCancelled, + InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION) + ) - InAppPaymentsRepository.setSubscriber(subscriber) - } + InAppPaymentsRepository.setSubscriber(subscriber) + } - if (accountData.subscriptionManuallyCancelled) { - SignalStore.donationsValues().updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION) + if (accountData.donationSubscriberData.manuallyCancelled) { + SignalStore.donationsValues().updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION) + } } if (accountData.avatarUrlPath.isNotEmpty()) { diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index 1893a075a4..2e7a0b5c8d 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -74,6 +74,13 @@ message AccountData { bool hasSeenGroupStoryEducationSheet = 15; bool hasCompletedUsernameOnboarding = 16; PhoneNumberSharingMode phoneNumberSharingMode = 17; + ChatStyle defaultChatStyle = 18; + } + + message SubscriberData { + bytes subscriberId = 1; + string currencyCode = 2; + bool manuallyCancelled = 3; } bytes profileKey = 1; @@ -82,10 +89,9 @@ message AccountData { string givenName = 4; string familyName = 5; string avatarUrlPath = 6; - bytes subscriberId = 7; - string subscriberCurrencyCode = 8; - bool subscriptionManuallyCancelled = 9; - AccountSettings accountSettings = 10; + SubscriberData donationSubscriberData = 7; + SubscriberData backupsSubscriberData = 8; + AccountSettings accountSettings = 9; } message Recipient { @@ -93,7 +99,7 @@ message Recipient { oneof destination { Contact contact = 2; Group group = 3; - DistributionList distributionList = 4; + DistributionListItem distributionList = 4; Self self = 5; ReleaseNotes releaseNotes = 6; CallLink callLink = 7; @@ -101,10 +107,15 @@ message Recipient { } message Contact { - enum Registered { - UNKNOWN = 0; - REGISTERED = 1; - NOT_REGISTERED = 2; + message Registered { } + message NotRegistered { + uint64 unregisteredTimestamp = 1; + } + + enum Visibility { + VISIBLE = 0; + HIDDEN = 1; + HIDDEN_MESSAGE_REQUEST = 2; } optional bytes aci = 1; // should be 16 bytes @@ -112,9 +123,13 @@ message Contact { optional string username = 3; optional uint64 e164 = 4; bool blocked = 5; - bool hidden = 6; - Registered registered = 7; - uint64 unregisteredTimestamp = 8; + Visibility visibility = 6; + + oneof registration { + Registered registered = 7; + NotRegistered notRegistered = 8; + } + optional bytes profileKey = 9; bool profileSharing = 10; optional string profileGivenName = 11; @@ -225,7 +240,7 @@ message Chat { uint64 muteUntilMs = 6; bool markedUnread = 7; bool dontNotifyForMentionsIfMuted = 8; - FilePointer wallpaper = 9; + ChatStyle style = 9; } /** @@ -260,10 +275,16 @@ message AdHocCall { // Refers to a `CallLink` recipient. uint64 recipientId = 2; State state = 3; - optional bytes startedCallAci = 4; - uint64 startedCallTimestamp = 5; - // The time the call ended. 0 indicates an unknown time. - uint64 endedCallTimestamp = 6; + uint64 callTimestamp = 4; +} + +message DistributionListItem { + bytes distributionId = 1; // distribution list ids are uuids + + oneof item { + uint64 deletionTimestamp = 2; + DistributionList distributionList = 3; + } } message DistributionList { @@ -275,11 +296,9 @@ message DistributionList { } string name = 1; - bytes distributionId = 2; // distribution list ids are uuids - bool allowReplies = 3; - uint64 deletionTimestamp = 4; - PrivacyMode privacyMode = 5; - repeated uint64 memberRecipientIds = 6; // generated recipient id + bool allowReplies = 2; + PrivacyMode privacyMode = 3; + repeated uint64 memberRecipientIds = 4; // generated recipient id } message Identity { @@ -370,9 +389,6 @@ message ContactMessage { } message PaymentNotification { - - // Transaction details should largely be best effort. If fields are missing or - // invalid, we should try to render things to the best of our abilities. message TransactionDetails { message MobileCoinTxoIdentification { // Used to map to payments on the ledger repeated bytes publicKey = 1; // for received transactions @@ -398,13 +414,13 @@ message PaymentNotification { // This identification is used to map the payment table to the ledger // and is likely required otherwise we may have issues reconciling with - // the ledger. If this + // the ledger MobileCoinTxoIdentification mobileCoinIdentification = 2; optional uint64 timestamp = 3; optional uint64 blockIndex = 4; optional uint64 blockTimestamp = 5; - optional bytes transaction = 6; // mobile coin blobs, these are best effort, if they are invalid or fail to parse, treat as if missing, but do not block - optional bytes receipt = 7; // mobile coin blobs, these are best effort, if they are invalid or fail to parse, treat as if missing, but do not block + optional bytes transaction = 6; // mobile coin blobs + optional bytes receipt = 7; // mobile coin blobs } oneof payment { @@ -481,7 +497,7 @@ message ContactAttachment { repeated Phone number = 3; repeated Email email = 4; repeated PostalAddress address = 5; - optional string avatarUrlPath = 6; + optional FilePointer avatar = 6; optional string organization = 7; } @@ -729,6 +745,7 @@ message SimpleChatUpdate { BAD_DECRYPT = 9; PAYMENTS_ACTIVATED = 10; PAYMENT_ACTIVATION_REQUEST = 11; + UNSUPPORTED_PROTOCOL_MESSAGE = 12; } Type type = 1; @@ -1019,3 +1036,79 @@ message StickerPackSticker { string emoji = 1; uint32 id = 2; } + + +message ChatStyle { + message Gradient { + uint32 angle = 1; // degrees + repeated uint32 colors = 2; + repeated float positions = 3; // percent from 0 to 1 + } + + message AutomaticBubbleColor { + } + + enum WallpaperPreset { + UNKNOWN_WALLPAPER_PRESET = 0; + SOLID_BLUSH = 1; + SOLID_COPPER = 2; + SOLID_DUST = 3; + SOLID_CELADON = 4; + SOLID_RAINFOREST = 5; + SOLID_PACIFIC = 6; + SOLID_FROST = 7; + SOLID_NAVY = 8; + SOLID_LILAC = 9; + SOLID_PINK = 10; + SOLID_EGGPLANT = 11; + SOLID_SILVER = 12; + GRADIENT_SUNSET = 13; + GRADIENT_NOIR = 14; + GRADIENT_HEATMAP = 15; + GRADIENT_AQUA = 16; + GRADIENT_IRIDESCENT = 17; + GRADIENT_MONSTERA = 18; + GRADIENT_BLISS = 19; + GRADIENT_SKY = 20; + GRADIENT_PEACH = 21; + } + + enum BubbleColorPreset { + UNKNOWN_BUBBLE_COLOR_PRESET = 0; + SOLID_ULTRAMARINE = 1; + SOLID_CRIMSON = 2; + SOLID_VERMILION = 3; + SOLID_BURLAP = 4; + SOLID_FOREST = 5; + SOLID_WINTERGREEN = 6; + SOLID_TEAL = 7; + SOLID_BLUE = 8; + SOLID_INDIGO = 9; + SOLID_VIOLET = 10; + SOLID_PLUM = 11; + SOLID_TAUPE = 12; + SOLID_STEEL = 13; + GRADIENT_EMBER = 14; + GRADIENT_MIDNIGHT = 15; + GRADIENT_INFRARED = 16; + GRADIENT_LAGOON = 17; + GRADIENT_FLUORESCENT = 18; + GRADIENT_BASIL = 19; + GRADIENT_SUBLIME = 20; + GRADIENT_SEA = 21; + GRADIENT_TANGERINE = 22; + } + + oneof wallpaper { + WallpaperPreset wallpaperPreset = 1; + FilePointer wallpaperPhoto = 2; + } + + oneof bubbleColor { + BubbleColorPreset bubbleColorPreset = 3; + Gradient bubbleGradient = 4; + uint32 bubbleSolidColor = 5; + // Bubble setting is automatically determined based on the wallpaper setting. + AutomaticBubbleColor autoBubbleColor = 6; + } +} \ No newline at end of file