Convert StorageSyncModels to kotlin.

This commit is contained in:
Greyson Parrelli 2024-11-07 13:37:11 -05:00
parent 89767cc260
commit a79b4c3ba0
2 changed files with 311 additions and 347 deletions

View file

@ -1,347 +0,0 @@
package org.thoughtcrime.securesms.storage;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme;
import org.thoughtcrime.securesms.database.CallLinkTable;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.IdentityTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListId;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord;
import org.thoughtcrime.securesms.database.model.RecipientRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId;
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;
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord;
import org.whispersystems.signalservice.api.subscriptions.SubscriberId;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState;
import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record;
import java.util.Currency;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public final class StorageSyncModels {
private StorageSyncModels() {}
public static @NonNull SignalStorageRecord localToRemoteRecord(@NonNull RecipientRecord settings) {
if (settings.getStorageId() == null) {
throw new AssertionError("Must have a storage key!");
}
return localToRemoteRecord(settings, settings.getStorageId());
}
public static @NonNull SignalStorageRecord localToRemoteRecord(@NonNull RecipientRecord settings, @NonNull GroupMasterKey groupMasterKey) {
if (settings.getStorageId() == null) {
throw new AssertionError("Must have a storage key!");
}
return SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, settings.getStorageId(), groupMasterKey));
}
public static @NonNull SignalStorageRecord localToRemoteRecord(@NonNull RecipientRecord settings, @NonNull byte[] rawStorageId) {
switch (settings.getRecipientType()) {
case INDIVIDUAL: return SignalStorageRecord.forContact(localToRemoteContact(settings, rawStorageId));
case GV1: return SignalStorageRecord.forGroupV1(localToRemoteGroupV1(settings, rawStorageId));
case GV2: return SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, rawStorageId, settings.getSyncExtras().getGroupMasterKey()));
case DISTRIBUTION_LIST: return SignalStorageRecord.forStoryDistributionList(localToRemoteStoryDistributionList(settings, rawStorageId));
case CALL_LINK: return SignalStorageRecord.forCallLink(localToRemoteCallLink(settings, rawStorageId));
default: throw new AssertionError("Unsupported type!");
}
}
public static AccountRecord.PhoneNumberSharingMode localToRemotePhoneNumberSharingMode(PhoneNumberPrivacyValues.PhoneNumberSharingMode phoneNumberPhoneNumberSharingMode) {
switch (phoneNumberPhoneNumberSharingMode) {
case DEFAULT : return AccountRecord.PhoneNumberSharingMode.NOBODY;
case EVERYBODY: return AccountRecord.PhoneNumberSharingMode.EVERYBODY;
case NOBODY : return AccountRecord.PhoneNumberSharingMode.NOBODY;
default : throw new AssertionError();
}
}
public static PhoneNumberPrivacyValues.PhoneNumberSharingMode remoteToLocalPhoneNumberSharingMode(AccountRecord.PhoneNumberSharingMode phoneNumberPhoneNumberSharingMode) {
switch (phoneNumberPhoneNumberSharingMode) {
case EVERYBODY : return PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY;
case NOBODY : return PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY;
default : return PhoneNumberPrivacyValues.PhoneNumberSharingMode.DEFAULT;
}
}
public static List<SignalAccountRecord.PinnedConversation> localToRemotePinnedConversations(@NonNull List<RecipientRecord> settings) {
return Stream.of(settings)
.filter(s -> s.getRecipientType() == RecipientTable.RecipientType.GV1 ||
s.getRecipientType() == RecipientTable.RecipientType.GV2 ||
s.getRegistered() == RecipientTable.RegisteredState.REGISTERED)
.map(StorageSyncModels::localToRemotePinnedConversation)
.toList();
}
private static @NonNull SignalAccountRecord.PinnedConversation localToRemotePinnedConversation(@NonNull RecipientRecord settings) {
switch (settings.getRecipientType()) {
case INDIVIDUAL: return SignalAccountRecord.PinnedConversation.forContact(new SignalServiceAddress(settings.getServiceId(), settings.getE164()));
case GV1: return SignalAccountRecord.PinnedConversation.forGroupV1(settings.getGroupId().requireV1().getDecodedId());
case GV2: return SignalAccountRecord.PinnedConversation.forGroupV2(settings.getSyncExtras().getGroupMasterKey().serialize());
default : throw new AssertionError("Unexpected group type!");
}
}
public static @NonNull AccountRecord.UsernameLink.Color localToRemoteUsernameColor(UsernameQrCodeColorScheme local) {
switch (local) {
case Blue: return AccountRecord.UsernameLink.Color.BLUE;
case White: return AccountRecord.UsernameLink.Color.WHITE;
case Grey: return AccountRecord.UsernameLink.Color.GREY;
case Tan: return AccountRecord.UsernameLink.Color.OLIVE;
case Green: return AccountRecord.UsernameLink.Color.GREEN;
case Orange: return AccountRecord.UsernameLink.Color.ORANGE;
case Pink: return AccountRecord.UsernameLink.Color.PINK;
case Purple: return AccountRecord.UsernameLink.Color.PURPLE;
default: return AccountRecord.UsernameLink.Color.BLUE;
}
}
public static @NonNull UsernameQrCodeColorScheme remoteToLocalUsernameColor(AccountRecord.UsernameLink.Color remote) {
switch (remote) {
case BLUE: return UsernameQrCodeColorScheme.Blue;
case WHITE: return UsernameQrCodeColorScheme.White;
case GREY: return UsernameQrCodeColorScheme.Grey;
case OLIVE: return UsernameQrCodeColorScheme.Tan;
case GREEN: return UsernameQrCodeColorScheme.Green;
case ORANGE: return UsernameQrCodeColorScheme.Orange;
case PINK: return UsernameQrCodeColorScheme.Pink;
case PURPLE: return UsernameQrCodeColorScheme.Purple;
default: return UsernameQrCodeColorScheme.Blue;
}
}
private static @NonNull SignalContactRecord localToRemoteContact(@NonNull RecipientRecord recipient, byte[] rawStorageId) {
if (recipient.getAci() == null && recipient.getPni() == null && recipient.getE164() == null) {
throw new AssertionError("Must have either a UUID or a phone number!");
}
boolean hideStory = recipient.getExtras() != null && recipient.getExtras().hideStory();
return new SignalContactRecord.Builder(rawStorageId, recipient.getAci(), recipient.getSyncExtras().getStorageProto())
.setE164(recipient.getE164())
.setPni(recipient.getPni())
.setProfileKey(recipient.getProfileKey())
.setProfileGivenName(recipient.getProfileName().getGivenName())
.setProfileFamilyName(recipient.getProfileName().getFamilyName())
.setSystemGivenName(recipient.getSystemProfileName().getGivenName())
.setSystemFamilyName(recipient.getSystemProfileName().getFamilyName())
.setSystemNickname(recipient.getSyncExtras().getSystemNickname())
.setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing() || recipient.getSystemContactUri() != null)
.setIdentityKey(recipient.getSyncExtras().getIdentityKey())
.setIdentityState(localToRemoteIdentityState(recipient.getSyncExtras().getIdentityStatus()))
.setArchived(recipient.getSyncExtras().isArchived())
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
.setMuteUntil(recipient.getMuteUntil())
.setHideStory(hideStory)
.setUnregisteredTimestamp(recipient.getSyncExtras().getUnregisteredTimestamp())
.setHidden(recipient.getHiddenState() != Recipient.HiddenState.NOT_HIDDEN)
.setUsername(recipient.getUsername())
.setPniSignatureVerified(recipient.getSyncExtras().getPniSignatureVerified())
.setNicknameGivenName(recipient.getNickname().getGivenName())
.setNicknameFamilyName(recipient.getNickname().getFamilyName())
.setNote(recipient.getNote())
.build();
}
private static @NonNull SignalGroupV1Record localToRemoteGroupV1(@NonNull RecipientRecord recipient, byte[] rawStorageId) {
GroupId groupId = recipient.getGroupId();
if (groupId == null) {
throw new AssertionError("Must have a groupId!");
}
if (!groupId.isV1()) {
throw new AssertionError("Group is not V1");
}
return new SignalGroupV1Record.Builder(rawStorageId, groupId.getDecodedId(), recipient.getSyncExtras().getStorageProto())
.setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing())
.setArchived(recipient.getSyncExtras().isArchived())
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
.setMuteUntil(recipient.getMuteUntil())
.build();
}
private static @NonNull SignalGroupV2Record localToRemoteGroupV2(@NonNull RecipientRecord recipient, byte[] rawStorageId, @NonNull GroupMasterKey groupMasterKey) {
GroupId groupId = recipient.getGroupId();
if (groupId == null) {
throw new AssertionError("Must have a groupId!");
}
if (!groupId.isV2()) {
throw new AssertionError("Group is not V2");
}
if (groupMasterKey == null) {
throw new AssertionError("Group master key not on recipient record");
}
boolean hideStory = recipient.getExtras() != null && recipient.getExtras().hideStory();
GroupTable.ShowAsStoryState showAsStoryState = SignalDatabase.groups().getShowAsStoryState(groupId);
GroupV2Record.StorySendMode storySendMode;
switch (showAsStoryState) {
case ALWAYS:
storySendMode = GroupV2Record.StorySendMode.ENABLED;
break;
case NEVER:
storySendMode = GroupV2Record.StorySendMode.DISABLED;
break;
default:
storySendMode = GroupV2Record.StorySendMode.DEFAULT;
}
return new SignalGroupV2Record.Builder(rawStorageId, groupMasterKey, recipient.getSyncExtras().getStorageProto())
.setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing())
.setArchived(recipient.getSyncExtras().isArchived())
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
.setMuteUntil(recipient.getMuteUntil())
.setNotifyForMentionsWhenMuted(recipient.getMentionSetting() == RecipientTable.MentionSetting.ALWAYS_NOTIFY)
.setHideStory(hideStory)
.setStorySendMode(storySendMode)
.build();
}
private static @NonNull SignalCallLinkRecord localToRemoteCallLink(@NonNull RecipientRecord recipient, @NonNull byte[] rawStorageId) {
CallLinkRoomId callLinkRoomId = recipient.getCallLinkRoomId();
if (callLinkRoomId == null) {
throw new AssertionError("Must have a callLinkRoomId!");
}
CallLinkTable.CallLink callLink = SignalDatabase.callLinks().getCallLinkByRoomId(callLinkRoomId);
if (callLink == null) {
throw new AssertionError("Must have a call link record!");
}
if (callLink.getCredentials() == null) {
throw new AssertionError("Must have call link credentials!");
}
long deletedTimestamp = Math.max(0, SignalDatabase.callLinks().getDeletedTimestampByRoomId(callLinkRoomId));
byte[] adminPassword = deletedTimestamp > 0 ? new byte[]{} : Objects.requireNonNull(callLink.getCredentials().getAdminPassBytes(), "Non-deleted call link requires admin pass!");
return new SignalCallLinkRecord.Builder(rawStorageId, null)
.setRootKey(callLink.getCredentials().getLinkKeyBytes())
.setAdminPassKey(adminPassword)
.setDeletedTimestamp(deletedTimestamp)
.build();
}
private static @NonNull SignalStoryDistributionListRecord localToRemoteStoryDistributionList(@NonNull RecipientRecord recipient, @NonNull byte[] rawStorageId) {
DistributionListId distributionListId = recipient.getDistributionListId();
if (distributionListId == null) {
throw new AssertionError("Must have a distributionListId!");
}
DistributionListRecord record = SignalDatabase.distributionLists().getListForStorageSync(distributionListId);
if (record == null) {
throw new AssertionError("Must have a distribution list record!");
}
if (record.getDeletedAtTimestamp() > 0L) {
return new SignalStoryDistributionListRecord.Builder(rawStorageId, recipient.getSyncExtras().getStorageProto())
.setIdentifier(UuidUtil.toByteArray(record.getDistributionId().asUuid()))
.setDeletedAtTimestamp(record.getDeletedAtTimestamp())
.build();
}
return new SignalStoryDistributionListRecord.Builder(rawStorageId, recipient.getSyncExtras().getStorageProto())
.setIdentifier(UuidUtil.toByteArray(record.getDistributionId().asUuid()))
.setName(record.getName())
.setRecipients(record.getMembersToSync()
.stream()
.map(Recipient::resolved)
.filter(Recipient::getHasServiceId)
.map(Recipient::requireServiceId)
.map(SignalServiceAddress::new)
.collect(Collectors.toList()))
.setAllowsReplies(record.getAllowsReplies())
.setIsBlockList(record.getPrivacyMode().isBlockList())
.build();
}
public static @NonNull IdentityTable.VerifiedStatus remoteToLocalIdentityStatus(@NonNull IdentityState identityState) {
switch (identityState) {
case VERIFIED: return IdentityTable.VerifiedStatus.VERIFIED;
case UNVERIFIED: return IdentityTable.VerifiedStatus.UNVERIFIED;
default: return IdentityTable.VerifiedStatus.DEFAULT;
}
}
private static IdentityState localToRemoteIdentityState(@NonNull IdentityTable.VerifiedStatus local) {
switch (local) {
case VERIFIED: return IdentityState.VERIFIED;
case UNVERIFIED: return IdentityState.UNVERIFIED;
default: return IdentityState.DEFAULT;
}
}
/**
* TODO - need to store the subscriber type
*/
public static @NonNull SignalAccountRecord.Subscriber localToRemoteSubscriber(@Nullable InAppPaymentSubscriberRecord subscriber) {
if (subscriber == null) {
return new SignalAccountRecord.Subscriber(null, null);
} else {
return new SignalAccountRecord.Subscriber(subscriber.getCurrency().getCurrencyCode(), subscriber.getSubscriberId().getBytes());
}
}
public static @Nullable InAppPaymentSubscriberRecord remoteToLocalSubscriber(
@NonNull SignalAccountRecord.Subscriber subscriber,
@NonNull InAppPaymentSubscriberRecord.Type type
) {
if (subscriber.getId().isPresent()) {
SubscriberId subscriberId = SubscriberId.fromBytes(subscriber.getId().get());
InAppPaymentSubscriberRecord localSubscriberRecord = SignalDatabase.inAppPaymentSubscribers().getBySubscriberId(subscriberId);
boolean requiresCancel = localSubscriberRecord != null && localSubscriberRecord.getRequiresCancel();
InAppPaymentData.PaymentMethodType paymentMethodType = localSubscriberRecord != null ? localSubscriberRecord.getPaymentMethodType() : InAppPaymentData.PaymentMethodType.UNKNOWN;
Currency currency;
if (subscriber.getCurrencyCode().isEmpty()) {
return null;
} else {
try {
currency = Currency.getInstance(subscriber.getCurrencyCode().get());
} catch (IllegalArgumentException e) {
return null;
}
}
return new InAppPaymentSubscriberRecord(subscriberId, currency, type, requiresCancel, paymentMethodType);
} else {
return null;
}
}
}

