From 6673da0b04a51cf3ba56eae437b99b10f3c50c1e Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 29 Oct 2021 10:33:35 -0300 Subject: [PATCH] Add subscriber information to storage service account record. --- .../subscription/DonationPaymentRepository.kt | 3 + .../storage/AccountRecordProcessor.java | 58 ++++++++++-------- .../securesms/storage/StorageSyncHelper.java | 7 +++ .../securesms/storage/StorageSyncModels.java | 20 ++++++- .../api/storage/SignalAccountRecord.java | 59 ++++++++++++++++++- .../src/main/proto/StorageService.proto | 2 + 6 files changed, 123 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt index 2e69789544..feaa5338a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.storage.StorageSyncHelper import org.thoughtcrime.securesms.subscription.LevelUpdateOperation import org.thoughtcrime.securesms.subscription.Subscriber import org.thoughtcrime.securesms.util.Environment @@ -99,6 +100,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet SignalStore .donationsValues() .setSubscriber(Subscriber(subscriberId, SignalStore.donationsValues().getSubscriptionCurrency().currencyCode)) + + StorageSyncHelper.scheduleSyncForDataChange() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java index 82c55d6acb..e5bbddcd1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java @@ -7,6 +7,7 @@ import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.subscription.Subscriber; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; import org.whispersystems.signalservice.api.storage.SignalAccountRecord.PinnedConversation; @@ -82,6 +83,14 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor defaultReactions = remote.getDefaultReactions().size() > 0 ? remote.getDefaultReactions() : local.getDefaultReactions(); - boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted, pinnedConversations, preferContactAvatars, payments, universalExpireTimer, primarySendsSms, e164, defaultReactions); - boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted, pinnedConversations, preferContactAvatars, payments, universalExpireTimer, primarySendsSms, e164, defaultReactions); + 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); + 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); if (matchesRemote) { return remote; @@ -129,6 +138,7 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor defaultReactions) + @NonNull List defaultReactions, + @NonNull SignalAccountRecord.Subscriber subscriber) { - return Arrays.equals(contact.serializeUnknownFields(), unknownFields) && - Objects.equals(contact.getGivenName().or(""), givenName) && - Objects.equals(contact.getFamilyName().or(""), familyName) && - Objects.equals(contact.getAvatarUrlPath().or(""), avatarUrlPath) && - Objects.equals(contact.getPayments(), payments) && - Objects.equals(contact.getE164(), e164) && - Objects.equals(contact.getDefaultReactions(), defaultReactions) && - Arrays.equals(contact.getProfileKey().orNull(), 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); + return Arrays.equals(contact.serializeUnknownFields(), unknownFields) && + Objects.equals(contact.getGivenName().or(""), givenName) && + Objects.equals(contact.getFamilyName().or(""), familyName) && + Objects.equals(contact.getAvatarUrlPath().or(""), avatarUrlPath) && + Objects.equals(contact.getPayments(), payments) && + Objects.equals(contact.getE164(), e164) && + Objects.equals(contact.getDefaultReactions(), defaultReactions) && + Arrays.equals(contact.getProfileKey().orNull(), 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); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java index 0d37f41884..72ed5bdf49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.payments.Entropy; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.subscription.Subscriber; import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -131,6 +132,7 @@ public final class StorageSyncHelper { .setUniversalExpireTimer(SignalStore.settings().getUniversalExpireTimer()) .setE164(TextSecurePreferences.getLocalNumber(context)) .setDefaultReactions(SignalStore.emojiValues().getReactions()) + .setSubscriber(StorageSyncModels.localToRemoteSubscriber(SignalStore.donationsValues().getSubscriber())) .build(); return SignalStorageRecord.forAccount(account); @@ -155,6 +157,11 @@ public final class StorageSyncHelper { SignalStore.settings().setUniversalExpireTimer(update.getNew().getUniversalExpireTimer()); SignalStore.emojiValues().setReactions(update.getNew().getDefaultReactions()); + Subscriber subscriber = StorageSyncModels.remoteToLocalSubscriber(update.getNew().getSubscriber()); + if (subscriber != null) { + SignalStore.donationsValues().setSubscriber(subscriber); + } + if (fetchProfile && update.getNew().getAvatarUrlPath().isPresent()) { ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(self, update.getNew().getAvatarUrlPath().get())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.java b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.java index c10ebf7b12..88cf4c51e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.storage; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.annimon.stream.Stream; @@ -10,6 +11,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues; +import org.thoughtcrime.securesms.subscription.Subscriber; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; @@ -17,12 +19,11 @@ 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.util.UuidUtil; +import org.whispersystems.signalservice.api.subscriptions.SubscriberId; import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState; import java.util.List; -import java.util.UUID; public final class StorageSyncModels { @@ -173,4 +174,19 @@ public final class StorageSyncModels { } } + public static @NonNull SignalAccountRecord.Subscriber localToRemoteSubscriber(@Nullable Subscriber subscriber) { + if (subscriber == null) { + return new SignalAccountRecord.Subscriber(null, null); + } else { + return new SignalAccountRecord.Subscriber(subscriber.getCurrencyCode(), subscriber.getSubscriberId().getBytes()); + } + } + + public static @Nullable Subscriber remoteToLocalSubscriber(@NonNull SignalAccountRecord.Subscriber subscriber) { + if (subscriber.getId().isPresent()) { + return new Subscriber(SubscriberId.fromBytes(subscriber.getId().get()), subscriber.getCurrencyCode().get()); + } else { + return null; + } + } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java index 9142c8319d..7f59232a26 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java @@ -10,7 +10,6 @@ import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.OptionalUtil; import org.whispersystems.signalservice.api.util.ProtoUtil; -import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import java.util.ArrayList; @@ -34,6 +33,7 @@ public final class SignalAccountRecord implements SignalRecord { private final List pinnedConversations; private final Payments payments; private final List defaultReactions; + private final Subscriber subscriber; public SignalAccountRecord(StorageId id, AccountRecord proto) { this.id = id; @@ -47,6 +47,7 @@ public final class SignalAccountRecord implements SignalRecord { this.pinnedConversations = new ArrayList<>(proto.getPinnedConversationsCount()); this.payments = new Payments(proto.getPayments().getEnabled(), OptionalUtil.absentIfEmpty(proto.getPayments().getEntropy())); this.defaultReactions = new ArrayList<>(proto.getPreferredReactionEmojiList()); + this.subscriber = new Subscriber(proto.getSubscriberCurrencyCode(), proto.getSubscriberId().toByteArray()); for (AccountRecord.PinnedConversation conversation : proto.getPinnedConversationsList()) { pinnedConversations.add(PinnedConversation.fromRemote(conversation)); @@ -153,6 +154,10 @@ public final class SignalAccountRecord implements SignalRecord { diff.add("UnknownFields"); } + if (!Objects.equals(this.getSubscriber(), that.getSubscriber())) { + diff.add("Subscriber"); + } + return diff.toString(); } else { return "Different class. " + getClass().getSimpleName() + " | " + other.getClass().getSimpleName(); @@ -243,6 +248,10 @@ public final class SignalAccountRecord implements SignalRecord { return defaultReactions; } + public Subscriber getSubscriber() { + return subscriber; + } + AccountRecord toProto() { return proto; } @@ -351,6 +360,42 @@ public final class SignalAccountRecord implements SignalRecord { } } + public static class Subscriber { + private final Optional currencyCode; + private final Optional id; + + public Subscriber(String currencyCode, byte[] id) { + if (currencyCode != null && id != null && id.length == 32) { + this.currencyCode = Optional.of(currencyCode); + this.id = Optional.of(id); + } else { + this.currencyCode = Optional.absent(); + this.id = Optional.absent(); + } + } + + public Optional getCurrencyCode() { + return currencyCode; + } + + public Optional getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Subscriber that = (Subscriber) o; + return Objects.equals(currencyCode, that.currencyCode) && OptionalUtil.byteArrayEquals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(currencyCode, id); + } + } + public static class Payments { private static final String TAG = Payments.class.getSimpleName(); @@ -518,6 +563,18 @@ public final class SignalAccountRecord implements SignalRecord { return this; } + public Builder setSubscriber(Subscriber subscriber) { + if (subscriber.id.isPresent() && subscriber.currencyCode.isPresent()) { + builder.setSubscriberId(ByteString.copyFrom(subscriber.id.get())); + builder.setSubscriberCurrencyCode(subscriber.currencyCode.get()); + } else { + builder.clearSubscriberId(); + builder.clearSubscriberCurrencyCode(); + } + + return this; + } + public SignalAccountRecord build() { AccountRecord proto = builder.build(); diff --git a/libsignal/service/src/main/proto/StorageService.proto b/libsignal/service/src/main/proto/StorageService.proto index abfbfad0a9..63827a1f22 100644 --- a/libsignal/service/src/main/proto/StorageService.proto +++ b/libsignal/service/src/main/proto/StorageService.proto @@ -148,4 +148,6 @@ message AccountRecord { bool primarySendsSms = 18; string e164 = 19; repeated string preferredReactionEmoji = 20; + bytes subscriberId = 21; + string subscriberCurrencyCode = 22; }