Update backup proto with subscriber and recipient changes.

This commit is contained in:
Clark
2024-06-07 11:17:12 -04:00
committed by Alex Hart
parent 485b466bd2
commit b782fabbb6
7 changed files with 250 additions and 158 deletions

View File

@@ -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.ChatUpdateMessage
import org.thoughtcrime.securesms.backup.v2.proto.Contact import org.thoughtcrime.securesms.backup.v2.proto.Contact
import org.thoughtcrime.securesms.backup.v2.proto.DistributionList 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.ExpirationTimerChatUpdate
import org.thoughtcrime.securesms.backup.v2.proto.FilePointer import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.proto.Frame
@@ -85,9 +86,11 @@ class ImportExportTest {
givenName = "Peter", givenName = "Peter",
familyName = "Parker", familyName = "Parker",
avatarUrlPath = "https://example.com/", avatarUrlPath = "https://example.com/",
subscriberId = SubscriberId.generate().bytes.toByteString(), donationSubscriberData = AccountData.SubscriberData(
subscriberCurrencyCode = "USD", subscriberId = SubscriberId.generate().bytes.toByteString(),
subscriptionManuallyCancelled = true, currencyCode = "USD",
manuallyCancelled = true
),
accountSettings = AccountData.AccountSettings( accountSettings = AccountData.AccountSettings(
readReceipts = true, readReceipts = true,
sealedSenderIndicators = true, sealedSenderIndicators = true,
@@ -116,9 +119,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = false, blocked = false,
hidden = false, visibility = Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -171,9 +173,8 @@ class ImportExportTest {
username = "rec$i.01", username = "rec$i.01",
e164 = 14125550000 + i, e164 = 14125550000 + i,
blocked = false, blocked = false,
hidden = false, visibility = Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Test", profileGivenName = "Test",
@@ -237,9 +238,8 @@ class ImportExportTest {
username = if (random.trueWithProbability(0.2f)) "rec$i.01" else null, username = if (random.trueWithProbability(0.2f)) "rec$i.01" else null,
e164 = 14125550000 + i, e164 = 14125550000 + i,
blocked = random.trueWithProbability(0.1f), blocked = random.trueWithProbability(0.1f),
hidden = random.trueWithProbability(0.1f), visibility = if (random.trueWithProbability(0.1f)) Contact.Visibility.HIDDEN else Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = random.trueWithProbability(0.9f), profileSharing = random.trueWithProbability(0.9f),
profileGivenName = "Test", profileGivenName = "Test",
@@ -390,9 +390,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -408,9 +407,8 @@ class ImportExportTest {
username = null, username = null,
e164 = 141255501235, e164 = 141255501235,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.HIDDEN,
registered = Contact.Registered.NOT_REGISTERED, notRegistered = Contact.NotRegistered(unregisteredTimestamp = 1234568927398L),
unregisteredTimestamp = 1234568927398L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = false, profileSharing = false,
profileGivenName = "Peter", profileGivenName = "Peter",
@@ -470,9 +468,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.HIDDEN,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -488,9 +485,8 @@ class ImportExportTest {
username = null, username = null,
e164 = 141255501235, e164 = 141255501235,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.HIDDEN,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Peter", profileGivenName = "Peter",
@@ -506,9 +502,8 @@ class ImportExportTest {
username = null, username = null,
e164 = 141255501236, e164 = 141255501236,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.HIDDEN,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Father", profileGivenName = "Father",
@@ -518,13 +513,14 @@ class ImportExportTest {
), ),
Recipient( Recipient(
id = 6, id = 6,
distributionList = DistributionList( distributionList = DistributionListItem(
name = "Kim Family",
distributionId = DistributionId.create().asUuid().toByteArray().toByteString(), distributionId = DistributionId.create().asUuid().toByteArray().toByteString(),
allowReplies = true, distributionList = DistributionList(
deletionTimestamp = 0L, name = "Kim Family",
privacyMode = DistributionList.PrivacyMode.ONLY_WITH, allowReplies = true,
memberRecipientIds = listOf(3, 4, 5) privacyMode = DistributionList.PrivacyMode.ONLY_WITH,
memberRecipientIds = listOf(3, 4, 5)
)
) )
) )
) )
@@ -540,9 +536,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = true, blocked = true,
hidden = true, visibility = Contact.Visibility.HIDDEN,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -555,13 +550,9 @@ class ImportExportTest {
alexa, alexa,
Recipient( Recipient(
id = 6, id = 6,
distributionList = DistributionList( distributionList = DistributionListItem(
name = "Deleted list",
distributionId = DistributionId.create().asUuid().toByteArray().toByteString(), distributionId = DistributionId.create().asUuid().toByteArray().toByteString(),
allowReplies = true, deletionTimestamp = 12345L
deletionTimestamp = 12345L,
privacyMode = DistributionList.PrivacyMode.ONLY_WITH,
memberRecipientIds = listOf(3)
) )
) )
) )
@@ -587,9 +578,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = false, blocked = false,
hidden = false, visibility = Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -614,8 +604,7 @@ class ImportExportTest {
expirationTimerMs = 1.days.inWholeMilliseconds, expirationTimerMs = 1.days.inWholeMilliseconds,
muteUntilMs = System.currentTimeMillis(), muteUntilMs = System.currentTimeMillis(),
markedUnread = true, markedUnread = true,
dontNotifyForMentionsIfMuted = true, dontNotifyForMentionsIfMuted = true
wallpaper = null
) )
) )
} }
@@ -689,9 +678,8 @@ class ImportExportTest {
username = "cool.01", username = "cool.01",
e164 = 141255501234, e164 = 141255501234,
blocked = false, blocked = false,
hidden = false, visibility = Contact.Visibility.VISIBLE,
registered = Contact.Registered.REGISTERED, registered = Contact.Registered(),
unregisteredTimestamp = 0L,
profileKey = TestRecipientUtils.generateProfileKey().toByteString(), profileKey = TestRecipientUtils.generateProfileKey().toByteString(),
profileSharing = true, profileSharing = true,
profileGivenName = "Alexa", profileGivenName = "Alexa",
@@ -716,8 +704,7 @@ class ImportExportTest {
expirationTimerMs = 1.days.inWholeMilliseconds, expirationTimerMs = 1.days.inWholeMilliseconds,
muteUntilMs = System.currentTimeMillis(), muteUntilMs = System.currentTimeMillis(),
markedUnread = true, markedUnread = true,
dontNotifyForMentionsIfMuted = true, dontNotifyForMentionsIfMuted = true
wallpaper = null
), ),
*individualCalls.toArray() *individualCalls.toArray()
) )
@@ -1361,8 +1348,7 @@ class ImportExportTest {
expirationTimerMs = 0, expirationTimerMs = 0,
muteUntilMs = 0, muteUntilMs = 0,
markedUnread = false, markedUnread = false,
dontNotifyForMentionsIfMuted = false, dontNotifyForMentionsIfMuted = false
wallpaper = null
) )
} }