View file

@ -0,0 +1,311 @@
package org.thoughtcrime.securesms.storage
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme
import org.thoughtcrime.securesms.database.GroupTable.ShowAsStoryState
import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.RecipientTable.RecipientType
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.callLinks
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.distributionLists
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groups
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.inAppPaymentSubscribers
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.database.model.RecipientRecord
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
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record
import org.whispersystems.signalservice.api.storage.SignalStorageRecord
import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.internal.storage.protos.AccountRecord
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState
import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record
import java.util.Currency
import kotlin.math.max
object StorageSyncModels {
@JvmStatic
fun localToRemoteRecord(settings: RecipientRecord): SignalStorageRecord {
if (settings.storageId == null) {
throw AssertionError("Must have a storage key!")
}
return localToRemoteRecord(settings, settings.storageId)
}
@JvmStatic
fun localToRemoteRecord(settings: RecipientRecord, groupMasterKey: GroupMasterKey): SignalStorageRecord {
if (settings.storageId == null) {
throw AssertionError("Must have a storage key!")
}
return SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, settings.storageId, groupMasterKey))
}
@JvmStatic
fun localToRemoteRecord(settings: RecipientRecord, rawStorageId: ByteArray): SignalStorageRecord {
return when (settings.recipientType) {
RecipientType.INDIVIDUAL -> SignalStorageRecord.forContact(localToRemoteContact(settings, rawStorageId))
RecipientType.GV1 -> SignalStorageRecord.forGroupV1(localToRemoteGroupV1(settings, rawStorageId))
RecipientType.GV2 -> SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, rawStorageId, settings.syncExtras.groupMasterKey!!))
RecipientType.DISTRIBUTION_LIST -> SignalStorageRecord.forStoryDistributionList(localToRemoteStoryDistributionList(settings, rawStorageId))
RecipientType.CALL_LINK -> SignalStorageRecord.forCallLink(localToRemoteCallLink(settings, rawStorageId))
else -> throw AssertionError("Unsupported type!")
}
}
@JvmStatic
fun localToRemotePhoneNumberSharingMode(phoneNumberPhoneNumberSharingMode: PhoneNumberPrivacyValues.PhoneNumberSharingMode): AccountRecord.PhoneNumberSharingMode {
return when (phoneNumberPhoneNumberSharingMode) {
PhoneNumberPrivacyValues.PhoneNumberSharingMode.DEFAULT -> AccountRecord.PhoneNumberSharingMode.NOBODY
PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY -> AccountRecord.PhoneNumberSharingMode.EVERYBODY
PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY -> AccountRecord.PhoneNumberSharingMode.NOBODY
}
}
@JvmStatic
fun remoteToLocalPhoneNumberSharingMode(phoneNumberPhoneNumberSharingMode: AccountRecord.PhoneNumberSharingMode): PhoneNumberPrivacyValues.PhoneNumberSharingMode {
return when (phoneNumberPhoneNumberSharingMode) {
AccountRecord.PhoneNumberSharingMode.EVERYBODY -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY
AccountRecord.PhoneNumberSharingMode.NOBODY -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY
else -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.DEFAULT
}
}
@JvmStatic
fun localToRemotePinnedConversations(records: List<RecipientRecord>): List<SignalAccountRecord.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 {
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())
else -> throw AssertionError("Unexpected group type!")
}
}
@JvmStatic
fun localToRemoteUsernameColor(local: UsernameQrCodeColorScheme): AccountRecord.UsernameLink.Color {
return when (local) {
UsernameQrCodeColorScheme.Blue -> AccountRecord.UsernameLink.Color.BLUE
UsernameQrCodeColorScheme.White -> AccountRecord.UsernameLink.Color.WHITE
UsernameQrCodeColorScheme.Grey -> AccountRecord.UsernameLink.Color.GREY
UsernameQrCodeColorScheme.Tan -> AccountRecord.UsernameLink.Color.OLIVE
UsernameQrCodeColorScheme.Green -> AccountRecord.UsernameLink.Color.GREEN
UsernameQrCodeColorScheme.Orange -> AccountRecord.UsernameLink.Color.ORANGE
UsernameQrCodeColorScheme.Pink -> AccountRecord.UsernameLink.Color.PINK
UsernameQrCodeColorScheme.Purple -> AccountRecord.UsernameLink.Color.PURPLE
}
}
@JvmStatic
fun remoteToLocalUsernameColor(remote: AccountRecord.UsernameLink.Color): UsernameQrCodeColorScheme {
return when (remote) {
AccountRecord.UsernameLink.Color.BLUE -> UsernameQrCodeColorScheme.Blue
AccountRecord.UsernameLink.Color.WHITE -> UsernameQrCodeColorScheme.White
AccountRecord.UsernameLink.Color.GREY -> UsernameQrCodeColorScheme.Grey
AccountRecord.UsernameLink.Color.OLIVE -> UsernameQrCodeColorScheme.Tan
AccountRecord.UsernameLink.Color.GREEN -> UsernameQrCodeColorScheme.Green
AccountRecord.UsernameLink.Color.ORANGE -> UsernameQrCodeColorScheme.Orange
AccountRecord.UsernameLink.Color.PINK -> UsernameQrCodeColorScheme.Pink
AccountRecord.UsernameLink.Color.PURPLE -> UsernameQrCodeColorScheme.Purple
else -> UsernameQrCodeColorScheme.Blue
}
}
private fun localToRemoteContact(recipient: RecipientRecord, rawStorageId: ByteArray): SignalContactRecord {
if (recipient.aci == null && recipient.pni == null && recipient.e164 == null) {
throw AssertionError("Must have either a UUID or a phone number!")
}
val hideStory = recipient.extras != null && recipient.extras.hideStory()
return SignalContactRecord.Builder(rawStorageId, recipient.aci, recipient.syncExtras.storageProto)
.setE164(recipient.e164)
.setPni(recipient.pni)
.setProfileKey(recipient.profileKey)
.setProfileGivenName(recipient.signalProfileName.givenName)
.setProfileFamilyName(recipient.signalProfileName.familyName)
.setSystemGivenName(recipient.systemProfileName.givenName)
.setSystemFamilyName(recipient.systemProfileName.familyName)
.setSystemNickname(recipient.syncExtras.systemNickname)
.setBlocked(recipient.isBlocked)
.setProfileSharingEnabled(recipient.profileSharing || recipient.systemContactUri != null)
.setIdentityKey(recipient.syncExtras.identityKey)
.setIdentityState(localToRemoteIdentityState(recipient.syncExtras.identityStatus))
.setArchived(recipient.syncExtras.isArchived)
.setForcedUnread(recipient.syncExtras.isForcedUnread)
.setMuteUntil(recipient.muteUntil)
.setHideStory(hideStory)
.setUnregisteredTimestamp(recipient.syncExtras.unregisteredTimestamp)
.setHidden(recipient.hiddenState != Recipient.HiddenState.NOT_HIDDEN)
.setUsername(recipient.username)
.setPniSignatureVerified(recipient.syncExtras.pniSignatureVerified)
.setNicknameGivenName(recipient.nickname.givenName)
.setNicknameFamilyName(recipient.nickname.familyName)
.setNote(recipient.note)
.build()
}
private fun localToRemoteGroupV1(recipient: RecipientRecord, rawStorageId: ByteArray): SignalGroupV1Record {
val groupId = recipient.groupId ?: throw AssertionError("Must have a groupId!")
if (!groupId.isV1) {
throw AssertionError("Group is not V1")
}
return SignalGroupV1Record.Builder(rawStorageId, groupId.decodedId, recipient.syncExtras.storageProto)
.setBlocked(recipient.isBlocked)
.setProfileSharingEnabled(recipient.profileSharing)
.setArchived(recipient.syncExtras.isArchived)
.setForcedUnread(recipient.syncExtras.isForcedUnread)
.setMuteUntil(recipient.muteUntil)
.build()
}
private fun localToRemoteGroupV2(recipient: RecipientRecord, rawStorageId: ByteArray?, groupMasterKey: GroupMasterKey): SignalGroupV2Record {
val groupId = recipient.groupId ?: throw AssertionError("Must have a groupId!")
if (!groupId.isV2) {
throw AssertionError("Group is not V2")
}
if (groupMasterKey == null) {
throw AssertionError("Group master key not on recipient record")
}
val hideStory = recipient.extras != null && recipient.extras.hideStory()
val showAsStoryState = groups.getShowAsStoryState(groupId)
val storySendMode = when (showAsStoryState) {
ShowAsStoryState.ALWAYS -> GroupV2Record.StorySendMode.ENABLED
ShowAsStoryState.NEVER -> GroupV2Record.StorySendMode.DISABLED
else -> GroupV2Record.StorySendMode.DEFAULT
}
return SignalGroupV2Record.Builder(rawStorageId, groupMasterKey, recipient.syncExtras.storageProto)
.setBlocked(recipient.isBlocked)
.setProfileSharingEnabled(recipient.profileSharing)
.setArchived(recipient.syncExtras.isArchived)
.setForcedUnread(recipient.syncExtras.isForcedUnread)
.setMuteUntil(recipient.muteUntil)
.setNotifyForMentionsWhenMuted(recipient.mentionSetting == RecipientTable.MentionSetting.ALWAYS_NOTIFY)
.setHideStory(hideStory)
.setStorySendMode(storySendMode)
.build()
}
private fun localToRemoteCallLink(recipient: RecipientRecord, rawStorageId: ByteArray): SignalCallLinkRecord {
val callLinkRoomId = recipient.callLinkRoomId ?: throw AssertionError("Must have a callLinkRoomId!")
val callLink = callLinks.getCallLinkByRoomId(callLinkRoomId) ?: throw AssertionError("Must have a call link record!")
if (callLink.credentials == null) {
throw AssertionError("Must have call link credentials!")
}
val deletedTimestamp = max(0.0, callLinks.getDeletedTimestampByRoomId(callLinkRoomId).toDouble()).toLong()
val adminPassword = if (deletedTimestamp > 0) byteArrayOf() else callLink.credentials.adminPassBytes!!
return SignalCallLinkRecord.Builder(rawStorageId, null)
.setRootKey(callLink.credentials.linkKeyBytes)
.setAdminPassKey(adminPassword)
.setDeletedTimestamp(deletedTimestamp)
.build()
}
private fun localToRemoteStoryDistributionList(recipient: RecipientRecord, rawStorageId: ByteArray): SignalStoryDistributionListRecord {
val distributionListId = recipient.distributionListId ?: throw AssertionError("Must have a distributionListId!")
val record = distributionLists.getListForStorageSync(distributionListId) ?: throw AssertionError("Must have a distribution list record!")
if (record.deletedAtTimestamp > 0L) {
return SignalStoryDistributionListRecord.Builder(rawStorageId, recipient.syncExtras.storageProto)
.setIdentifier(UuidUtil.toByteArray(record.distributionId.asUuid()))
.setDeletedAtTimestamp(record.deletedAtTimestamp)
.build()
}
return SignalStoryDistributionListRecord.Builder(rawStorageId, recipient.syncExtras.storageProto)
.setIdentifier(UuidUtil.toByteArray(record.distributionId.asUuid()))
.setName(record.name)
.setRecipients(
record.getMembersToSync()
.map { Recipient.resolved(it) }
.filter { it.hasServiceId }
.map { it.requireServiceId() }
.map { SignalServiceAddress(it) }
)
.setAllowsReplies(record.allowsReplies)
.setIsBlockList(record.privacyMode.isBlockList)
.build()
}
fun remoteToLocalIdentityStatus(identityState: IdentityState): VerifiedStatus {
return when (identityState) {
IdentityState.VERIFIED -> VerifiedStatus.VERIFIED
IdentityState.UNVERIFIED -> VerifiedStatus.UNVERIFIED
else -> VerifiedStatus.DEFAULT
}
}
private fun localToRemoteIdentityState(local: VerifiedStatus): IdentityState {
return when (local) {
VerifiedStatus.VERIFIED -> IdentityState.VERIFIED
VerifiedStatus.UNVERIFIED -> IdentityState.UNVERIFIED
else -> IdentityState.DEFAULT
}
}
/**
* 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,
type: InAppPaymentSubscriberRecord.Type
): InAppPaymentSubscriberRecord? {
if (subscriber.id.isPresent) {
val subscriberId = SubscriberId.fromBytes(subscriber.id.get())
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) {
return null
} else {
try {
currency = Currency.getInstance(subscriber.currencyCode.get())
} catch (e: IllegalArgumentException) {
return null
}
}
return InAppPaymentSubscriberRecord(subscriberId, currency, type, requiresCancel, paymentMethodType)
} else {
return null
}
}
}