Convert AccountRecordProcessor to kotlin.
This commit is contained in:
parent
4273d9e3d7
commit
0f8580c398
12 changed files with 448 additions and 411 deletions
|
@ -1,267 +0,0 @@
|
|||
package org.thoughtcrime.securesms.storage;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.StringUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord.PinnedConversation;
|
||||
import org.whispersystems.signalservice.api.util.OptionalUtil;
|
||||
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
|
||||
import org.whispersystems.signalservice.internal.storage.protos.OptionalBool;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Processes {@link SignalAccountRecord}s. Unlike some other {@link StorageRecordProcessor}s, this
|
||||
* one has some statefulness in order to reject all but one account record (since we should have
|
||||
* exactly one account record).
|
||||
*/
|
||||
public class AccountRecordProcessor extends DefaultStorageRecordProcessor<SignalAccountRecord> {
|
||||
|
||||
private static final String TAG = Log.tag(AccountRecordProcessor.class);
|
||||
|
||||
private final Context context;
|
||||
private final SignalAccountRecord localAccountRecord;
|
||||
private final Recipient self;
|
||||
|
||||
private boolean foundAccountRecord = false;
|
||||
|
||||
public AccountRecordProcessor(@NonNull Context context, @NonNull Recipient self) {
|
||||
this(context, self, StorageSyncHelper.buildAccountRecord(context, self).getAccount().get());
|
||||
}
|
||||
|
||||
AccountRecordProcessor(@NonNull Context context, @NonNull Recipient self, @NonNull SignalAccountRecord localAccountRecord) {
|
||||
this.context = context;
|
||||
this.self = self;
|
||||
this.localAccountRecord = localAccountRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to catch:
|
||||
* - Multiple account records
|
||||
*/
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalAccountRecord remote) {
|
||||
if (foundAccountRecord) {
|
||||
Log.w(TAG, "Found an additional account record! Considering it invalid.");
|
||||
return true;
|
||||
}
|
||||
|
||||
foundAccountRecord = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Optional<SignalAccountRecord> getMatching(@NonNull SignalAccountRecord record, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
return Optional.of(localAccountRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SignalAccountRecord merge(@NonNull SignalAccountRecord remote, @NonNull SignalAccountRecord local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
String givenName;
|
||||
String familyName;
|
||||
|
||||
if (remote.getGivenName().isPresent() || remote.getFamilyName().isPresent()) {
|
||||
givenName = remote.getGivenName().orElse("");
|
||||
familyName = remote.getFamilyName().orElse("");
|
||||
} else {
|
||||
givenName = local.getGivenName().orElse("");
|
||||
familyName = local.getFamilyName().orElse("");
|
||||
}
|
||||
|
||||
SignalAccountRecord.Payments payments;
|
||||
|
||||
if (remote.getPayments().getEntropy().isPresent()) {
|
||||
payments = remote.getPayments();
|
||||
} else {
|
||||
payments = local.getPayments();
|
||||
}
|
||||
|
||||
SignalAccountRecord.Subscriber subscriber;
|
||||
|
||||
if (remote.getSubscriber().getId().isPresent()) {
|
||||
subscriber = remote.getSubscriber();
|
||||
} else {
|
||||
subscriber = local.getSubscriber();
|
||||
}
|
||||
|
||||
SignalAccountRecord.Subscriber backupsSubscriber;
|
||||
|
||||
if (remote.getSubscriber().getId().isPresent()) {
|
||||
backupsSubscriber = remote.getSubscriber();
|
||||
} else {
|
||||
backupsSubscriber = local.getSubscriber();
|
||||
}
|
||||
|
||||
OptionalBool storyViewReceiptsState;
|
||||
if (remote.getStoryViewReceiptsState() == OptionalBool.UNSET) {
|
||||
storyViewReceiptsState = local.getStoryViewReceiptsState();
|
||||
} else {
|
||||
storyViewReceiptsState = remote.getStoryViewReceiptsState();
|
||||
}
|
||||
|
||||
byte[] unknownFields = remote.serializeUnknownFields();
|
||||
String avatarUrlPath = OptionalUtil.or(remote.getAvatarUrlPath(), local.getAvatarUrlPath()).orElse("");
|
||||
byte[] profileKey = OptionalUtil.or(remote.getProfileKey(), local.getProfileKey()).orElse(null);
|
||||
boolean noteToSelfArchived = remote.isNoteToSelfArchived();
|
||||
boolean noteToSelfForcedUnread = remote.isNoteToSelfForcedUnread();
|
||||
boolean readReceipts = remote.isReadReceiptsEnabled();
|
||||
boolean typingIndicators = remote.isTypingIndicatorsEnabled();
|
||||
boolean sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled();
|
||||
boolean linkPreviews = remote.isLinkPreviewsEnabled();
|
||||
boolean unlisted = remote.isPhoneNumberUnlisted();
|
||||
List<PinnedConversation> pinnedConversations = remote.getPinnedConversations();
|
||||
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode = remote.getPhoneNumberSharingMode();
|
||||
boolean preferContactAvatars = remote.isPreferContactAvatars();
|
||||
int universalExpireTimer = remote.getUniversalExpireTimer();
|
||||
boolean primarySendsSms = SignalStore.account().isPrimaryDevice() ? local.isPrimarySendsSms() : remote.isPrimarySendsSms();
|
||||
String e164 = SignalStore.account().isPrimaryDevice() ? local.getE164() : remote.getE164();
|
||||
List<String> defaultReactions = remote.getDefaultReactions().size() > 0 ? remote.getDefaultReactions() : local.getDefaultReactions();
|
||||
boolean displayBadgesOnProfile = remote.isDisplayBadgesOnProfile();
|
||||
boolean subscriptionManuallyCancelled = remote.isSubscriptionManuallyCancelled();
|
||||
boolean keepMutedChatsArchived = remote.isKeepMutedChatsArchived();
|
||||
boolean hasSetMyStoriesPrivacy = remote.hasSetMyStoriesPrivacy();
|
||||
boolean hasViewedOnboardingStory = remote.hasViewedOnboardingStory() || local.hasViewedOnboardingStory();
|
||||
boolean storiesDisabled = remote.isStoriesDisabled();
|
||||
boolean hasSeenGroupStoryEducation = remote.hasSeenGroupStoryEducationSheet() || local.hasSeenGroupStoryEducationSheet();
|
||||
boolean hasSeenUsernameOnboarding = remote.hasCompletedUsernameOnboarding() || local.hasCompletedUsernameOnboarding();
|
||||
String username = remote.getUsername();
|
||||
AccountRecord.UsernameLink usernameLink = remote.getUsernameLink();
|
||||
boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted, pinnedConversations, preferContactAvatars, payments, universalExpireTimer, primarySendsSms, e164, defaultReactions, subscriber, displayBadgesOnProfile, subscriptionManuallyCancelled, keepMutedChatsArchived, hasSetMyStoriesPrivacy, hasViewedOnboardingStory, hasSeenUsernameOnboarding, storiesDisabled, storyViewReceiptsState, username, usernameLink, backupsSubscriber);
|
||||
boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted, pinnedConversations, preferContactAvatars, payments, universalExpireTimer, primarySendsSms, e164, defaultReactions, subscriber, displayBadgesOnProfile, subscriptionManuallyCancelled, keepMutedChatsArchived, hasSetMyStoriesPrivacy, hasViewedOnboardingStory, hasSeenUsernameOnboarding, storiesDisabled, storyViewReceiptsState, username, usernameLink, backupsSubscriber);
|
||||
|
||||
if (matchesRemote) {
|
||||
return remote;
|
||||
} else if (matchesLocal) {
|
||||
return local;
|
||||
} else {
|
||||
SignalAccountRecord.Builder builder = new 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.getEntropy().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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void insertLocal(@NonNull SignalAccountRecord record) {
|
||||
throw new UnsupportedOperationException("We should always have a local AccountRecord, so we should never been inserting a new one.");
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLocal(@NonNull StorageRecordUpdate<SignalAccountRecord> update) {
|
||||
StorageSyncHelper.applyAccountStorageSyncUpdates(context, self, update, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(@NonNull SignalAccountRecord lhs, @NonNull SignalAccountRecord rhs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean doParamsMatch(@NonNull SignalAccountRecord contact,
|
||||
@Nullable byte[] unknownFields,
|
||||
@NonNull String givenName,
|
||||
@NonNull String familyName,
|
||||
@NonNull String avatarUrlPath,
|
||||
@Nullable byte[] profileKey,
|
||||
boolean noteToSelfArchived,
|
||||
boolean noteToSelfForcedUnread,
|
||||
boolean readReceipts,
|
||||
boolean typingIndicators,
|
||||
boolean sealedSenderIndicators,
|
||||
boolean linkPreviewsEnabled,
|
||||
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode,
|
||||
boolean unlistedPhoneNumber,
|
||||
@NonNull List<PinnedConversation> pinnedConversations,
|
||||
boolean preferContactAvatars,
|
||||
SignalAccountRecord.Payments payments,
|
||||
int universalExpireTimer,
|
||||
boolean primarySendsSms,
|
||||
String e164,
|
||||
@NonNull List <String> defaultReactions,
|
||||
@NonNull SignalAccountRecord.Subscriber subscriber,
|
||||
boolean displayBadgesOnProfile,
|
||||
boolean subscriptionManuallyCancelled,
|
||||
boolean keepMutedChatsArchived,
|
||||
boolean hasSetMyStoriesPrivacy,
|
||||
boolean hasViewedOnboardingStory,
|
||||
boolean hasCompletedUsernameOnboarding,
|
||||
boolean storiesDisabled,
|
||||
@NonNull OptionalBool storyViewReceiptsState,
|
||||
@Nullable String username,
|
||||
@Nullable AccountRecord.UsernameLink usernameLink,
|
||||
@NonNull SignalAccountRecord.Subscriber backupsSubscriber)
|
||||
{
|
||||
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
||||
Objects.equals(contact.getGivenName().orElse(""), givenName) &&
|
||||
Objects.equals(contact.getFamilyName().orElse(""), familyName) &&
|
||||
Objects.equals(contact.getAvatarUrlPath().orElse(""), avatarUrlPath) &&
|
||||
Objects.equals(contact.getPayments(), payments) &&
|
||||
Objects.equals(contact.getE164(), e164) &&
|
||||
Objects.equals(contact.getDefaultReactions(), defaultReactions) &&
|
||||
Arrays.equals(contact.getProfileKey().orElse(null), profileKey) &&
|
||||
contact.isNoteToSelfArchived() == noteToSelfArchived &&
|
||||
contact.isNoteToSelfForcedUnread() == noteToSelfForcedUnread &&
|
||||
contact.isReadReceiptsEnabled() == readReceipts &&
|
||||
contact.isTypingIndicatorsEnabled() == typingIndicators &&
|
||||
contact.isSealedSenderIndicatorsEnabled() == sealedSenderIndicators &&
|
||||
contact.isLinkPreviewsEnabled() == linkPreviewsEnabled &&
|
||||
contact.getPhoneNumberSharingMode() == phoneNumberSharingMode &&
|
||||
contact.isPhoneNumberUnlisted() == unlistedPhoneNumber &&
|
||||
contact.isPreferContactAvatars() == preferContactAvatars &&
|
||||
contact.getUniversalExpireTimer() == universalExpireTimer &&
|
||||
contact.isPrimarySendsSms() == primarySendsSms &&
|
||||
Objects.equals(contact.getPinnedConversations(), pinnedConversations) &&
|
||||
Objects.equals(contact.getSubscriber(), subscriber) &&
|
||||
contact.isDisplayBadgesOnProfile() == displayBadgesOnProfile &&
|
||||
contact.isSubscriptionManuallyCancelled() == subscriptionManuallyCancelled &&
|
||||
contact.isKeepMutedChatsArchived() == keepMutedChatsArchived &&
|
||||
contact.hasSetMyStoriesPrivacy() == hasSetMyStoriesPrivacy &&
|
||||
contact.hasViewedOnboardingStory() == hasViewedOnboardingStory &&
|
||||
contact.hasCompletedUsernameOnboarding() == hasCompletedUsernameOnboarding &&
|
||||
contact.isStoriesDisabled() == storiesDisabled &&
|
||||
contact.getStoryViewReceiptsState().equals(storyViewReceiptsState) &&
|
||||
Objects.equals(contact.getUsername(), username) &&
|
||||
Objects.equals(contact.getUsernameLink(), usernameLink) &&
|
||||
Objects.equals(contact.getBackupsSubscriber(), backupsSubscriber);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import android.content.Context
|
||||
import org.signal.core.util.logging.Log
|
||||
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.internal.storage.protos.OptionalBool
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Processes [SignalAccountRecord]s. Unlike some other [StorageRecordProcessor]s, this
|
||||
* one has some statefulness in order to reject all but one account record (since we should have
|
||||
* exactly one account record).
|
||||
*/
|
||||
class AccountRecordProcessor(
|
||||
private val context: Context,
|
||||
private val self: Recipient,
|
||||
private val localAccountRecord: SignalAccountRecord
|
||||
) : DefaultStorageRecordProcessor<SignalAccountRecord>() {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(AccountRecordProcessor::class.java)
|
||||
}
|
||||
|
||||
private var foundAccountRecord = false
|
||||
|
||||
constructor(context: Context, self: Recipient) : this(context, self, buildAccountRecord(context, self).account.get())
|
||||
|
||||
/**
|
||||
* We want to catch:
|
||||
* - Multiple account records
|
||||
*/
|
||||
override fun isInvalid(remote: SignalAccountRecord): Boolean {
|
||||
if (foundAccountRecord) {
|
||||
Log.w(TAG, "Found an additional account record! Considering it invalid.")
|
||||
return true
|
||||
}
|
||||
|
||||
foundAccountRecord = true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getMatching(record: 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
|
||||
|
||||
if (remote.givenName.isPresent || remote.familyName.isPresent) {
|
||||
givenName = remote.givenName.orElse("")
|
||||
familyName = remote.familyName.orElse("")
|
||||
} else {
|
||||
givenName = local.givenName.orElse("")
|
||||
familyName = local.familyName.orElse("")
|
||||
}
|
||||
|
||||
val payments = if (remote.payments.entropy.isPresent) {
|
||||
remote.payments
|
||||
} else {
|
||||
local.payments
|
||||
}
|
||||
|
||||
val subscriber = if (remote.subscriber.id.isPresent) {
|
||||
remote.subscriber
|
||||
} else {
|
||||
local.subscriber
|
||||
}
|
||||
|
||||
val backupsSubscriber = if (remote.subscriber.id.isPresent) {
|
||||
remote.subscriber
|
||||
} else {
|
||||
local.subscriber
|
||||
}
|
||||
val storyViewReceiptsState = if (remote.storyViewReceiptsState == OptionalBool.UNSET) {
|
||||
local.storyViewReceiptsState
|
||||
} else {
|
||||
remote.storyViewReceiptsState
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
if (matchesRemote) {
|
||||
return remote
|
||||
} else if (matchesLocal) {
|
||||
return 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()
|
||||
}
|
||||
}
|
||||
|
||||
override fun insertLocal(record: SignalAccountRecord) {
|
||||
throw UnsupportedOperationException("We should always have a local AccountRecord, so we should never been inserting a new one.")
|
||||
}
|
||||
|
||||
override fun updateLocal(update: StorageRecordUpdate<SignalAccountRecord>) {
|
||||
applyAccountStorageSyncUpdates(context, self, update, true)
|
||||
}
|
||||
|
||||
override fun compare(lhs: SignalAccountRecord, rhs: SignalAccountRecord): Int {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -26,11 +26,11 @@ internal class CallLinkRecordProcessor : DefaultStorageRecordProcessor<SignalCal
|
|||
}
|
||||
}
|
||||
|
||||
internal override fun isInvalid(remote: SignalCallLinkRecord): Boolean {
|
||||
override fun isInvalid(remote: SignalCallLinkRecord): Boolean {
|
||||
return remote.adminPassKey.isNotEmpty() && remote.deletionTimestamp > 0L
|
||||
}
|
||||
|
||||
internal override fun getMatching(remote: SignalCallLinkRecord, keyGenerator: StorageKeyGenerator): Optional<SignalCallLinkRecord> {
|
||||
override fun getMatching(remote: SignalCallLinkRecord, keyGenerator: StorageKeyGenerator): Optional<SignalCallLinkRecord> {
|
||||
Log.d(TAG, "Attempting to get matching record...")
|
||||
val rootKey = CallLinkRootKey(remote.rootKey)
|
||||
val roomId = CallLinkRoomId.fromCallLinkRootKey(rootKey)
|
||||
|
@ -52,7 +52,7 @@ internal class CallLinkRecordProcessor : DefaultStorageRecordProcessor<SignalCal
|
|||
* An earlier deletion takes precedence over a later deletion
|
||||
* Other fields should not change, except for the clearing of the admin passkey on deletion
|
||||
*/
|
||||
internal override fun merge(remote: SignalCallLinkRecord, local: SignalCallLinkRecord, keyGenerator: StorageKeyGenerator): SignalCallLinkRecord {
|
||||
override fun merge(remote: SignalCallLinkRecord, local: SignalCallLinkRecord, keyGenerator: StorageKeyGenerator): SignalCallLinkRecord {
|
||||
return if (remote.isDeleted() && local.isDeleted()) {
|
||||
if (remote.deletionTimestamp < local.deletionTimestamp) {
|
||||
remote
|
||||
|
|
|
@ -61,8 +61,9 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
* The reasons are nuanced, but the TL;DR is that we want to split unregistered users into separate rows so that a user
|
||||
* could re-register and get a different ACI.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void process(@NonNull Collection<SignalContactRecord> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException {
|
||||
public void process(@NonNull Collection<? extends SignalContactRecord> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException {
|
||||
List<SignalContactRecord> unregisteredAciOnly = new ArrayList<>();
|
||||
|
||||
for (SignalContactRecord remoteRecord : remoteRecords) {
|
||||
|
@ -92,7 +93,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
* Note: This method could be written more succinctly, but the logs are useful :)
|
||||
*/
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalContactRecord remote) {
|
||||
public boolean isInvalid(@NonNull SignalContactRecord remote) {
|
||||
boolean hasAci = remote.getAci().isPresent() && remote.getAci().get().isValid();
|
||||
boolean hasPni = remote.getPni().isPresent() && remote.getPni().get().isValid();
|
||||
|
||||
|
@ -114,7 +115,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull Optional<SignalContactRecord> getMatching(@NonNull SignalContactRecord remote, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull Optional<SignalContactRecord> getMatching(@NonNull SignalContactRecord remote, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
Optional<RecipientId> found = remote.getAci().isPresent() ? recipientTable.getByAci(remote.getAci().get()) : Optional.empty();
|
||||
|
||||
if (found.isEmpty() && remote.getNumber().isPresent()) {
|
||||
|
@ -141,7 +142,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull SignalContactRecord merge(@NonNull SignalContactRecord remote, @NonNull SignalContactRecord local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull SignalContactRecord merge(@NonNull SignalContactRecord remote, @NonNull SignalContactRecord local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
String profileGivenName;
|
||||
String profileFamilyName;
|
||||
|
||||
|
@ -258,12 +259,12 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
}
|
||||
|
||||
@Override
|
||||
void insertLocal(@NonNull SignalContactRecord record) {
|
||||
public void insertLocal(@NonNull SignalContactRecord record) {
|
||||
recipientTable.applyStorageSyncContactInsert(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLocal(@NonNull StorageRecordUpdate<SignalContactRecord> update) {
|
||||
public void updateLocal(@NonNull StorageRecordUpdate<SignalContactRecord> update) {
|
||||
recipientTable.applyStorageSyncContactUpdate(update);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
package org.thoughtcrime.securesms.storage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* An implementation of {@link StorageRecordProcessor} that solidifies a pattern and reduces
|
||||
* duplicate code in individual implementations.
|
||||
*
|
||||
* Concerning the implementation of {@link #compare(Object, Object)}, it's purpose is to detect if
|
||||
* two items would map to the same logical entity (i.e. they would correspond to the same record in
|
||||
* our local store). We use it for a {@link 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 extends SignalRecord> implements StorageRecordProcessor<E>, Comparator<E> {
|
||||
|
||||
private static final String TAG = Log.tag(DefaultStorageRecordProcessor.class);
|
||||
|
||||
/**
|
||||
* One type of invalid remote data this handles is two records mapping to the same local data. We
|
||||
* have to trim this bad data out, because if we don't, we'll upload an ID set that only has one
|
||||
* of the IDs in it, but won't properly delete the dupes, which will then fail our validation
|
||||
* checks.
|
||||
*
|
||||
* This is a bit tricky -- as we process records, ID's are written back to the local store, so we
|
||||
* can't easily be like "oh multiple records are mapping to the same local storage ID". And in
|
||||
* general we rely on SignalRecords to implement an equals() that includes the StorageId, so using
|
||||
* a regular set is out. Instead, we use a {@link TreeSet}, which allows us to define a custom
|
||||
* comparator for checking equality. Then we delegate to the subclass to tell us if two items are
|
||||
* the same based on their actual data (i.e. two contacts having the same UUID, or two groups
|
||||
* having the same MasterKey).
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull Collection<E> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException {
|
||||
Set<E> matchedRecords = new TreeSet<>(this);
|
||||
int i = 0;
|
||||
|
||||
for (E remote : remoteRecords) {
|
||||
if (isInvalid(remote)) {
|
||||
warn(i, remote, "Found invalid key! Ignoring it.");
|
||||
} else {
|
||||
Optional<E> local = getMatching(remote, keyGenerator);
|
||||
|
||||
if (local.isPresent()) {
|
||||
E merged = 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.");
|
||||
} else {
|
||||
matchedRecords.add(local.get());
|
||||
|
||||
if (!merged.equals(remote)) {
|
||||
info(i, remote, "[Remote Update] " + new StorageRecordUpdate<>(remote, merged).toString());
|
||||
}
|
||||
|
||||
if (!merged.equals(local.get())) {
|
||||
StorageRecordUpdate<E> update = new StorageRecordUpdate<>(local.get(), merged);
|
||||
info(i, remote, "[Local Update] " + update.toString());
|
||||
updateLocal(update);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info(i, remote, "No matching local record. Inserting.");
|
||||
insertLocal(remote);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void info(int i, E record, String message) {
|
||||
Log.i(TAG, "[" + i + "][" + record.getClass().getSimpleName() + "] " + message);
|
||||
}
|
||||
|
||||
private void warn(int i, E record, String message) {
|
||||
Log.w(TAG, "[" + i + "][" + record.getClass().getSimpleName() + "] " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the record is invalid and should be removed from storage service, otherwise false.
|
||||
*/
|
||||
abstract boolean isInvalid(@NonNull E remote);
|
||||
|
||||
/**
|
||||
* Only records that pass the validity check (i.e. return false from {@link #isInvalid(SignalRecord)}
|
||||
* make it to here, so you can assume all records are valid.
|
||||
*/
|
||||
abstract @NonNull Optional<E> getMatching(@NonNull E remote, @NonNull StorageKeyGenerator keyGenerator);
|
||||
|
||||
abstract @NonNull E merge(@NonNull E remote, @NonNull E local, @NonNull StorageKeyGenerator keyGenerator);
|
||||
abstract void insertLocal(@NonNull E record) throws IOException;
|
||||
abstract void updateLocal(@NonNull StorageRecordUpdate<E> update);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import java.util.TreeSet
|
||||
|
||||
/**
|
||||
* An implementation of [StorageRecordProcessor] that solidifies a pattern and reduces
|
||||
* duplicate code in individual implementations.
|
||||
*
|
||||
* Concerning the implementation of [.compare], it's purpose is to detect if
|
||||
* two items would map to the same logical entity (i.e. they would correspond to the same record in
|
||||
* 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> {
|
||||
companion object {
|
||||
private val TAG = Log.tag(DefaultStorageRecordProcessor::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* One type of invalid remote data this handles is two records mapping to the same local data. We
|
||||
* have to trim this bad data out, because if we don't, we'll upload an ID set that only has one
|
||||
* of the IDs in it, but won't properly delete the dupes, which will then fail our validation
|
||||
* checks.
|
||||
*
|
||||
* This is a bit tricky -- as we process records, ID's are written back to the local store, so we
|
||||
* can't easily be like "oh multiple records are mapping to the same local storage ID". And in
|
||||
* general we rely on SignalRecords to implement an equals() that includes the StorageId, so using
|
||||
* a regular set is out. Instead, we use a [TreeSet], which allows us to define a custom
|
||||
* comparator for checking equality. Then we delegate to the subclass to tell us if two items are
|
||||
* the same based on their actual data (i.e. two contacts having the same UUID, or two groups
|
||||
* having the same MasterKey).
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
override fun process(remoteRecords: Collection<E>, keyGenerator: StorageKeyGenerator) {
|
||||
val matchedRecords: MutableSet<E> = TreeSet(this)
|
||||
var i = 0
|
||||
|
||||
for (remote in remoteRecords) {
|
||||
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)
|
||||
|
||||
if (matchedRecords.contains(local.get())) {
|
||||
warn(i, remote, "Multiple remote records map to the same local record! Ignoring this one.")
|
||||
} else {
|
||||
matchedRecords.add(local.get())
|
||||
|
||||
if (merged != remote) {
|
||||
info(i, remote, "[Remote Update] " + StorageRecordUpdate(remote, merged).toString())
|
||||
}
|
||||
|
||||
if (merged != local.get()) {
|
||||
val update = StorageRecordUpdate(local.get(), merged)
|
||||
info(i, remote, "[Local Update] $update")
|
||||
updateLocal(update)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info(i, remote, "No matching local record. Inserting.")
|
||||
insertLocal(remote)
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
private fun info(i: Int, record: E, message: String) {
|
||||
Log.i(TAG, "[$i][${record.javaClass.getSimpleName()}] $message")
|
||||
}
|
||||
|
||||
private fun warn(i: Int, record: E, message: String) {
|
||||
Log.w(TAG, "[$i][${record.javaClass.getSimpleName()}] $message")
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the record is invalid and should be removed from storage service, otherwise false.
|
||||
*/
|
||||
abstract fun isInvalid(remote: E): Boolean
|
||||
|
||||
/**
|
||||
* Only records that pass the validity check (i.e. return false from [.isInvalid]
|
||||
* make it to here, so you can assume all records are valid.
|
||||
*/
|
||||
abstract fun getMatching(remote: E, keyGenerator: StorageKeyGenerator): Optional<E>
|
||||
|
||||
abstract fun merge(remote: E, local: E, keyGenerator: StorageKeyGenerator): E
|
||||
|
||||
@Throws(IOException::class)
|
||||
abstract fun insertLocal(record: E)
|
||||
abstract fun updateLocal(update: StorageRecordUpdate<E>)
|
||||
}
|
|
@ -45,7 +45,7 @@ public final class GroupV1RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
* Note: This method could be written more succinctly, but the logs are useful :)
|
||||
*/
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalGroupV1Record remote) {
|
||||
public boolean isInvalid(@NonNull SignalGroupV1Record remote) {
|
||||
try {
|
||||
GroupId.V1 id = GroupId.v1(remote.getGroupId());
|
||||
Optional<GroupRecord> v2Record = groupDatabase.getGroup(id.deriveV2MigrationGroupId());
|
||||
|
@ -63,7 +63,7 @@ public final class GroupV1RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull Optional<SignalGroupV1Record> getMatching(@NonNull SignalGroupV1Record record, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull Optional<SignalGroupV1Record> getMatching(@NonNull SignalGroupV1Record record, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
GroupId.V1 groupId = GroupId.v1orThrow(record.getGroupId());
|
||||
|
||||
Optional<RecipientId> recipientId = recipientTable.getByGroupId(groupId);
|
||||
|
@ -74,7 +74,7 @@ public final class GroupV1RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull SignalGroupV1Record merge(@NonNull SignalGroupV1Record remote, @NonNull SignalGroupV1Record local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull SignalGroupV1Record merge(@NonNull SignalGroupV1Record remote, @NonNull SignalGroupV1Record local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
byte[] unknownFields = remote.serializeUnknownFields();
|
||||
boolean blocked = remote.isBlocked();
|
||||
boolean profileSharing = remote.isProfileSharingEnabled();
|
||||
|
@ -101,12 +101,12 @@ public final class GroupV1RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
void insertLocal(@NonNull SignalGroupV1Record record) {
|
||||
public void insertLocal(@NonNull SignalGroupV1Record record) {
|
||||
recipientTable.applyStorageSyncGroupV1Insert(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLocal(@NonNull StorageRecordUpdate<SignalGroupV1Record> update) {
|
||||
public void updateLocal(@NonNull StorageRecordUpdate<SignalGroupV1Record> update) {
|
||||
recipientTable.applyStorageSyncGroupV1Update(update);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@ public final class GroupV2RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalGroupV2Record remote) {
|
||||
public boolean isInvalid(@NonNull SignalGroupV2Record remote) {
|
||||
return remote.getMasterKeyBytes().length != GroupMasterKey.SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull Optional<SignalGroupV2Record> getMatching(@NonNull SignalGroupV2Record record, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull Optional<SignalGroupV2Record> getMatching(@NonNull SignalGroupV2Record record, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
GroupId.V2 groupId = GroupId.v2(record.getMasterKeyOrThrow());
|
||||
|
||||
Optional<RecipientId> recipientId = recipientTable.getByGroupId(groupId);
|
||||
|
@ -64,7 +64,7 @@ public final class GroupV2RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull SignalGroupV2Record merge(@NonNull SignalGroupV2Record remote, @NonNull SignalGroupV2Record local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull SignalGroupV2Record merge(@NonNull SignalGroupV2Record remote, @NonNull SignalGroupV2Record local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
byte[] unknownFields = remote.serializeUnknownFields();
|
||||
boolean blocked = remote.isBlocked();
|
||||
boolean profileSharing = remote.isProfileSharingEnabled();
|
||||
|
@ -97,12 +97,12 @@ public final class GroupV2RecordProcessor extends DefaultStorageRecordProcessor<
|
|||
}
|
||||
|
||||
@Override
|
||||
void insertLocal(@NonNull SignalGroupV2Record record) {
|
||||
public void insertLocal(@NonNull SignalGroupV2Record record) {
|
||||
recipientTable.applyStorageSyncGroupV2Insert(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLocal(@NonNull StorageRecordUpdate<SignalGroupV2Record> update) {
|
||||
public void updateLocal(@NonNull StorageRecordUpdate<SignalGroupV2Record> update) {
|
||||
recipientTable.applyStorageSyncGroupV2Update(update);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package org.thoughtcrime.securesms.storage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Handles processing a remote record, which involves applying any local changes that need to be
|
||||
* made based on the remote records.
|
||||
*/
|
||||
public interface StorageRecordProcessor<E extends SignalRecord> {
|
||||
void process(@NonNull Collection<E> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.thoughtcrime.securesms.storage
|
||||
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord
|
||||
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?> {
|
||||
@Throws(IOException::class)
|
||||
fun process(remoteRecords: Collection<E>, keyGenerator: StorageKeyGenerator)
|
||||
}
|
|
@ -14,7 +14,6 @@ public class StorageRecordUpdate<E extends SignalRecord> {
|
|||
private final E oldRecord;
|
||||
private final E newRecord;
|
||||
|
||||
@VisibleForTesting
|
||||
public StorageRecordUpdate(@NonNull E oldRecord, @NonNull E newRecord) {
|
||||
this.oldRecord = oldRecord;
|
||||
this.newRecord = newRecord;
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
@ -35,7 +36,7 @@ public class StoryDistributionListRecordProcessor extends DefaultStorageRecordPr
|
|||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalStoryDistributionListRecord remote) {
|
||||
public boolean isInvalid(@NonNull SignalStoryDistributionListRecord remote) {
|
||||
UUID remoteUuid = UuidUtil.parseOrNull(remote.getIdentifier());
|
||||
if (remoteUuid == null) {
|
||||
Log.d(TAG, "Bad distribution list identifier -- marking as invalid");
|
||||
|
@ -68,7 +69,7 @@ public class StoryDistributionListRecordProcessor extends DefaultStorageRecordPr
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull Optional<SignalStoryDistributionListRecord> getMatching(@NonNull SignalStoryDistributionListRecord remote, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull Optional<SignalStoryDistributionListRecord> getMatching(@NonNull SignalStoryDistributionListRecord remote, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
Log.d(TAG, "Attempting to get matching record...");
|
||||
RecipientId matching = SignalDatabase.distributionLists().getRecipientIdForSyncRecord(remote);
|
||||
if (matching == null && UuidUtil.parseOrThrow(remote.getIdentifier()).equals(DistributionId.MY_STORY.asUuid())) {
|
||||
|
@ -104,7 +105,7 @@ public class StoryDistributionListRecordProcessor extends DefaultStorageRecordPr
|
|||
}
|
||||
|
||||
@Override
|
||||
@NonNull SignalStoryDistributionListRecord merge(@NonNull SignalStoryDistributionListRecord remote, @NonNull SignalStoryDistributionListRecord local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
public @NonNull SignalStoryDistributionListRecord merge(@NonNull SignalStoryDistributionListRecord remote, @NonNull SignalStoryDistributionListRecord local, @NonNull StorageKeyGenerator keyGenerator) {
|
||||
byte[] unknownFields = remote.serializeUnknownFields();
|
||||
byte[] identifier = remote.getIdentifier();
|
||||
String name = remote.getName();
|
||||
|
@ -133,12 +134,12 @@ public class StoryDistributionListRecordProcessor extends DefaultStorageRecordPr
|
|||
}
|
||||
|
||||
@Override
|
||||
void insertLocal(@NonNull SignalStoryDistributionListRecord record) throws IOException {
|
||||
public void insertLocal(@NonNull SignalStoryDistributionListRecord record) throws IOException {
|
||||
SignalDatabase.distributionLists().applyStorageSyncStoryDistributionListInsert(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateLocal(@NonNull StorageRecordUpdate<SignalStoryDistributionListRecord> update) {
|
||||
public void updateLocal(@NonNull StorageRecordUpdate<SignalStoryDistributionListRecord> update) {
|
||||
SignalDatabase.distributionLists().applyStorageSyncStoryDistributionListUpdate(update);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue