diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt index 3cda2e78b..23b432bd2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt @@ -7,6 +7,7 @@ import org.signal.ringrtc.CallManager import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob import org.thoughtcrime.securesms.keyvalue.InternalValues import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.util.livedata.Store @@ -130,6 +131,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito fun onClearOnboardingState() { SignalStore.storyValues().hasDownloadedOnboardingStory = false SignalStore.storyValues().userHasSeenOnboardingStory = false + Stories.onStorySettingsChanged(Recipient.self().id) refresh() StoryOnboardingDownloadJob.enqueueIfNeeded() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java index 6d2f6e7a8..d51d7848c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -68,6 +68,7 @@ public final class SettingsValues extends SignalStoreValues { private static final String UNIVERSAL_EXPIRE_TIMER = "settings.universal.expire.timer"; private static final String SENT_MEDIA_QUALITY = "settings.sentMediaQuality"; private static final String CENSORSHIP_CIRCUMVENTION_ENABLED = "settings.censorshipCircumventionEnabled"; + private static final String KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived"; private final SingleLiveEvent onConfigurationSettingChanged = new SingleLiveEvent<>(); @@ -111,7 +112,8 @@ public final class SettingsValues extends SignalStoreValues { CALL_VIBRATE_ENABLED, NOTIFY_WHEN_CONTACT_JOINS_SIGNAL, UNIVERSAL_EXPIRE_TIMER, - SENT_MEDIA_QUALITY); + SENT_MEDIA_QUALITY, + KEEP_MUTED_CHATS_ARCHIVED); } public @NonNull LiveData getOnConfigurationSettingChanged() { @@ -430,6 +432,14 @@ public final class SettingsValues extends SignalStoreValues { putInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, enabled ? CensorshipCircumventionEnabled.ENABLED.serialize() : CensorshipCircumventionEnabled.DISABLED.serialize()); } + public void setKeepMutedChatsArchived(boolean enabled) { + putBoolean(KEEP_MUTED_CHATS_ARCHIVED, enabled); + } + + public boolean shouldKeepMutedChatsArchived() { + return getBoolean(KEEP_MUTED_CHATS_ARCHIVED, false); + } + private @Nullable Uri getUri(@NonNull String key) { String uri = getString(key, ""); 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 3d3311af1..01dcec58b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java @@ -111,8 +111,12 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor defaultReactions = remote.getDefaultReactions().size() > 0 ? remote.getDefaultReactions() : local.getDefaultReactions(); boolean displayBadgesOnProfile = remote.isDisplayBadgesOnProfile(); boolean subscriptionManuallyCancelled = remote.isSubscriptionManuallyCancelled(); - 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); - 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); + boolean keepMutedChatsArchived = remote.isKeepMutedChatsArchived(); + boolean hasSetMyStoriesPrivacy = remote.hasSetMyStoriesPrivacy(); + boolean hasViewedOnboardingStory = remote.hasViewedOnboardingStory(); + boolean storiesDisabled = remote.isStoriesDisabled(); + 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, storiesDisabled); + 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, storiesDisabled); if (matchesRemote) { return remote; @@ -143,6 +147,10 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor defaultReactions, @NonNull SignalAccountRecord.Subscriber subscriber, boolean displayBadgesOnProfile, - boolean subscriptionManuallyCancelled) + boolean subscriptionManuallyCancelled, + boolean keepMutedChatsArchived, + boolean hasSetMyStoriesPrivacy, + boolean hasViewedOnboardingStory, + boolean storiesDisabled) { return Arrays.equals(contact.serializeUnknownFields(), unknownFields) && Objects.equals(contact.getGivenName().orElse(""), givenName) && @@ -209,6 +221,10 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor { return Single.fromCallable { SignalStore.storyValues().userHasBeenNotifiedAboutStories = true + Stories.onStorySettingsChanged(Recipient.self().id) store.state.recipientId }.observeOn(AndroidSchedulers.mainThread()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsRepository.kt index 05111acb5..9acd109c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsRepository.kt @@ -1,9 +1,12 @@ package org.thoughtcrime.securesms.stories.settings.story import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.schedulers.Schedulers import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.stories.Stories class StoriesPrivacySettingsRepository { fun markGroupsAsStories(groups: List): Completable { @@ -13,4 +16,11 @@ class StoriesPrivacySettingsRepository { .forEach { SignalDatabase.groups.markDisplayAsStory(it.requireGroupId()) } } } + + fun setStoriesEnabled(isEnabled: Boolean): Completable { + return Completable.fromAction { + SignalStore.storyValues().isFeatureDisabled = !isEnabled + Stories.onStorySettingsChanged(Recipient.self().id) + }.subscribeOn(Schedulers.io()) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsState.kt index 35ace1d3d..bc1e0b3b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsState.kt @@ -4,5 +4,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchData data class StoriesPrivacySettingsState( val areStoriesEnabled: Boolean, + val isUpdatingEnabledState: Boolean = false, val storyContactItems: List = emptyList() ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsViewModel.kt index bdb077e10..a17d08ed0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/story/StoriesPrivacySettingsViewModel.kt @@ -14,7 +14,6 @@ import org.signal.paging.ProxyPagingController import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchPagedDataSource -import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.util.rx.RxStore @@ -70,8 +69,15 @@ class StoriesPrivacySettingsViewModel : ViewModel() { } fun setStoriesEnabled(isEnabled: Boolean) { - SignalStore.storyValues().isFeatureDisabled = !isEnabled - store.update { it.copy(areStoriesEnabled = Stories.isFeatureEnabled()) } + store.update { it.copy(isUpdatingEnabledState = true) } + disposables += repository.setStoriesEnabled(isEnabled).subscribe { + store.update { + it.copy( + isUpdatingEnabledState = false, + areStoriesEnabled = Stories.isFeatureEnabled() + ) + } + } } fun displayGroupsAsStories(recipientIds: List) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt index 26968b340..ae667ae61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt @@ -175,6 +175,7 @@ open class StoryViewerPageRepository(context: Context) { if (storyPost.sender.isReleaseNotes) { SignalStore.storyValues().userHasSeenOnboardingStory = true + Stories.onStorySettingsChanged(Recipient.self().id) } else { ApplicationDependencies.getJobManager().add( SendViewedReceiptJob( 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 82c91b8db..3e4062a7e 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 @@ -166,6 +166,22 @@ public final class SignalAccountRecord implements SignalRecord { diff.add("SubscriptionManuallyCancelled"); } + if (isKeepMutedChatsArchived() != that.isKeepMutedChatsArchived()) { + diff.add("KeepMutedChatsArchived"); + } + + if (hasSetMyStoriesPrivacy() != that.hasSetMyStoriesPrivacy()) { + diff.add("HasSetMyStoryPrivacy"); + } + + if (hasViewedOnboardingStory() != that.hasViewedOnboardingStory()) { + diff.add("HasViewedOnboardingStory"); + } + + if (isStoriesDisabled() != that.isStoriesDisabled()) { + diff.add("StoriesDisabled"); + } + return diff.toString(); } else { return "Different class. " + getClass().getSimpleName() + " | " + other.getClass().getSimpleName(); @@ -268,6 +284,22 @@ public final class SignalAccountRecord implements SignalRecord { return proto.getSubscriptionManuallyCancelled(); } + public boolean isKeepMutedChatsArchived() { + return proto.getKeepMutedChatsArchived(); + } + + public boolean hasSetMyStoriesPrivacy() { + return proto.getHasSetMyStoriesPrivacy(); + } + + public boolean hasViewedOnboardingStory() { + return proto.getHasViewedOnboardingStory(); + } + + public boolean isStoriesDisabled() { + return proto.getStoriesDisabled(); + } + public AccountRecord toProto() { return proto; } @@ -605,6 +637,26 @@ public final class SignalAccountRecord implements SignalRecord { return this; } + public Builder setKeepMutedChatsArchived(boolean keepMutedChatsArchived) { + builder.setKeepMutedChatsArchived(keepMutedChatsArchived); + return this; + } + + public Builder setHasSetMyStoriesPrivacy(boolean hasSetMyStoriesPrivacy) { + builder.setHasSetMyStoriesPrivacy(hasSetMyStoriesPrivacy); + return this; + } + + public Builder setHasViewedOnboardingStory(boolean hasViewedOnboardingStory) { + builder.setHasViewedOnboardingStory(hasViewedOnboardingStory); + return this; + } + + public Builder setStoriesDisabled(boolean storiesDisabled) { + builder.setStoriesDisabled(storiesDisabled); + return this; + } + private static AccountRecord.Builder parseUnknowns(byte[] serializedUnknowns) { try { return AccountRecord.parseFrom(serializedUnknowns).toBuilder(); diff --git a/libsignal/service/src/main/proto/StorageService.proto b/libsignal/service/src/main/proto/StorageService.proto index 07fc00a4b..a2b74e17b 100644 --- a/libsignal/service/src/main/proto/StorageService.proto +++ b/libsignal/service/src/main/proto/StorageService.proto @@ -158,6 +158,10 @@ message AccountRecord { string subscriberCurrencyCode = 22; bool displayBadgesOnProfile = 23; bool subscriptionManuallyCancelled = 24; + bool keepMutedChatsArchived = 25; + bool hasSetMyStoriesPrivacy = 26; + bool hasViewedOnboardingStory = 27; + bool storiesDisabled = 28; } message StoryDistributionListRecord {