View File

@@ -38,7 +38,7 @@ fun CallTable.restoreCallLogFromBackup(call: AdHocCall, backupState: BackupState
CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL), CallTable.TYPE to CallTable.Type.serialize(CallTable.Type.AD_HOC_CALL),
CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING), CallTable.DIRECTION to CallTable.Direction.serialize(CallTable.Direction.OUTGOING),
CallTable.EVENT to CallTable.Event.serialize(event), 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) writableDatabase.insert(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values)
@@ -64,7 +64,7 @@ class CallLogIterator(private val cursor: Cursor) : Iterator<AdHocCall?>, Closea
callId = callId, callId = callId,
recipientId = cursor.requireLong(CallTable.PEER), recipientId = cursor.requireLong(CallTable.PEER),
state = AdHocCall.State.GENERIC, state = AdHocCall.State.GENERIC,
startedCallTimestamp = cursor.requireLong(CallTable.TIMESTAMP) callTimestamp = cursor.requireLong(CallTable.TIMESTAMP)
) )
} }

View File

@@ -538,6 +538,7 @@ class ChatItemImportInserter(
SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE 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.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED or typeWithoutBase
SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST 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 -> { updateMessage.expirationTimerChange != null -> {

View File

@@ -15,6 +15,8 @@ import org.signal.core.util.requireNonNullString
import org.signal.core.util.requireObject import org.signal.core.util.requireObject
import org.signal.core.util.select import org.signal.core.util.select
import org.thoughtcrime.securesms.backup.v2.BackupState 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.DistributionListTables
import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.database.model.DistributionListId
@@ -60,19 +62,28 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
.map { recipient -> .map { recipient ->
BackupRecipient( BackupRecipient(
id = recipient.id.toLong(), id = recipient.id.toLong(),
distributionList = BackupDistributionList( distributionList = if (recipient.record.deletedAtTimestamp != 0L) {
name = recipient.record.name, DistributionListItem(
distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(), distributionId = recipient.record.distributionId.asUuid().toByteArray().toByteString(),
allowReplies = recipient.record.allowsReplies, deletionTimestamp = recipient.record.deletedAtTimestamp
deletionTimestamp = recipient.record.deletedAtTimestamp, )
privacyMode = recipient.record.privacyMode.toBackupPrivacyMode(), } else {
memberRecipientIds = recipient.record.members.map { it.toLong() } 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<RecipientId> = dlist.memberRecipientIds val members: List<RecipientId> = dlist.memberRecipientIds
.mapNotNull { backupState.backupToLocalRecipientId[it] } .mapNotNull { backupState.backupToLocalRecipientId[it] }
@@ -83,9 +94,9 @@ fun DistributionListTables.restoreFromBackup(dlist: BackupDistributionList, back
val dlistId = this.createList( val dlistId = this.createList(
name = dlist.name, name = dlist.name,
members = members, members = members,
distributionId = DistributionId.from(UuidUtil.fromByteString(dlist.distributionId)), distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)),
allowsReplies = dlist.allowReplies, allowsReplies = dlist.allowReplies,
deletionTimestamp = dlist.deletionTimestamp, deletionTimestamp = dlistItem.deletionTimestamp ?: 0,
storageId = null, storageId = null,
privacyMode = dlist.privacyMode.toLocalPrivacyMode() privacyMode = dlist.privacyMode.toLocalPrivacyMode()
)!! )!!

View File

@@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.database
import android.content.ContentValues import android.content.ContentValues
import android.database.Cursor import android.database.Cursor
import androidx.core.content.contentValuesOf
import okio.ByteString.Companion.toByteString import okio.ByteString.Companion.toByteString
import org.signal.core.util.Base64 import org.signal.core.util.Base64
import org.signal.core.util.SqlUtil import org.signal.core.util.SqlUtil
@@ -175,23 +176,30 @@ fun RecipientTable.restoreContactFromBackup(contact: Contact): RecipientId {
) )
val profileKey = contact.profileKey?.toByteArray() 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 writableDatabase
.update(RecipientTable.TABLE_NAME) .update(RecipientTable.TABLE_NAME)
.values( .values(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()
)
.where("${RecipientTable.ID} = ?", id) .where("${RecipientTable.ID} = ?", id)
.run() .run()
@@ -418,23 +426,28 @@ class BackupContactIterator(private val cursor: Cursor, private val selfId: Long
return null 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( return BackupRecipient(
id = id, id = id,
contact = Contact( contact = contactBuilder.build()
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
)
) )
} }
@@ -489,22 +502,6 @@ private fun String.e164ToLong(): Long? {
return fixed.toLongOrNull() 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 { private fun GroupTable.ShowAsStoryState.toGroupStorySendMode(): Group.StorySendMode {
return when (this) { return when (this) {
GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED

View File

@@ -49,10 +49,7 @@ object AccountDataProcessor {
givenName = self.profileName.givenName, givenName = self.profileName.givenName,
familyName = self.profileName.familyName, familyName = self.profileName.familyName,
avatarUrlPath = self.profileAvatar ?: "", avatarUrlPath = self.profileAvatar ?: "",
subscriptionManuallyCancelled = InAppPaymentsRepository.isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION),
username = self.username.getOrNull(), username = self.username.getOrNull(),
subscriberId = subscriber?.subscriberId?.bytes?.toByteString() ?: defaultAccountRecord.subscriberId,
subscriberCurrencyCode = subscriber?.currency?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode,
accountSettings = AccountData.AccountSettings( accountSettings = AccountData.AccountSettings(
storyViewReceiptsEnabled = SignalStore.storyValues().viewedReceiptsEnabled, storyViewReceiptsEnabled = SignalStore.storyValues().viewedReceiptsEnabled,
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context), typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context),
@@ -71,6 +68,11 @@ object AccountDataProcessor {
displayBadgesOnProfile = SignalStore.donationsValues().getDisplayBadgesOnProfile(), displayBadgesOnProfile = SignalStore.donationsValues().getDisplayBadgesOnProfile(),
hasSeenGroupStoryEducationSheet = SignalStore.storyValues().userHasSeenGroupStoryEducationSheet, hasSeenGroupStoryEducationSheet = SignalStore.storyValues().userHasSeenGroupStoryEducationSheet,
hasCompletedUsernameOnboarding = SignalStore.uiHints().hasCompletedUsernameOnboarding() 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().userHasSeenGroupStoryEducationSheet = settings.hasSeenGroupStoryEducationSheet
SignalStore.storyValues().viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts SignalStore.storyValues().viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts
if (accountData.subscriberId.size > 0) { if (accountData.donationSubscriberData != null) {
val remoteSubscriberId = SubscriberId.fromBytes(accountData.subscriberId.toByteArray()) if (accountData.donationSubscriberData.subscriberId.size > 0) {
val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION) val remoteSubscriberId = SubscriberId.fromBytes(accountData.donationSubscriberData.subscriberId.toByteArray())
val localSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION)
val subscriber = InAppPaymentSubscriberRecord( val subscriber = InAppPaymentSubscriberRecord(
remoteSubscriberId, remoteSubscriberId,
Currency.getInstance(accountData.subscriberCurrencyCode), Currency.getInstance(accountData.donationSubscriberData.currencyCode),
InAppPaymentSubscriberRecord.Type.DONATION, InAppPaymentSubscriberRecord.Type.DONATION,
localSubscriber?.requiresCancel ?: false, localSubscriber?.requiresCancel ?: accountData.donationSubscriberData.manuallyCancelled,
InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION) InAppPaymentsRepository.getLatestPaymentMethodType(InAppPaymentSubscriberRecord.Type.DONATION)
) )
InAppPaymentsRepository.setSubscriber(subscriber) InAppPaymentsRepository.setSubscriber(subscriber)
} }
if (accountData.subscriptionManuallyCancelled) { if (accountData.donationSubscriberData.manuallyCancelled) {
SignalStore.donationsValues().updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION) SignalStore.donationsValues().updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION)
}
} }
if (accountData.avatarUrlPath.isNotEmpty()) { if (accountData.avatarUrlPath.isNotEmpty()) {

View File

@@ -74,6 +74,13 @@ message AccountData {
bool hasSeenGroupStoryEducationSheet = 15; bool hasSeenGroupStoryEducationSheet = 15;
bool hasCompletedUsernameOnboarding = 16; bool hasCompletedUsernameOnboarding = 16;
PhoneNumberSharingMode phoneNumberSharingMode = 17; PhoneNumberSharingMode phoneNumberSharingMode = 17;
ChatStyle defaultChatStyle = 18;
}
message SubscriberData {
bytes subscriberId = 1;
string currencyCode = 2;
bool manuallyCancelled = 3;
} }
bytes profileKey = 1; bytes profileKey = 1;
@@ -82,10 +89,9 @@ message AccountData {
string givenName = 4; string givenName = 4;
string familyName = 5; string familyName = 5;
string avatarUrlPath = 6; string avatarUrlPath = 6;
bytes subscriberId = 7; SubscriberData donationSubscriberData = 7;
string subscriberCurrencyCode = 8; SubscriberData backupsSubscriberData = 8;
bool subscriptionManuallyCancelled = 9; AccountSettings accountSettings = 9;
AccountSettings accountSettings = 10;
} }
message Recipient { message Recipient {
@@ -93,7 +99,7 @@ message Recipient {
oneof destination { oneof destination {
Contact contact = 2; Contact contact = 2;
Group group = 3; Group group = 3;
DistributionList distributionList = 4; DistributionListItem distributionList = 4;
Self self = 5; Self self = 5;
ReleaseNotes releaseNotes = 6; ReleaseNotes releaseNotes = 6;
CallLink callLink = 7; CallLink callLink = 7;
@@ -101,10 +107,15 @@ message Recipient {
} }
message Contact { message Contact {
enum Registered { message Registered { }
UNKNOWN = 0; message NotRegistered {
REGISTERED = 1; uint64 unregisteredTimestamp = 1;
NOT_REGISTERED = 2; }
enum Visibility {
VISIBLE = 0;
HIDDEN = 1;
HIDDEN_MESSAGE_REQUEST = 2;
} }
optional bytes aci = 1; // should be 16 bytes optional bytes aci = 1; // should be 16 bytes
@@ -112,9 +123,13 @@ message Contact {
optional string username = 3; optional string username = 3;
optional uint64 e164 = 4; optional uint64 e164 = 4;
bool blocked = 5; bool blocked = 5;
bool hidden = 6; Visibility visibility = 6;
Registered registered = 7;
uint64 unregisteredTimestamp = 8; oneof registration {
Registered registered = 7;
NotRegistered notRegistered = 8;
}
optional bytes profileKey = 9; optional bytes profileKey = 9;
bool profileSharing = 10; bool profileSharing = 10;
optional string profileGivenName = 11; optional string profileGivenName = 11;
@@ -225,7 +240,7 @@ message Chat {
uint64 muteUntilMs = 6; uint64 muteUntilMs = 6;
bool markedUnread = 7; bool markedUnread = 7;
bool dontNotifyForMentionsIfMuted = 8; bool dontNotifyForMentionsIfMuted = 8;
FilePointer wallpaper = 9; ChatStyle style = 9;
} }
/** /**
@@ -260,10 +275,16 @@ message AdHocCall {
// Refers to a `CallLink` recipient. // Refers to a `CallLink` recipient.
uint64 recipientId = 2; uint64 recipientId = 2;
State state = 3; State state = 3;
optional bytes startedCallAci = 4; uint64 callTimestamp = 4;
uint64 startedCallTimestamp = 5; }
// The time the call ended. 0 indicates an unknown time.
uint64 endedCallTimestamp = 6; message DistributionListItem {
bytes distributionId = 1; // distribution list ids are uuids
oneof item {
uint64 deletionTimestamp = 2;
DistributionList distributionList = 3;
}
} }
message DistributionList { message DistributionList {
@@ -275,11 +296,9 @@ message DistributionList {
} }
string name = 1; string name = 1;
bytes distributionId = 2; // distribution list ids are uuids bool allowReplies = 2;
bool allowReplies = 3; PrivacyMode privacyMode = 3;
uint64 deletionTimestamp = 4; repeated uint64 memberRecipientIds = 4; // generated recipient id
PrivacyMode privacyMode = 5;
repeated uint64 memberRecipientIds = 6; // generated recipient id
} }
message Identity { message Identity {
@@ -370,9 +389,6 @@ message ContactMessage {
} }
message PaymentNotification { 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 TransactionDetails {
message MobileCoinTxoIdentification { // Used to map to payments on the ledger message MobileCoinTxoIdentification { // Used to map to payments on the ledger
repeated bytes publicKey = 1; // for received transactions 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 // This identification is used to map the payment table to the ledger
// and is likely required otherwise we may have issues reconciling with // and is likely required otherwise we may have issues reconciling with
// the ledger. If this // the ledger
MobileCoinTxoIdentification mobileCoinIdentification = 2; MobileCoinTxoIdentification mobileCoinIdentification = 2;
optional uint64 timestamp = 3; optional uint64 timestamp = 3;
optional uint64 blockIndex = 4; optional uint64 blockIndex = 4;
optional uint64 blockTimestamp = 5; 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 transaction = 6; // mobile coin blobs
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 receipt = 7; // mobile coin blobs
} }
oneof payment { oneof payment {
@@ -481,7 +497,7 @@ message ContactAttachment {
repeated Phone number = 3; repeated Phone number = 3;
repeated Email email = 4; repeated Email email = 4;
repeated PostalAddress address = 5; repeated PostalAddress address = 5;
optional string avatarUrlPath = 6; optional FilePointer avatar = 6;
optional string organization = 7; optional string organization = 7;
} }
@@ -729,6 +745,7 @@ message SimpleChatUpdate {
BAD_DECRYPT = 9; BAD_DECRYPT = 9;
PAYMENTS_ACTIVATED = 10; PAYMENTS_ACTIVATED = 10;
PAYMENT_ACTIVATION_REQUEST = 11; PAYMENT_ACTIVATION_REQUEST = 11;
UNSUPPORTED_PROTOCOL_MESSAGE = 12;
} }
Type type = 1; Type type = 1;
@@ -1019,3 +1036,79 @@ message StickerPackSticker {
string emoji = 1; string emoji = 1;
uint32 id = 2; 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;
}
}