mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 09:49:30 +01:00
Remove cruft around SignalAccountRecord.
This commit is contained in:
@@ -17,8 +17,11 @@ import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.delete
|
||||
import org.signal.core.util.exists
|
||||
import org.signal.core.util.forEach
|
||||
import org.signal.core.util.hasUnknownFields
|
||||
import org.signal.core.util.isNotEmpty
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.nullIfBlank
|
||||
import org.signal.core.util.nullIfEmpty
|
||||
import org.signal.core.util.optionalString
|
||||
import org.signal.core.util.or
|
||||
import org.signal.core.util.orNull
|
||||
@@ -1024,12 +1027,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
}
|
||||
|
||||
fun applyStorageSyncAccountUpdate(update: StorageRecordUpdate<SignalAccountRecord>) {
|
||||
val profileName = ProfileName.fromParts(update.new.givenName.orElse(null), update.new.familyName.orElse(null))
|
||||
val localKey = ProfileKeyUtil.profileKeyOptional(update.old.profileKey.orElse(null))
|
||||
val remoteKey = ProfileKeyUtil.profileKeyOptional(update.new.profileKey.orElse(null))
|
||||
val profileKey: String? = remoteKey.or(localKey).map { obj: ProfileKey -> obj.serialize() }.map { source: ByteArray? -> Base64.encodeWithPadding(source!!) }.orElse(null)
|
||||
if (!remoteKey.isPresent) {
|
||||
Log.w(TAG, "Got an empty profile key while applying an account record update! The parsed local key is ${if (localKey.isPresent) "present" else "not present"}. The raw local key is ${if (update.old.profileKey.isPresent) "present" else "not present"}. The resulting key is ${if (profileKey != null) "present" else "not present"}.")
|
||||
val profileName = ProfileName.fromParts(update.new.proto.givenName, update.new.proto.familyName)
|
||||
val localKey = update.old.proto.profileKey.nullIfEmpty()?.toByteArray()?.let { ProfileKeyUtil.profileKeyOrNull(it) }
|
||||
val remoteKey = update.new.proto.profileKey.nullIfEmpty()?.toByteArray()?.let { ProfileKeyUtil.profileKeyOrNull(it) }
|
||||
val profileKey: String? = (remoteKey ?: localKey)?.let { Base64.encodeWithPadding(it.serialize()) }
|
||||
|
||||
if (remoteKey == null) {
|
||||
Log.w(TAG, "Got an empty profile key while applying an account record update! The parsed local key is ${if (localKey != null) "present" else "not present"}. The raw local key is ${if (update.old.proto.profileKey.isNotEmpty()) "present" else "not present"}. The resulting key is ${if (profileKey != null) "present" else "not present"}.")
|
||||
}
|
||||
|
||||
val values = ContentValues().apply {
|
||||
@@ -1043,21 +1047,21 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
Log.w(TAG, "Avoided attempt to apply null profile key in account record update!")
|
||||
}
|
||||
|
||||
put(USERNAME, update.new.username)
|
||||
put(USERNAME, update.new.proto.username.nullIfBlank())
|
||||
put(STORAGE_SERVICE_ID, Base64.encodeWithPadding(update.new.id.raw))
|
||||
|
||||
if (update.new.hasUnknownFields()) {
|
||||
put(STORAGE_SERVICE_PROTO, Base64.encodeWithPadding(Objects.requireNonNull(update.new.serializeUnknownFields())))
|
||||
if (update.new.proto.hasUnknownFields()) {
|
||||
put(STORAGE_SERVICE_PROTO, Base64.encodeWithPadding(update.new.serializeUnknownFields()!!))
|
||||
} else {
|
||||
putNull(STORAGE_SERVICE_PROTO)
|
||||
}
|
||||
}
|
||||
|
||||
if (update.new.username != null) {
|
||||
if (update.new.proto.username.nullIfBlank() != null) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(USERNAME to null)
|
||||
.where("$USERNAME = ?", update.new.username!!)
|
||||
.where("$USERNAME = ?", update.new.proto.username)
|
||||
.run()
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +71,11 @@ import org.thoughtcrime.securesms.util.LRUCache
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.isScheduled
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord.PinnedConversation
|
||||
import org.whispersystems.signalservice.api.storage.SignalContactRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record
|
||||
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record
|
||||
import org.whispersystems.signalservice.api.storage.toSignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord
|
||||
import java.io.Closeable
|
||||
import java.io.IOException
|
||||
import java.util.Collections
|
||||
@@ -1522,7 +1523,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
|
||||
fun applyStorageSyncUpdate(recipientId: RecipientId, record: SignalAccountRecord) {
|
||||
writableDatabase.withinTransaction { db ->
|
||||
applyStorageSyncUpdate(recipientId, record.isNoteToSelfArchived, record.isNoteToSelfForcedUnread)
|
||||
applyStorageSyncUpdate(recipientId, record.proto.noteToSelfArchived, record.proto.noteToSelfMarkedUnread)
|
||||
|
||||
db.updateAll(TABLE_NAME)
|
||||
.values(PINNED to 0)
|
||||
@@ -1530,19 +1531,19 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
|
||||
var pinnedPosition = 1
|
||||
|
||||
for (pinned: PinnedConversation in record.pinnedConversations) {
|
||||
val pinnedRecipient: Recipient? = if (pinned.contact.isPresent) {
|
||||
Recipient.externalPush(pinned.contact.get())
|
||||
} else if (pinned.groupV1Id.isPresent) {
|
||||
for (pinned: AccountRecord.PinnedConversation in record.proto.pinnedConversations) {
|
||||
val pinnedRecipient: Recipient? = if (pinned.contact != null) {
|
||||
Recipient.externalPush(pinned.contact!!.toSignalServiceAddress())
|
||||
} else if (pinned.legacyGroupId != null) {
|
||||
try {
|
||||
Recipient.externalGroupExact(GroupId.v1(pinned.groupV1Id.get()))
|
||||
Recipient.externalGroupExact(GroupId.v1(pinned.legacyGroupId!!.toByteArray()))
|
||||
} catch (e: BadGroupIdException) {
|
||||
Log.w(TAG, "Failed to parse pinned groupV1 ID!", e)
|
||||
null
|
||||
}
|
||||
} else if (pinned.groupV2MasterKey.isPresent) {
|
||||
} else if (pinned.groupMasterKey != null) {
|
||||
try {
|
||||
Recipient.externalGroupExact(GroupId.v2(GroupMasterKey(pinned.groupV2MasterKey.get())))
|
||||
Recipient.externalGroupExact(GroupId.v2(GroupMasterKey(pinned.groupMasterKey!!.toByteArray())))
|
||||
} catch (e: InvalidInputException) {
|
||||
Log.w(TAG, "Failed to parse pinned groupV2 master key!", e)
|
||||
null
|
||||
|
||||
@@ -124,9 +124,9 @@ public class StorageAccountRestoreJob extends BaseJob {
|
||||
|
||||
JobManager jobManager = AppDependencies.getJobManager();
|
||||
|
||||
if (accountRecord.getAvatarUrlPath().isPresent()) {
|
||||
if (!accountRecord.getProto().avatarUrlPath.isEmpty()) {
|
||||
Log.i(TAG, "Fetching avatar...");
|
||||
Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RetrieveProfileAvatarJob(Recipient.self(), accountRecord.getAvatarUrlPath().get()), LIFESPAN/2);
|
||||
Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RetrieveProfileAvatarJob(Recipient.self(), accountRecord.getProto().avatarUrlPath), LIFESPAN / 2);
|
||||
|
||||
if (state.isPresent()) {
|
||||
Log.i(TAG, "Avatar retrieved successfully. " + state.get());
|
||||
|
||||
@@ -100,13 +100,11 @@ import java.util.stream.Collectors
|
||||
* - Update the respective model (i.e. [SignalContactRecord])
|
||||
* - Add getters
|
||||
* - Update the builder
|
||||
* - Update [SignalRecord.describeDiff].
|
||||
* - Update the respective record processor (i.e [ContactRecordProcessor]). You need to make
|
||||
* sure that you're:
|
||||
* - Merging the attributes, likely preferring remote
|
||||
* - Adding to doParamsMatch()
|
||||
* - Adding the parameter to the builder chain when creating a merged model
|
||||
* - Update builder usage in StorageSyncModels
|
||||
* - Update the respective record processor (i.e [ContactRecordProcessor]). You need to make sure that you're:
|
||||
* - Merging the attributes, likely preferring remote
|
||||
* - Adding to doParamsMatch()
|
||||
* - Adding the parameter to the builder chain when creating a merged model
|
||||
* - Update builder usage in StorageSyncModels
|
||||
* - Handle the new data when writing to the local storage
|
||||
* (i.e. [RecipientTable.applyStorageSyncContactUpdate]).
|
||||
* - Make sure that whenever you change the field in the UI, we rotate the storageId for that row
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.nullIfBlank
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.ecc.Curve
|
||||
@@ -401,10 +402,10 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
var username: String?
|
||||
get() {
|
||||
val value = getString(KEY_USERNAME, null)
|
||||
return if (value.isNullOrBlank()) null else value
|
||||
return value.nullIfBlank()
|
||||
}
|
||||
set(value) {
|
||||
putString(KEY_USERNAME, value)
|
||||
putString(KEY_USERNAME, value.nullIfBlank())
|
||||
}
|
||||
|
||||
/** The local user's username link components, if set. */
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import android.content.Context
|
||||
import okio.ByteString
|
||||
import org.signal.core.util.isNotEmpty
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.nullIfEmpty
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper.applyAccountStorageSyncUpdates
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper.buildAccountRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord
|
||||
import org.whispersystems.signalservice.api.util.OptionalUtil
|
||||
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord
|
||||
import org.whispersystems.signalservice.api.storage.StorageId
|
||||
import org.whispersystems.signalservice.api.storage.safeSetBackupsSubscriber
|
||||
import org.whispersystems.signalservice.api.storage.safeSetPayments
|
||||
import org.whispersystems.signalservice.api.storage.safeSetSubscriber
|
||||
import org.whispersystems.signalservice.api.storage.toSignalAccountRecord
|
||||
import org.whispersystems.signalservice.internal.storage.protos.OptionalBool
|
||||
import java.util.Optional
|
||||
|
||||
@@ -45,184 +51,100 @@ class AccountRecordProcessor(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getMatching(record: SignalAccountRecord, keyGenerator: StorageKeyGenerator): Optional<SignalAccountRecord> {
|
||||
override fun getMatching(remote: SignalAccountRecord, keyGenerator: StorageKeyGenerator): Optional<SignalAccountRecord> {
|
||||
return Optional.of(localAccountRecord)
|
||||
}
|
||||
|
||||
override fun merge(remote: SignalAccountRecord, local: SignalAccountRecord, keyGenerator: StorageKeyGenerator): SignalAccountRecord {
|
||||
val givenName: String
|
||||
val familyName: String
|
||||
val mergedGivenName: String
|
||||
val mergedFamilyName: String
|
||||
|
||||
if (remote.givenName.isPresent || remote.familyName.isPresent) {
|
||||
givenName = remote.givenName.orElse("")
|
||||
familyName = remote.familyName.orElse("")
|
||||
if (remote.proto.givenName.isNotBlank() || remote.proto.familyName.isNotBlank()) {
|
||||
mergedGivenName = remote.proto.givenName
|
||||
mergedFamilyName = remote.proto.familyName
|
||||
} else {
|
||||
givenName = local.givenName.orElse("")
|
||||
familyName = local.familyName.orElse("")
|
||||
mergedGivenName = local.proto.givenName
|
||||
mergedFamilyName = local.proto.familyName
|
||||
}
|
||||
|
||||
val payments = if (remote.payments.entropy.isPresent) {
|
||||
remote.payments
|
||||
val payments = if (remote.proto.payments?.entropy != null) {
|
||||
remote.proto.payments
|
||||
} else {
|
||||
local.payments
|
||||
local.proto.payments
|
||||
}
|
||||
|
||||
val subscriber = if (remote.subscriber.id.isPresent) {
|
||||
remote.subscriber
|
||||
val donationSubscriberId: ByteString
|
||||
val donationSubscriberCurrencyCode: String
|
||||
|
||||
if (remote.proto.subscriberId.isNotEmpty()) {
|
||||
donationSubscriberId = remote.proto.subscriberId
|
||||
donationSubscriberCurrencyCode = remote.proto.subscriberCurrencyCode
|
||||
} else {
|
||||
local.subscriber
|
||||
donationSubscriberId = local.proto.subscriberId
|
||||
donationSubscriberCurrencyCode = remote.proto.subscriberCurrencyCode
|
||||
}
|
||||
|
||||
val backupsSubscriber = if (remote.subscriber.id.isPresent) {
|
||||
remote.subscriber
|
||||
val backupsSubscriberId: ByteString
|
||||
val backupsSubscriberCurrencyCode: String
|
||||
|
||||
if (remote.proto.backupsSubscriberId.isNotEmpty()) {
|
||||
backupsSubscriberId = remote.proto.backupsSubscriberId
|
||||
backupsSubscriberCurrencyCode = remote.proto.backupsSubscriberCurrencyCode
|
||||
} else {
|
||||
local.subscriber
|
||||
backupsSubscriberId = local.proto.backupsSubscriberId
|
||||
backupsSubscriberCurrencyCode = remote.proto.backupsSubscriberCurrencyCode
|
||||
}
|
||||
val storyViewReceiptsState = if (remote.storyViewReceiptsState == OptionalBool.UNSET) {
|
||||
local.storyViewReceiptsState
|
||||
|
||||
val storyViewReceiptsState = if (remote.proto.storyViewReceiptsEnabled == OptionalBool.UNSET) {
|
||||
local.proto.storyViewReceiptsEnabled
|
||||
} else {
|
||||
remote.storyViewReceiptsState
|
||||
remote.proto.storyViewReceiptsEnabled
|
||||
}
|
||||
|
||||
val unknownFields = remote.serializeUnknownFields()
|
||||
val avatarUrlPath = OptionalUtil.or(remote.avatarUrlPath, local.avatarUrlPath).orElse("")
|
||||
val profileKey = OptionalUtil.or(remote.profileKey, local.profileKey).orElse(null)
|
||||
val noteToSelfArchived = remote.isNoteToSelfArchived
|
||||
val noteToSelfForcedUnread = remote.isNoteToSelfForcedUnread
|
||||
val readReceipts = remote.isReadReceiptsEnabled
|
||||
val typingIndicators = remote.isTypingIndicatorsEnabled
|
||||
val sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled
|
||||
val linkPreviews = remote.isLinkPreviewsEnabled
|
||||
val unlisted = remote.isPhoneNumberUnlisted
|
||||
val pinnedConversations = remote.pinnedConversations
|
||||
val phoneNumberSharingMode = remote.phoneNumberSharingMode
|
||||
val preferContactAvatars = remote.isPreferContactAvatars
|
||||
val universalExpireTimer = remote.universalExpireTimer
|
||||
val primarySendsSms = if (SignalStore.account.isPrimaryDevice) local.isPrimarySendsSms else remote.isPrimarySendsSms
|
||||
val e164 = if (SignalStore.account.isPrimaryDevice) local.e164 else remote.e164
|
||||
val defaultReactions = if (remote.defaultReactions.size > 0) remote.defaultReactions else local.defaultReactions
|
||||
val displayBadgesOnProfile = remote.isDisplayBadgesOnProfile
|
||||
val subscriptionManuallyCancelled = remote.isSubscriptionManuallyCancelled
|
||||
val keepMutedChatsArchived = remote.isKeepMutedChatsArchived
|
||||
val hasSetMyStoriesPrivacy = remote.hasSetMyStoriesPrivacy()
|
||||
val hasViewedOnboardingStory = remote.hasViewedOnboardingStory() || local.hasViewedOnboardingStory()
|
||||
val storiesDisabled = remote.isStoriesDisabled
|
||||
val hasSeenGroupStoryEducation = remote.hasSeenGroupStoryEducationSheet() || local.hasSeenGroupStoryEducationSheet()
|
||||
val hasSeenUsernameOnboarding = remote.hasCompletedUsernameOnboarding() || local.hasCompletedUsernameOnboarding()
|
||||
val username = remote.username
|
||||
val usernameLink = remote.usernameLink
|
||||
|
||||
val matchesRemote = doParamsMatch(
|
||||
contact = remote,
|
||||
unknownFields = unknownFields,
|
||||
givenName = givenName,
|
||||
familyName = familyName,
|
||||
avatarUrlPath = avatarUrlPath,
|
||||
profileKey = profileKey,
|
||||
noteToSelfArchived = noteToSelfArchived,
|
||||
noteToSelfForcedUnread = noteToSelfForcedUnread,
|
||||
readReceipts = readReceipts,
|
||||
typingIndicators = typingIndicators,
|
||||
sealedSenderIndicators = sealedSenderIndicators,
|
||||
linkPreviewsEnabled = linkPreviews,
|
||||
phoneNumberSharingMode = phoneNumberSharingMode,
|
||||
unlistedPhoneNumber = unlisted,
|
||||
pinnedConversations = pinnedConversations,
|
||||
preferContactAvatars = preferContactAvatars,
|
||||
payments = payments,
|
||||
universalExpireTimer = universalExpireTimer,
|
||||
primarySendsSms = primarySendsSms,
|
||||
e164 = e164,
|
||||
defaultReactions = defaultReactions,
|
||||
subscriber = subscriber,
|
||||
displayBadgesOnProfile = displayBadgesOnProfile,
|
||||
subscriptionManuallyCancelled = subscriptionManuallyCancelled,
|
||||
keepMutedChatsArchived = keepMutedChatsArchived,
|
||||
hasSetMyStoriesPrivacy = hasSetMyStoriesPrivacy,
|
||||
hasViewedOnboardingStory = hasViewedOnboardingStory,
|
||||
hasCompletedUsernameOnboarding = hasSeenUsernameOnboarding,
|
||||
storiesDisabled = storiesDisabled,
|
||||
storyViewReceiptsState = storyViewReceiptsState,
|
||||
username = username,
|
||||
usernameLink = usernameLink,
|
||||
backupsSubscriber = backupsSubscriber
|
||||
)
|
||||
val matchesLocal = doParamsMatch(
|
||||
contact = local,
|
||||
unknownFields = unknownFields,
|
||||
givenName = givenName,
|
||||
familyName = familyName,
|
||||
avatarUrlPath = avatarUrlPath,
|
||||
profileKey = profileKey,
|
||||
noteToSelfArchived = noteToSelfArchived,
|
||||
noteToSelfForcedUnread = noteToSelfForcedUnread,
|
||||
readReceipts = readReceipts,
|
||||
typingIndicators = typingIndicators,
|
||||
sealedSenderIndicators = sealedSenderIndicators,
|
||||
linkPreviewsEnabled = linkPreviews,
|
||||
phoneNumberSharingMode = phoneNumberSharingMode,
|
||||
unlistedPhoneNumber = unlisted,
|
||||
pinnedConversations = pinnedConversations,
|
||||
preferContactAvatars = preferContactAvatars,
|
||||
payments = payments,
|
||||
universalExpireTimer = universalExpireTimer,
|
||||
primarySendsSms = primarySendsSms,
|
||||
e164 = e164,
|
||||
defaultReactions = defaultReactions,
|
||||
subscriber = subscriber,
|
||||
displayBadgesOnProfile = displayBadgesOnProfile,
|
||||
subscriptionManuallyCancelled = subscriptionManuallyCancelled,
|
||||
keepMutedChatsArchived = keepMutedChatsArchived,
|
||||
hasSetMyStoriesPrivacy = hasSetMyStoriesPrivacy,
|
||||
hasViewedOnboardingStory = hasViewedOnboardingStory,
|
||||
hasCompletedUsernameOnboarding = hasSeenUsernameOnboarding,
|
||||
storiesDisabled = storiesDisabled,
|
||||
storyViewReceiptsState = storyViewReceiptsState,
|
||||
username = username,
|
||||
usernameLink = usernameLink,
|
||||
backupsSubscriber = backupsSubscriber
|
||||
)
|
||||
val merged = SignalAccountRecord.newBuilder(unknownFields).apply {
|
||||
givenName = mergedGivenName
|
||||
familyName = mergedFamilyName
|
||||
avatarUrlPath = remote.proto.avatarUrlPath.nullIfEmpty() ?: local.proto.avatarUrlPath
|
||||
profileKey = remote.proto.profileKey.nullIfEmpty() ?: local.proto.profileKey
|
||||
noteToSelfArchived = remote.proto.noteToSelfArchived
|
||||
noteToSelfMarkedUnread = remote.proto.noteToSelfMarkedUnread
|
||||
readReceipts = remote.proto.readReceipts
|
||||
typingIndicators = remote.proto.typingIndicators
|
||||
sealedSenderIndicators = remote.proto.sealedSenderIndicators
|
||||
linkPreviews = remote.proto.linkPreviews
|
||||
unlistedPhoneNumber = remote.proto.unlistedPhoneNumber
|
||||
pinnedConversations = remote.proto.pinnedConversations
|
||||
phoneNumberSharingMode = remote.proto.phoneNumberSharingMode
|
||||
preferContactAvatars = remote.proto.preferContactAvatars
|
||||
universalExpireTimer = remote.proto.universalExpireTimer
|
||||
primarySendsSms = false
|
||||
e164 = if (SignalStore.account.isPrimaryDevice) local.proto.e164 else remote.proto.e164
|
||||
preferredReactionEmoji = remote.proto.preferredReactionEmoji.takeIf { it.isNotEmpty() } ?: local.proto.preferredReactionEmoji
|
||||
displayBadgesOnProfile = remote.proto.displayBadgesOnProfile
|
||||
subscriptionManuallyCancelled = remote.proto.subscriptionManuallyCancelled
|
||||
keepMutedChatsArchived = remote.proto.keepMutedChatsArchived
|
||||
hasSetMyStoriesPrivacy = remote.proto.hasSetMyStoriesPrivacy
|
||||
hasViewedOnboardingStory = remote.proto.hasViewedOnboardingStory || local.proto.hasViewedOnboardingStory
|
||||
storiesDisabled = remote.proto.storiesDisabled
|
||||
storyViewReceiptsEnabled = storyViewReceiptsState
|
||||
hasSeenGroupStoryEducationSheet = remote.proto.hasSeenGroupStoryEducationSheet || local.proto.hasSeenGroupStoryEducationSheet
|
||||
hasCompletedUsernameOnboarding = remote.proto.hasCompletedUsernameOnboarding || local.proto.hasCompletedUsernameOnboarding
|
||||
username = remote.proto.username
|
||||
usernameLink = remote.proto.usernameLink
|
||||
|
||||
if (matchesRemote) {
|
||||
return remote
|
||||
} else if (matchesLocal) {
|
||||
return local
|
||||
safeSetPayments(payments?.enabled == true, payments?.entropy?.toByteArray())
|
||||
safeSetSubscriber(donationSubscriberId, donationSubscriberCurrencyCode)
|
||||
safeSetBackupsSubscriber(backupsSubscriberId, backupsSubscriberCurrencyCode)
|
||||
}.toSignalAccountRecord(StorageId.forAccount(keyGenerator.generate()))
|
||||
|
||||
return if (doParamsMatch(remote, merged)) {
|
||||
remote
|
||||
} else if (doParamsMatch(local, merged)) {
|
||||
local
|
||||
} else {
|
||||
val builder = SignalAccountRecord.Builder(keyGenerator.generate(), unknownFields)
|
||||
.setGivenName(givenName)
|
||||
.setFamilyName(familyName)
|
||||
.setAvatarUrlPath(avatarUrlPath)
|
||||
.setProfileKey(profileKey)
|
||||
.setNoteToSelfArchived(noteToSelfArchived)
|
||||
.setNoteToSelfForcedUnread(noteToSelfForcedUnread)
|
||||
.setReadReceiptsEnabled(readReceipts)
|
||||
.setTypingIndicatorsEnabled(typingIndicators)
|
||||
.setSealedSenderIndicatorsEnabled(sealedSenderIndicators)
|
||||
.setLinkPreviewsEnabled(linkPreviews)
|
||||
.setUnlistedPhoneNumber(unlisted)
|
||||
.setPhoneNumberSharingMode(phoneNumberSharingMode)
|
||||
.setUnlistedPhoneNumber(unlisted)
|
||||
.setPinnedConversations(pinnedConversations)
|
||||
.setPreferContactAvatars(preferContactAvatars)
|
||||
.setPayments(payments.isEnabled, payments.entropy.orElse(null))
|
||||
.setUniversalExpireTimer(universalExpireTimer)
|
||||
.setPrimarySendsSms(primarySendsSms)
|
||||
.setDefaultReactions(defaultReactions)
|
||||
.setSubscriber(subscriber)
|
||||
.setStoryViewReceiptsState(storyViewReceiptsState)
|
||||
.setDisplayBadgesOnProfile(displayBadgesOnProfile)
|
||||
.setSubscriptionManuallyCancelled(subscriptionManuallyCancelled)
|
||||
.setKeepMutedChatsArchived(keepMutedChatsArchived)
|
||||
.setHasSetMyStoriesPrivacy(hasSetMyStoriesPrivacy)
|
||||
.setHasViewedOnboardingStory(hasViewedOnboardingStory)
|
||||
.setStoriesDisabled(storiesDisabled)
|
||||
.setHasSeenGroupStoryEducationSheet(hasSeenGroupStoryEducation)
|
||||
.setHasCompletedUsernameOnboarding(hasSeenUsernameOnboarding)
|
||||
.setUsername(username)
|
||||
.setUsernameLink(usernameLink)
|
||||
.setBackupsSubscriber(backupsSubscriber)
|
||||
|
||||
return builder.build()
|
||||
merged
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,72 +160,7 @@ class AccountRecordProcessor(
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun doParamsMatch(
|
||||
contact: SignalAccountRecord,
|
||||
unknownFields: ByteArray?,
|
||||
givenName: String,
|
||||
familyName: String,
|
||||
avatarUrlPath: String,
|
||||
profileKey: ByteArray?,
|
||||
noteToSelfArchived: Boolean,
|
||||
noteToSelfForcedUnread: Boolean,
|
||||
readReceipts: Boolean,
|
||||
typingIndicators: Boolean,
|
||||
sealedSenderIndicators: Boolean,
|
||||
linkPreviewsEnabled: Boolean,
|
||||
phoneNumberSharingMode: AccountRecord.PhoneNumberSharingMode,
|
||||
unlistedPhoneNumber: Boolean,
|
||||
pinnedConversations: List<SignalAccountRecord.PinnedConversation>,
|
||||
preferContactAvatars: Boolean,
|
||||
payments: SignalAccountRecord.Payments,
|
||||
universalExpireTimer: Int,
|
||||
primarySendsSms: Boolean,
|
||||
e164: String,
|
||||
defaultReactions: List<String>,
|
||||
subscriber: SignalAccountRecord.Subscriber,
|
||||
displayBadgesOnProfile: Boolean,
|
||||
subscriptionManuallyCancelled: Boolean,
|
||||
keepMutedChatsArchived: Boolean,
|
||||
hasSetMyStoriesPrivacy: Boolean,
|
||||
hasViewedOnboardingStory: Boolean,
|
||||
hasCompletedUsernameOnboarding: Boolean,
|
||||
storiesDisabled: Boolean,
|
||||
storyViewReceiptsState: OptionalBool,
|
||||
username: String?,
|
||||
usernameLink: AccountRecord.UsernameLink?,
|
||||
backupsSubscriber: SignalAccountRecord.Subscriber
|
||||
): Boolean {
|
||||
return contact.serializeUnknownFields().contentEquals(unknownFields) &&
|
||||
contact.givenName.orElse("") == givenName &&
|
||||
contact.familyName.orElse("") == familyName &&
|
||||
contact.avatarUrlPath.orElse("") == avatarUrlPath &&
|
||||
contact.payments == payments &&
|
||||
contact.e164 == e164 &&
|
||||
contact.defaultReactions == defaultReactions &&
|
||||
contact.profileKey.orElse(null).contentEquals(profileKey) &&
|
||||
contact.isNoteToSelfArchived == noteToSelfArchived &&
|
||||
contact.isNoteToSelfForcedUnread == noteToSelfForcedUnread &&
|
||||
contact.isReadReceiptsEnabled == readReceipts &&
|
||||
contact.isTypingIndicatorsEnabled == typingIndicators &&
|
||||
contact.isSealedSenderIndicatorsEnabled == sealedSenderIndicators &&
|
||||
contact.isLinkPreviewsEnabled == linkPreviewsEnabled &&
|
||||
contact.phoneNumberSharingMode == phoneNumberSharingMode &&
|
||||
contact.isPhoneNumberUnlisted == unlistedPhoneNumber &&
|
||||
contact.isPreferContactAvatars == preferContactAvatars &&
|
||||
contact.universalExpireTimer == universalExpireTimer &&
|
||||
contact.isPrimarySendsSms == primarySendsSms &&
|
||||
contact.pinnedConversations == pinnedConversations &&
|
||||
contact.subscriber == subscriber &&
|
||||
contact.isDisplayBadgesOnProfile == displayBadgesOnProfile &&
|
||||
contact.isSubscriptionManuallyCancelled == subscriptionManuallyCancelled &&
|
||||
contact.isKeepMutedChatsArchived == keepMutedChatsArchived &&
|
||||
contact.hasSetMyStoriesPrivacy() == hasSetMyStoriesPrivacy &&
|
||||
contact.hasViewedOnboardingStory() == hasViewedOnboardingStory &&
|
||||
contact.hasCompletedUsernameOnboarding() == hasCompletedUsernameOnboarding &&
|
||||
contact.isStoriesDisabled == storiesDisabled &&
|
||||
contact.storyViewReceiptsState == storyViewReceiptsState &&
|
||||
contact.username == username &&
|
||||
contact.usernameLink == usernameLink &&
|
||||
contact.backupsSubscriber == backupsSubscriber
|
||||
private fun doParamsMatch(base: SignalAccountRecord, test: SignalAccountRecord): Boolean {
|
||||
return base.serializeUnknownFields().contentEquals(test.serializeUnknownFields()) && base.proto == test.proto
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.TreeSet
|
||||
* our local store). We use it for a [TreeSet], so mainly it's just important that the '0'
|
||||
* case is correct. Other cases are whatever, just make it something stable.
|
||||
*/
|
||||
abstract class DefaultStorageRecordProcessor<E : SignalRecord> : StorageRecordProcessor<E>, Comparator<E> {
|
||||
abstract class DefaultStorageRecordProcessor<E : SignalRecord<*>> : StorageRecordProcessor<E>, Comparator<E> {
|
||||
companion object {
|
||||
private val TAG = Log.tag(DefaultStorageRecordProcessor::class.java)
|
||||
}
|
||||
@@ -37,16 +37,15 @@ abstract class DefaultStorageRecordProcessor<E : SignalRecord> : StorageRecordPr
|
||||
@Throws(IOException::class)
|
||||
override fun process(remoteRecords: Collection<E>, keyGenerator: StorageKeyGenerator) {
|
||||
val matchedRecords: MutableSet<E> = TreeSet(this)
|
||||
var i = 0
|
||||
|
||||
for (remote in remoteRecords) {
|
||||
for ((i, remote) in remoteRecords.withIndex()) {
|
||||
if (isInvalid(remote)) {
|
||||
warn(i, remote, "Found invalid key! Ignoring it.")
|
||||
} else {
|
||||
val local = getMatching(remote, keyGenerator)
|
||||
|
||||
if (local.isPresent) {
|
||||
val merged = merge(remote, local.get(), keyGenerator)
|
||||
val merged: E = merge(remote, local.get(), keyGenerator)
|
||||
|
||||
if (matchedRecords.contains(local.get())) {
|
||||
warn(i, remote, "Multiple remote records map to the same local record! Ignoring this one.")
|
||||
@@ -54,7 +53,7 @@ abstract class DefaultStorageRecordProcessor<E : SignalRecord> : StorageRecordPr
|
||||
matchedRecords.add(local.get())
|
||||
|
||||
if (merged != remote) {
|
||||
info(i, remote, "[Remote Update] " + StorageRecordUpdate(remote, merged).toString())
|
||||
info(i, remote, "[Remote Update] " + remote.describeDiff(merged))
|
||||
}
|
||||
|
||||
if (merged != local.get()) {
|
||||
@@ -68,8 +67,6 @@ abstract class DefaultStorageRecordProcessor<E : SignalRecord> : StorageRecordPr
|
||||
insertLocal(remote)
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.io.IOException
|
||||
* Handles processing a remote record, which involves applying any local changes that need to be
|
||||
* made based on the remote records.
|
||||
*/
|
||||
interface StorageRecordProcessor<E : SignalRecord?> {
|
||||
interface StorageRecordProcessor<E : SignalRecord<*>> {
|
||||
@Throws(IOException::class)
|
||||
fun process(remoteRecords: Collection<E>, keyGenerator: StorageKeyGenerator)
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.thoughtcrime.securesms.storage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a pair of records: one old, and one new. The new record should replace the old.
|
||||
*/
|
||||
public class StorageRecordUpdate<E extends SignalRecord> {
|
||||
private final E oldRecord;
|
||||
private final E newRecord;
|
||||
|
||||
public StorageRecordUpdate(@NonNull E oldRecord, @NonNull E newRecord) {
|
||||
this.oldRecord = oldRecord;
|
||||
this.newRecord = newRecord;
|
||||
}
|
||||
|
||||
public @NonNull E getOld() {
|
||||
return oldRecord;
|
||||
}
|
||||
|
||||
public @NonNull E getNew() {
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
StorageRecordUpdate that = (StorageRecordUpdate) o;
|
||||
return oldRecord.equals(that.oldRecord) &&
|
||||
newRecord.equals(that.newRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(oldRecord, newRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String toString() {
|
||||
return newRecord.describeDiff(oldRecord);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord
|
||||
|
||||
/**
|
||||
* Represents a pair of records: one old, and one new. The new record should replace the old.
|
||||
*/
|
||||
class StorageRecordUpdate<E : SignalRecord<*>>(val old: E, val new: E) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as StorageRecordUpdate<*>
|
||||
|
||||
if (old != other.old) return false
|
||||
if (new != other.new) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = old.hashCode()
|
||||
result = 31 * result + new.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.storage
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import okio.ByteString
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.Base64.encodeWithPadding
|
||||
import org.signal.core.util.logging.Log
|
||||
@@ -28,6 +29,10 @@ import org.whispersystems.signalservice.api.storage.SignalContactRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalStorageManifest
|
||||
import org.whispersystems.signalservice.api.storage.SignalStorageRecord
|
||||
import org.whispersystems.signalservice.api.storage.StorageId
|
||||
import org.whispersystems.signalservice.api.storage.safeSetBackupsSubscriber
|
||||
import org.whispersystems.signalservice.api.storage.safeSetPayments
|
||||
import org.whispersystems.signalservice.api.storage.safeSetSubscriber
|
||||
import org.whispersystems.signalservice.api.storage.toSignalAccountRecord
|
||||
import org.whispersystems.signalservice.api.util.OptionalUtil.byteArrayEquals
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import org.whispersystems.signalservice.api.util.toByteArray
|
||||
@@ -130,52 +135,54 @@ object StorageSyncHelper {
|
||||
|
||||
val storageId = selfRecord?.storageId ?: self.storageId
|
||||
|
||||
val account = SignalAccountRecord.Builder(storageId, selfRecord?.syncExtras?.storageProto)
|
||||
.setProfileKey(self.profileKey)
|
||||
.setGivenName(self.profileName.givenName)
|
||||
.setFamilyName(self.profileName.familyName)
|
||||
.setAvatarUrlPath(self.profileAvatar)
|
||||
.setNoteToSelfArchived(selfRecord != null && selfRecord.syncExtras.isArchived)
|
||||
.setNoteToSelfForcedUnread(selfRecord != null && selfRecord.syncExtras.isForcedUnread)
|
||||
.setTypingIndicatorsEnabled(TextSecurePreferences.isTypingIndicatorsEnabled(context))
|
||||
.setReadReceiptsEnabled(TextSecurePreferences.isReadReceiptsEnabled(context))
|
||||
.setSealedSenderIndicatorsEnabled(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context))
|
||||
.setLinkPreviewsEnabled(SignalStore.settings.isLinkPreviewsEnabled)
|
||||
.setUnlistedPhoneNumber(SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode == PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE)
|
||||
.setPhoneNumberSharingMode(StorageSyncModels.localToRemotePhoneNumberSharingMode(SignalStore.phoneNumberPrivacy.phoneNumberSharingMode))
|
||||
.setPinnedConversations(StorageSyncModels.localToRemotePinnedConversations(pinned))
|
||||
.setPreferContactAvatars(SignalStore.settings.isPreferSystemContactPhotos)
|
||||
.setPayments(SignalStore.payments.mobileCoinPaymentsEnabled(), Optional.ofNullable(SignalStore.payments.paymentsEntropy).map { obj: Entropy -> obj.bytes }.orElse(null))
|
||||
.setPrimarySendsSms(false)
|
||||
.setUniversalExpireTimer(SignalStore.settings.universalExpireTimer)
|
||||
.setDefaultReactions(SignalStore.emoji.reactions)
|
||||
.setSubscriber(StorageSyncModels.localToRemoteSubscriber(getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION)))
|
||||
.setBackupsSubscriber(StorageSyncModels.localToRemoteSubscriber(getSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP)))
|
||||
.setDisplayBadgesOnProfile(SignalStore.inAppPayments.getDisplayBadgesOnProfile())
|
||||
.setSubscriptionManuallyCancelled(isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION))
|
||||
.setKeepMutedChatsArchived(SignalStore.settings.shouldKeepMutedChatsArchived())
|
||||
.setHasSetMyStoriesPrivacy(SignalStore.story.userHasBeenNotifiedAboutStories)
|
||||
.setHasViewedOnboardingStory(SignalStore.story.userHasViewedOnboardingStory)
|
||||
.setStoriesDisabled(SignalStore.story.isFeatureDisabled)
|
||||
.setStoryViewReceiptsState(storyViewReceiptsState)
|
||||
.setHasSeenGroupStoryEducationSheet(SignalStore.story.userHasSeenGroupStoryEducationSheet)
|
||||
.setUsername(SignalStore.account.username)
|
||||
.setHasCompletedUsernameOnboarding(SignalStore.uiHints.hasCompletedUsernameOnboarding())
|
||||
val accountRecord = SignalAccountRecord.newBuilder(selfRecord?.syncExtras?.storageProto).apply {
|
||||
profileKey = self.profileKey?.toByteString() ?: ByteString.EMPTY
|
||||
givenName = self.profileName.givenName
|
||||
familyName = self.profileName.familyName
|
||||
avatarUrlPath = self.profileAvatar ?: ""
|
||||
noteToSelfArchived = selfRecord != null && selfRecord.syncExtras.isArchived
|
||||
noteToSelfMarkedUnread = selfRecord != null && selfRecord.syncExtras.isForcedUnread
|
||||
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context)
|
||||
readReceipts = TextSecurePreferences.isReadReceiptsEnabled(context)
|
||||
sealedSenderIndicators = TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context)
|
||||
linkPreviews = SignalStore.settings.isLinkPreviewsEnabled
|
||||
unlistedPhoneNumber = SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode == PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE
|
||||
phoneNumberSharingMode = StorageSyncModels.localToRemotePhoneNumberSharingMode(SignalStore.phoneNumberPrivacy.phoneNumberSharingMode)
|
||||
pinnedConversations = StorageSyncModels.localToRemotePinnedConversations(pinned)
|
||||
preferContactAvatars = SignalStore.settings.isPreferSystemContactPhotos
|
||||
primarySendsSms = false
|
||||
universalExpireTimer = SignalStore.settings.universalExpireTimer
|
||||
preferredReactionEmoji = SignalStore.emoji.reactions
|
||||
displayBadgesOnProfile = SignalStore.inAppPayments.getDisplayBadgesOnProfile()
|
||||
subscriptionManuallyCancelled = isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
keepMutedChatsArchived = SignalStore.settings.shouldKeepMutedChatsArchived()
|
||||
hasSetMyStoriesPrivacy = SignalStore.story.userHasBeenNotifiedAboutStories
|
||||
hasViewedOnboardingStory = SignalStore.story.userHasViewedOnboardingStory
|
||||
storiesDisabled = SignalStore.story.isFeatureDisabled
|
||||
storyViewReceiptsEnabled = storyViewReceiptsState
|
||||
hasSeenGroupStoryEducationSheet = SignalStore.story.userHasSeenGroupStoryEducationSheet
|
||||
hasCompletedUsernameOnboarding = SignalStore.uiHints.hasCompletedUsernameOnboarding()
|
||||
username = SignalStore.account.username ?: ""
|
||||
usernameLink = SignalStore.account.usernameLink?.let { linkComponents ->
|
||||
AccountRecord.UsernameLink(
|
||||
entropy = linkComponents.entropy.toByteString(),
|
||||
serverId = linkComponents.serverId.toByteArray().toByteString(),
|
||||
color = StorageSyncModels.localToRemoteUsernameColor(SignalStore.misc.usernameQrCodeColorScheme)
|
||||
)
|
||||
}
|
||||
|
||||
val linkComponents = SignalStore.account.usernameLink
|
||||
if (linkComponents != null) {
|
||||
account.setUsernameLink(
|
||||
AccountRecord.UsernameLink.Builder()
|
||||
.entropy(linkComponents.entropy.toByteString())
|
||||
.serverId(linkComponents.serverId.toByteArray().toByteString())
|
||||
.color(StorageSyncModels.localToRemoteUsernameColor(SignalStore.misc.usernameQrCodeColorScheme))
|
||||
.build()
|
||||
)
|
||||
} else {
|
||||
account.setUsernameLink(null)
|
||||
getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION)?.let {
|
||||
safeSetSubscriber(it.subscriberId.bytes.toByteString(), it.currency.currencyCode)
|
||||
}
|
||||
|
||||
getSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP)?.let {
|
||||
safeSetBackupsSubscriber(it.subscriberId.bytes.toByteString(), it.currency.currencyCode)
|
||||
}
|
||||
|
||||
safeSetPayments(SignalStore.payments.mobileCoinPaymentsEnabled(), Optional.ofNullable(SignalStore.payments.paymentsEntropy).map { obj: Entropy -> obj.bytes }.orElse(null))
|
||||
}
|
||||
|
||||
return SignalStorageRecord.forAccount(account.build())
|
||||
return SignalStorageRecord.forAccount(accountRecord.toSignalAccountRecord(StorageId.forAccount(storageId)))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -188,62 +195,56 @@ object StorageSyncHelper {
|
||||
fun applyAccountStorageSyncUpdates(context: Context, self: Recipient, update: StorageRecordUpdate<SignalAccountRecord>, fetchProfile: Boolean) {
|
||||
SignalDatabase.recipients.applyStorageSyncAccountUpdate(update)
|
||||
|
||||
TextSecurePreferences.setReadReceiptsEnabled(context, update.new.isReadReceiptsEnabled)
|
||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, update.new.isTypingIndicatorsEnabled)
|
||||
TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, update.new.isSealedSenderIndicatorsEnabled)
|
||||
SignalStore.settings.isLinkPreviewsEnabled = update.new.isLinkPreviewsEnabled
|
||||
SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode = if (update.new.isPhoneNumberUnlisted) PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE else PhoneNumberDiscoverabilityMode.DISCOVERABLE
|
||||
SignalStore.phoneNumberPrivacy.phoneNumberSharingMode = StorageSyncModels.remoteToLocalPhoneNumberSharingMode(update.new.phoneNumberSharingMode)
|
||||
SignalStore.settings.isPreferSystemContactPhotos = update.new.isPreferContactAvatars
|
||||
SignalStore.payments.setEnabledAndEntropy(update.new.payments.isEnabled, Entropy.fromBytes(update.new.payments.entropy.orElse(null)))
|
||||
SignalStore.settings.universalExpireTimer = update.new.universalExpireTimer
|
||||
SignalStore.emoji.reactions = update.new.defaultReactions
|
||||
SignalStore.inAppPayments.setDisplayBadgesOnProfile(update.new.isDisplayBadgesOnProfile)
|
||||
SignalStore.settings.setKeepMutedChatsArchived(update.new.isKeepMutedChatsArchived)
|
||||
SignalStore.story.userHasBeenNotifiedAboutStories = update.new.hasSetMyStoriesPrivacy()
|
||||
SignalStore.story.userHasViewedOnboardingStory = update.new.hasViewedOnboardingStory()
|
||||
SignalStore.story.isFeatureDisabled = update.new.isStoriesDisabled
|
||||
SignalStore.story.userHasSeenGroupStoryEducationSheet = update.new.hasSeenGroupStoryEducationSheet()
|
||||
SignalStore.uiHints.setHasCompletedUsernameOnboarding(update.new.hasCompletedUsernameOnboarding())
|
||||
TextSecurePreferences.setReadReceiptsEnabled(context, update.new.proto.readReceipts)
|
||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, update.new.proto.typingIndicators)
|
||||
TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, update.new.proto.sealedSenderIndicators)
|
||||
SignalStore.settings.isLinkPreviewsEnabled = update.new.proto.linkPreviews
|
||||
SignalStore.phoneNumberPrivacy.phoneNumberDiscoverabilityMode = if (update.new.proto.unlistedPhoneNumber) PhoneNumberDiscoverabilityMode.NOT_DISCOVERABLE else PhoneNumberDiscoverabilityMode.DISCOVERABLE
|
||||
SignalStore.phoneNumberPrivacy.phoneNumberSharingMode = StorageSyncModels.remoteToLocalPhoneNumberSharingMode(update.new.proto.phoneNumberSharingMode)
|
||||
SignalStore.settings.isPreferSystemContactPhotos = update.new.proto.preferContactAvatars
|
||||
SignalStore.payments.setEnabledAndEntropy(update.new.proto.payments?.enabled == true, Entropy.fromBytes(update.new.proto.payments?.entropy?.toByteArray()))
|
||||
SignalStore.settings.universalExpireTimer = update.new.proto.universalExpireTimer
|
||||
SignalStore.emoji.reactions = update.new.proto.preferredReactionEmoji
|
||||
SignalStore.inAppPayments.setDisplayBadgesOnProfile(update.new.proto.displayBadgesOnProfile)
|
||||
SignalStore.settings.setKeepMutedChatsArchived(update.new.proto.keepMutedChatsArchived)
|
||||
SignalStore.story.userHasBeenNotifiedAboutStories = update.new.proto.hasSetMyStoriesPrivacy
|
||||
SignalStore.story.userHasViewedOnboardingStory = update.new.proto.hasViewedOnboardingStory
|
||||
SignalStore.story.isFeatureDisabled = update.new.proto.storiesDisabled
|
||||
SignalStore.story.userHasSeenGroupStoryEducationSheet = update.new.proto.hasSeenGroupStoryEducationSheet
|
||||
SignalStore.uiHints.setHasCompletedUsernameOnboarding(update.new.proto.hasCompletedUsernameOnboarding)
|
||||
|
||||
if (update.new.storyViewReceiptsState == OptionalBool.UNSET) {
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.isReadReceiptsEnabled
|
||||
if (update.new.proto.storyViewReceiptsEnabled == OptionalBool.UNSET) {
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.proto.readReceipts
|
||||
} else {
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.storyViewReceiptsState == OptionalBool.ENABLED
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.proto.storyViewReceiptsEnabled == OptionalBool.ENABLED
|
||||
}
|
||||
|
||||
if (update.new.storyViewReceiptsState == OptionalBool.UNSET) {
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.isReadReceiptsEnabled
|
||||
} else {
|
||||
SignalStore.story.viewedReceiptsEnabled = update.new.storyViewReceiptsState == OptionalBool.ENABLED
|
||||
}
|
||||
|
||||
val remoteSubscriber = StorageSyncModels.remoteToLocalSubscriber(update.new.subscriber, InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
val remoteSubscriber = StorageSyncModels.remoteToLocalSubscriber(update.new.proto.subscriberId, update.new.proto.subscriberCurrencyCode, InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
if (remoteSubscriber != null) {
|
||||
setSubscriber(remoteSubscriber)
|
||||
}
|
||||
|
||||
if (update.new.isSubscriptionManuallyCancelled && !update.old.isSubscriptionManuallyCancelled) {
|
||||
if (update.new.proto.subscriptionManuallyCancelled && !update.old.proto.subscriptionManuallyCancelled) {
|
||||
SignalStore.inAppPayments.updateLocalStateForManualCancellation(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
}
|
||||
|
||||
if (fetchProfile && update.new.avatarUrlPath.isPresent) {
|
||||
AppDependencies.jobManager.add(RetrieveProfileAvatarJob(self, update.new.avatarUrlPath.get()))
|
||||
if (fetchProfile && update.new.proto.avatarUrlPath.isNotBlank()) {
|
||||
AppDependencies.jobManager.add(RetrieveProfileAvatarJob(self, update.new.proto.avatarUrlPath))
|
||||
}
|
||||
|
||||
if (update.new.username != update.old.username) {
|
||||
SignalStore.account.username = update.new.username
|
||||
if (update.new.proto.username != update.old.proto.username) {
|
||||
SignalStore.account.username = update.new.proto.username
|
||||
SignalStore.account.usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC
|
||||
SignalStore.account.usernameSyncErrorCount = 0
|
||||
}
|
||||
|
||||
if (update.new.usernameLink != null) {
|
||||
if (update.new.proto.usernameLink != null) {
|
||||
SignalStore.account.usernameLink = UsernameLinkComponents(
|
||||
update.new.usernameLink!!.entropy.toByteArray(),
|
||||
UuidUtil.parseOrThrow(update.new.usernameLink!!.serverId.toByteArray())
|
||||
update.new.proto.usernameLink!!.entropy.toByteArray(),
|
||||
UuidUtil.parseOrThrow(update.new.proto.usernameLink!!.serverId.toByteArray())
|
||||
)
|
||||
|
||||
SignalStore.misc.usernameQrCodeColorScheme = StorageSyncModels.remoteToLocalUsernameColor(update.new.usernameLink!!.color)
|
||||
SignalStore.misc.usernameQrCodeColorScheme = StorageSyncModels.remoteToLocalUsernameColor(update.new.proto.usernameLink!!.color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import okio.ByteString
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.isNotEmpty
|
||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
|
||||
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme
|
||||
import org.thoughtcrime.securesms.database.GroupTable.ShowAsStoryState
|
||||
@@ -16,7 +19,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalCallLinkRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalContactRecord
|
||||
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record
|
||||
@@ -82,18 +84,33 @@ object StorageSyncModels {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun localToRemotePinnedConversations(records: List<RecipientRecord>): List<SignalAccountRecord.PinnedConversation> {
|
||||
fun localToRemotePinnedConversations(records: List<RecipientRecord>): List<AccountRecord.PinnedConversation> {
|
||||
return records
|
||||
.filter { it.recipientType == RecipientType.GV1 || it.recipientType == RecipientType.GV2 || it.registered == RecipientTable.RegisteredState.REGISTERED }
|
||||
.map { localToRemotePinnedConversation(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun localToRemotePinnedConversation(settings: RecipientRecord): SignalAccountRecord.PinnedConversation {
|
||||
private fun localToRemotePinnedConversation(settings: RecipientRecord): AccountRecord.PinnedConversation {
|
||||
return when (settings.recipientType) {
|
||||
RecipientType.INDIVIDUAL -> SignalAccountRecord.PinnedConversation.forContact(SignalServiceAddress(settings.serviceId, settings.e164))
|
||||
RecipientType.GV1 -> SignalAccountRecord.PinnedConversation.forGroupV1(settings.groupId!!.requireV1().decodedId)
|
||||
RecipientType.GV2 -> SignalAccountRecord.PinnedConversation.forGroupV2(settings.syncExtras.groupMasterKey!!.serialize())
|
||||
RecipientType.INDIVIDUAL -> {
|
||||
AccountRecord.PinnedConversation(
|
||||
contact = AccountRecord.PinnedConversation.Contact(
|
||||
serviceId = settings.serviceId?.toString() ?: "",
|
||||
e164 = settings.e164 ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
RecipientType.GV1 -> {
|
||||
AccountRecord.PinnedConversation(
|
||||
legacyGroupId = settings.groupId!!.requireV1().decodedId.toByteString()
|
||||
)
|
||||
}
|
||||
RecipientType.GV2 -> {
|
||||
AccountRecord.PinnedConversation(
|
||||
groupMasterKey = settings.syncExtras.groupMasterKey!!.serialize().toByteString()
|
||||
)
|
||||
}
|
||||
else -> throw AssertionError("Unexpected group type!")
|
||||
}
|
||||
}
|
||||
@@ -271,33 +288,23 @@ object StorageSyncModels {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO - need to store the subscriber type
|
||||
*/
|
||||
fun localToRemoteSubscriber(subscriber: InAppPaymentSubscriberRecord?): SignalAccountRecord.Subscriber {
|
||||
return if (subscriber == null) {
|
||||
SignalAccountRecord.Subscriber(null, null)
|
||||
} else {
|
||||
SignalAccountRecord.Subscriber(subscriber.currency.currencyCode, subscriber.subscriberId.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
fun remoteToLocalSubscriber(
|
||||
subscriber: SignalAccountRecord.Subscriber,
|
||||
subscriberId: ByteString,
|
||||
subscriberCurrencyCode: String,
|
||||
type: InAppPaymentSubscriberRecord.Type
|
||||
): InAppPaymentSubscriberRecord? {
|
||||
if (subscriber.id.isPresent) {
|
||||
val subscriberId = SubscriberId.fromBytes(subscriber.id.get())
|
||||
if (subscriberId.isNotEmpty()) {
|
||||
val subscriberId = SubscriberId.fromBytes(subscriberId.toByteArray())
|
||||
val localSubscriberRecord = inAppPaymentSubscribers.getBySubscriberId(subscriberId)
|
||||
val requiresCancel = localSubscriberRecord != null && localSubscriberRecord.requiresCancel
|
||||
val paymentMethodType = localSubscriberRecord?.paymentMethodType ?: InAppPaymentData.PaymentMethodType.UNKNOWN
|
||||
|
||||
val currency: Currency
|
||||
if (subscriber.currencyCode.isEmpty) {
|
||||
if (subscriberCurrencyCode.isBlank()) {
|
||||
return null
|
||||
} else {
|
||||
try {
|
||||
currency = Currency.getInstance(subscriber.currencyCode.get())
|
||||
currency = Currency.getInstance(subscriberCurrencyCode)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public final class StorageSyncValidations {
|
||||
}
|
||||
}
|
||||
|
||||
if (insert.getAccount().isPresent() && !insert.getAccount().get().getProfileKey().isPresent()) {
|
||||
if (insert.getAccount().isPresent() && insert.getAccount().get().getProto().profileKey.size() == 0) {
|
||||
Log.w(TAG, "Uploading a null profile key in our AccountRecord!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user