From 4b7107ad675eb12610d853f48ffd412cc7520093 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Fri, 27 Feb 2026 08:43:30 +1100 Subject: [PATCH 1/4] Replace TextSecurePreference usage --- .../sending_receiving/MessageParser.kt | 9 +-- .../ReceivedMessageProcessor.kt | 9 +-- .../securesms/home/HomeActivity.kt | 10 ++-- .../securesms/home/HomePreferenceKeys.kt | 13 +++++ .../securesms/home/HomeViewModel.kt | 24 ++++---- .../notifications/PushRegistrationHandler.kt | 11 +++- .../onboarding/OnboardingPreferenceKeys.kt | 10 ++++ .../onboarding/landing/LandingActivity.kt | 7 ++- .../loadaccount/LoadAccountActivity.kt | 7 ++- .../onboarding/loading/LoadingActivity.kt | 4 -- .../onboarding/loading/LoadingViewModel.kt | 2 - .../onboarding/manager/LoadAccountManager.kt | 1 - .../MessageNotificationsActivity.kt | 5 -- .../MessageNotificationsViewModel.kt | 9 +-- .../pickname/PickDisplayNameActivity.kt | 5 -- .../pickname/PickDisplayNameViewModel.kt | 3 - .../CommunicationPreferenceKeys.kt | 11 ++++ .../compose/ChatsPreferenceViewModel.kt | 25 ++++---- .../NotificationsPreferenceViewModel.kt | 44 +++++++------- .../PrivacySettingsPreferenceViewModel.kt | 57 ++++++++----------- .../compose/SettingsPreferenceKeys.kt | 29 ++++++++++ .../prosettings/ProSettingsPreferenceKeys.kt | 10 ++++ .../prosettings/ProSettingsViewModel.kt | 10 ++-- .../securesms/reviews/InAppReviewManager.kt | 34 +++-------- .../reviews/InAppReviewPreferenceKeys.kt | 11 ++++ .../reviews/ui/InAppReviewViewModel.kt | 9 +-- .../sskenvironment/ReadReceiptManager.kt | 9 +-- .../securesms/util/DateTimePreferenceKeys.kt | 8 +++ .../thoughtcrime/securesms/util/DateUtils.kt | 20 +++---- .../securesms/webrtc/CallMessageProcessor.kt | 9 +-- 30 files changed, 235 insertions(+), 180 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/home/HomePreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingPreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/preferences/CommunicationPreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/preferences/compose/SettingsPreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsPreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewPreferenceKeys.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/DateTimePreferenceKeys.kt diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt index ee64c19de6..f15ee64e0b 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt @@ -21,7 +21,6 @@ import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.network.SnodeClock import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.withGroupConfigs import org.session.libsession.utilities.withUserConfigs import org.session.libsignal.exceptions.NonRetryableException @@ -30,6 +29,8 @@ import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.IdPrefix import org.session.protos.SessionProtos +import org.thoughtcrime.securesms.preferences.CommunicationPreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.pro.ProBackendConfig import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -42,7 +43,7 @@ class MessageParser @Inject constructor( private val configFactory: ConfigFactoryProtocol, private val storage: StorageProtocol, private val snodeClock: SnodeClock, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val proBackendConfig: Provider, ) { @@ -143,7 +144,7 @@ class MessageParser @Inject constructor( message.isSenderSelf = isSenderSelf // Only process pro features post pro launch - if (prefs.forcePostPro()) { + if (prefs[CommunicationPreferenceKeys.FORCE_POST_PRO]) { if (pro?.status == ProProof.STATUS_VALID) { (message as? VisibleMessage)?.proFeatures = buildSet { addAll(pro.proMessageFeatures.asSequence()) @@ -304,4 +305,4 @@ class MessageParser @Inject constructor( currentUserBlindedIDs = currentUserBlindedIDs, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageProcessor.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageProcessor.kt index 069a2ac2e0..56506faea3 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageProcessor.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageProcessor.kt @@ -33,7 +33,6 @@ import org.session.libsession.utilities.Address.Companion.toAddress import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.GroupUtil.doubleEncodeGroupID import org.session.libsession.utilities.MessageExpirationManagerProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TypingIndicatorsProtocol import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.recipients.MessageType @@ -54,6 +53,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.ReactionRecord import org.thoughtcrime.securesms.dependencies.ManagerScope +import org.thoughtcrime.securesms.preferences.CommunicationPreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantLock @@ -70,7 +71,7 @@ class ReceivedMessageProcessor @Inject constructor( private val threadDatabase: ThreadDatabase, private val readReceiptManager: Provider, private val typingIndicators: Provider, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val groupMessageHandler: Provider, private val messageExpirationManager: Provider, private val messageDataProvider: MessageDataProvider, @@ -378,7 +379,7 @@ class ReceivedMessageProcessor @Inject constructor( private fun showTypingIndicatorIfNeeded(senderPublicKey: String) { // We don't want to show other people's indicators if the toggle is off - if (!prefs.isTypingIndicatorsEnabled()) return + if (!prefs[CommunicationPreferenceKeys.TYPING_INDICATORS]) return val address = Address.fromSerialized(senderPublicKey) val threadID = storage.getThreadId(address) ?: return @@ -632,4 +633,4 @@ class ReceivedMessageProcessor @Inject constructor( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index ae3be8f4d3..7a623ac53a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -57,7 +57,6 @@ import org.session.libsession.network.onion.PathManager import org.session.libsession.utilities.Address import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.RecipientData import org.session.libsession.utilities.recipients.displayName import org.session.libsession.utilities.updateContact @@ -135,7 +134,6 @@ class HomeActivity : ScreenLockActionBarActivity(), @Inject lateinit var mmsSmsDatabase: MmsSmsDatabase @Inject lateinit var storage: Storage @Inject lateinit var groupDatabase: GroupDatabase - @Inject lateinit var textSecurePreferences: TextSecurePreferences @Inject lateinit var configFactory: ConfigFactory @Inject lateinit var tokenPageNotificationManager: TokenPageNotificationManager @Inject lateinit var groupManagerV2: GroupManagerV2 @@ -331,7 +329,7 @@ class HomeActivity : ScreenLockActionBarActivity(), binding.globalSearchRecycler.adapter = globalSearchAdapter binding.configOutdatedView.setOnClickListener { - textSecurePreferences.setHasLegacyConfig(false) + prefs[HomePreferenceKeys.HAS_RECEIVED_LEGACY_CONFIG] = false updateLegacyConfigView() } @@ -397,7 +395,7 @@ class HomeActivity : ScreenLockActionBarActivity(), // subscribe to outdated config updates, this should be removed after long enough time for device migration lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - TextSecurePreferences.events.filter { it == TextSecurePreferences.HAS_RECEIVED_LEGACY_CONFIG }.collect { + prefs.watch(lifecycleScope, HomePreferenceKeys.HAS_RECEIVED_LEGACY_CONFIG).collect { updateLegacyConfigView() } } @@ -629,7 +627,7 @@ class HomeActivity : ScreenLockActionBarActivity(), } private fun updateLegacyConfigView() { - binding.configOutdatedView.isVisible = textSecurePreferences.getHasLegacyConfig() + binding.configOutdatedView.isVisible = prefs[HomePreferenceKeys.HAS_RECEIVED_LEGACY_CONFIG] } override fun onResume() { @@ -995,7 +993,7 @@ class HomeActivity : ScreenLockActionBarActivity(), showSessionDialog { text(getString(R.string.hide)) button(R.string.yes) { - textSecurePreferences.setHasHiddenMessageRequests(true) + prefs[HomePreferenceKeys.HAS_HIDDEN_MESSAGE_REQUESTS] = true homeViewModel.tryReload() } button(R.string.no) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomePreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomePreferenceKeys.kt new file mode 100644 index 0000000000..9ebd53b624 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomePreferenceKeys.kt @@ -0,0 +1,13 @@ +package org.thoughtcrime.securesms.home + +import network.loki.messenger.BuildConfig +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object HomePreferenceKeys { + val HAS_HIDDEN_MESSAGE_REQUESTS = PreferenceKey.boolean("pref_message_requests_hidden", false) + val HAS_CHECKED_DOZE_WHITELIST = PreferenceKey.boolean("has_checked_doze_whitelist", false) + val PUSH_ENABLED = PreferenceKey.boolean("pref_is_using_fcm${BuildConfig.PUSH_KEY_SUFFIX}", false) + val HAS_SEEN_PRO_EXPIRING = PreferenceKey.boolean("has_seen_pro_expiring", false) + val HAS_SEEN_PRO_EXPIRED = PreferenceKey.boolean("has_seen_pro_expired", false) + val HAS_RECEIVED_LEGACY_CONFIG = PreferenceKey.boolean("has_received_legacy_config", false) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt index b4a1281707..697055f591 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt @@ -34,7 +34,6 @@ import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.displayName import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.Log @@ -69,8 +68,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( @param:ApplicationContext private val context: Context, - private val prefs: TextSecurePreferences, - private val prefStorage: PreferenceStorage, + private val prefs: PreferenceStorage, private val loginStateRepository: LoginStateRepository, private val typingStatusRepository: TypingStatusRepository, private val configFactory: ConfigFactory, @@ -143,14 +141,12 @@ class HomeViewModel @Inject constructor( observeTypingStatus(), // Third flow: whether the user has marked message requests as hidden - (TextSecurePreferences.events.filter { it == TextSecurePreferences.HAS_HIDDEN_MESSAGE_REQUESTS } as Flow<*>) - .onStart { emit(Unit) } - .map { prefs.hasHiddenMessageRequests() } + prefs.watch(viewModelScope, HomePreferenceKeys.HAS_HIDDEN_MESSAGE_REQUESTS) ) { (unapproveConvoCount, convoList), typingStatus, hiddenMessageRequest -> // check if we should show the recovery phrase backup banner: // - if the user has not yet seen the warning // - if the user has at least 3 conversations - if (!prefStorage[HAS_VIEWED_SEED] && convoList.size >= 3){ + if (!prefs[HAS_VIEWED_SEED] && convoList.size >= 3){ _uiState.update { it.copy(showRecoveryPhraseBackupBanner = true) } @@ -183,11 +179,11 @@ class HomeViewModel @Inject constructor( init { // check for white list status in case of slow mode - if(!prefs.hasCheckedDozeWhitelist() // the user has not yet seen the dialog - && !prefs.pushEnabled.value // the user is in slow mode + if(!prefs[HomePreferenceKeys.HAS_CHECKED_DOZE_WHITELIST] // the user has not yet seen the dialog + && !prefs[HomePreferenceKeys.PUSH_ENABLED] // the user is in slow mode && !context.isWhitelistedFromDoze() // the user isn't yet whitelisted ){ - prefs.setHasCheckedDozeWhitelist(true) + prefs[HomePreferenceKeys.HAS_CHECKED_DOZE_WHITELIST] = true viewModelScope.launch { delay(1500) _dialogsState.update { @@ -229,7 +225,7 @@ class HomeViewModel @Inject constructor( var showExpired: Boolean = false if(subscription.type is ProStatus.Active.Expiring - && !prefs.hasSeenProExpiring() + && !prefs[HomePreferenceKeys.HAS_SEEN_PRO_EXPIRING] ){ val validUntil = subscription.type.renewingAt showExpiring = validUntil.isBefore(now.plus(7, ChronoUnit.DAYS)) @@ -245,7 +241,7 @@ class HomeViewModel @Inject constructor( } } else if(subscription.type is ProStatus.Expired - && !prefs.hasSeenProExpired()) { + && !prefs[HomePreferenceKeys.HAS_SEEN_PRO_EXPIRED]) { val validUntil = subscription.type.expiredAt showExpired = now.isBefore(validUntil.plus(30, ChronoUnit.DAYS)) @@ -383,12 +379,12 @@ class HomeViewModel @Inject constructor( } is Commands.HideExpiringCTADialog -> { - prefs.setHasSeenProExpiring() + prefs[HomePreferenceKeys.HAS_SEEN_PRO_EXPIRING] = true _dialogsState.update { it.copy(proExpiringCTA = null) } } is Commands.HideExpiredCTADialog -> { - prefs.setHasSeenProExpired() + prefs[HomePreferenceKeys.HAS_SEEN_PRO_EXPIRED] = true _dialogsState.update { it.copy(proExpiredCTA = false) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistrationHandler.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistrationHandler.kt index 7fb8fb2d9d..a0bf0ebdbc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistrationHandler.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistrationHandler.kt @@ -8,11 +8,11 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import org.session.libsession.messaging.notifications.TokenFetcher -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.userConfigsChanged import org.session.libsession.utilities.withUserConfigs @@ -21,6 +21,8 @@ import org.thoughtcrime.securesms.auth.AuthAwareComponent import org.thoughtcrime.securesms.auth.LoggedInState import org.thoughtcrime.securesms.database.PushRegistrationDatabase import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.preferences.CommunicationPreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.util.castAwayType import java.util.EnumSet import java.util.concurrent.atomic.AtomicBoolean @@ -34,7 +36,7 @@ import javax.inject.Singleton @Singleton class PushRegistrationHandler @Inject constructor( private val configFactory: ConfigFactory, - private val preferences: TextSecurePreferences, + private val preferences: PreferenceStorage, private val tokenFetcher: TokenFetcher, @param:ApplicationContext private val context: Context, @param:PushNotificationModule.PushProcessingSemaphore @@ -52,7 +54,10 @@ class PushRegistrationHandler @Inject constructor( ) .castAwayType() .onStart { emit(Unit) }, - preferences.pushEnabled, + preferences.changes() + .filter { it.name == CommunicationPreferenceKeys.PUSH_ENABLED.name } + .onStart { emit(CommunicationPreferenceKeys.PUSH_ENABLED) } + .map { preferences[CommunicationPreferenceKeys.PUSH_ENABLED] }, tokenFetcher.token.filterNotNull().filter { !it.isBlank() } ) { _, enabled, token -> if (enabled) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingPreferenceKeys.kt new file mode 100644 index 0000000000..434e9ea4ac --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingPreferenceKeys.kt @@ -0,0 +1,10 @@ +package org.thoughtcrime.securesms.onboarding + +import network.loki.messenger.BuildConfig +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object OnboardingPreferenceKeys { + val PUSH_ENABLED = PreferenceKey.boolean("pref_is_using_fcm${BuildConfig.PUSH_KEY_SUFFIX}", false) + val CONFIGURATION_SYNCED = PreferenceKey.boolean("pref_configuration_synced", false) + val PASSWORD_DISABLED = PreferenceKey.boolean("pref_disable_passphrase", true) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/LandingActivity.kt index cabc138f2d..3a14d0f009 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/LandingActivity.kt @@ -2,11 +2,12 @@ package org.thoughtcrime.securesms.onboarding.landing import android.os.Bundle import dagger.hilt.android.AndroidEntryPoint -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.onboarding.OnboardingPreferenceKeys import org.thoughtcrime.securesms.onboarding.loadaccount.LoadAccountActivity import org.thoughtcrime.securesms.onboarding.pickname.startPickDisplayNameActivity +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo @@ -17,7 +18,7 @@ import javax.inject.Inject class LandingActivity: BaseActionBarActivity() { @Inject - internal lateinit var prefs: TextSecurePreferences + internal lateinit var prefs: PreferenceStorage override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,7 +37,7 @@ class LandingActivity: BaseActionBarActivity() { } IdentityKeyUtil.generateIdentityKeyPair(this) - TextSecurePreferences.setPasswordDisabled(this, true) + prefs[OnboardingPreferenceKeys.PASSWORD_DISABLED] = true // AC: This is a temporary workaround to trick the old code that the screen is unlocked. KeyCachingService.setMasterSecret(applicationContext, Object()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt index bbb688785e..648152c178 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt @@ -12,11 +12,12 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import network.loki.messenger.R -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.onboarding.OnboardingPreferenceKeys import org.thoughtcrime.securesms.onboarding.manager.LoadAccountManager import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.applySafeInsetsPaddings import org.thoughtcrime.securesms.util.start @@ -26,7 +27,7 @@ import javax.inject.Inject class LoadAccountActivity : BaseActionBarActivity() { @Inject - internal lateinit var prefs: TextSecurePreferences + internal lateinit var prefs: PreferenceStorage @Inject internal lateinit var loadAccountManager: LoadAccountManager @@ -45,7 +46,7 @@ class LoadAccountActivity : BaseActionBarActivity() { ) supportActionBar?.setTitle(R.string.loadAccount) - prefs.setConfigurationMessageSynced(false) + prefs[OnboardingPreferenceKeys.CONFIGURATION_SYNCED] = false lifecycleScope.launch { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt index ecb923ca10..5cb67fbcf4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt @@ -7,7 +7,6 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.home.startHomeActivity @@ -22,9 +21,6 @@ class LoadingActivity: BaseActionBarActivity() { @Inject internal lateinit var configFactory: ConfigFactory - @Inject - internal lateinit var prefs: TextSecurePreferences - private val viewModel: LoadingViewModel by viewModels() private fun register(loadFailed: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt index 82cb14bb32..0b80af993a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.timeout import kotlinx.coroutines.launch import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.userConfigsChanged import org.session.libsession.utilities.withUserConfigs @@ -50,7 +49,6 @@ private val REFRESH_TIME = 50.milliseconds @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) @HiltViewModel internal class LoadingViewModel @Inject constructor( - val prefs: TextSecurePreferences, val configFactory: ConfigFactoryProtocol, private val loginStateRepository: LoginStateRepository, ): ViewModel() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt index 6e8dba568a..568ec4c8e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.onboarding.manager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.auth.LoggedInState diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt index abab1f68c8..a696d0ba01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt @@ -10,11 +10,9 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.home.startHomeActivity import org.thoughtcrime.securesms.onboarding.loading.LoadingActivity -import org.thoughtcrime.securesms.onboarding.manager.LoadAccountManager import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity.Companion.EXTRA_PROFILE_NAME import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo @@ -31,9 +29,6 @@ class MessageNotificationsActivity : BaseActionBarActivity() { @Inject internal lateinit var viewModelFactory: MessageNotificationsViewModel.AssistedFactory - @Inject lateinit var prefs: TextSecurePreferences - @Inject lateinit var loadAccountManager: LoadAccountManager - val profileName by lazy { intent.getStringExtra(EXTRA_PROFILE_NAME) } private val viewModel: MessageNotificationsViewModel by viewModels { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt index da904db003..8c53a651b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt @@ -14,14 +14,15 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R -import org.session.libsession.utilities.TextSecurePreferences +import org.thoughtcrime.securesms.onboarding.OnboardingPreferenceKeys import org.thoughtcrime.securesms.onboarding.manager.CreateAccountManager +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.util.ClearDataUtils internal class MessageNotificationsViewModel( private val state: State, private val application: Application, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val createAccountManager: CreateAccountManager, private val clearDataUtils: ClearDataUtils, ): AndroidViewModel(application) { @@ -39,7 +40,7 @@ internal class MessageNotificationsViewModel( viewModelScope.launch { if (state is State.CreateAccount) createAccountManager.createAccount(state.displayName) - prefs.setPushEnabled(uiStates.value.pushEnabled) + prefs[OnboardingPreferenceKeys.PUSH_ENABLED] = uiStates.value.pushEnabled _events.emit( when (state) { @@ -98,7 +99,7 @@ internal class MessageNotificationsViewModel( class Factory @AssistedInject constructor( @Assisted private val profileName: String?, private val application: Application, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val createAccountManager: CreateAccountManager, private val clearDataUtils: ClearDataUtils, ) : ViewModelProvider.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt index c84cc600a7..96e0506c17 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt @@ -12,22 +12,17 @@ import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.withCreationCallback import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.home.startHomeActivity import org.thoughtcrime.securesms.onboarding.messagenotifications.startMessageNotificationsActivity import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo -import javax.inject.Inject private const val EXTRA_LOAD_FAILED = "extra_load_failed" @AndroidEntryPoint class PickDisplayNameActivity : BaseActionBarActivity() { - @Inject - internal lateinit var prefs: TextSecurePreferences - private val viewModel: PickDisplayNameViewModel by viewModels(extrasProducer = { defaultViewModelCreationExtras.withCreationCallback { it.create(intent.getBooleanExtra(EXTRA_LOAD_FAILED, false)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt index 58dbf32288..d35c625e3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameViewModel.kt @@ -18,14 +18,11 @@ import network.loki.messenger.R import network.loki.messenger.libsession_util.PRIORITY_HIDDEN import org.session.libsession.messaging.messages.ProfileUpdateHandler import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.withMutableUserConfigs -import org.session.libsignal.utilities.Log @HiltViewModel(assistedFactory = PickDisplayNameViewModel.Factory::class) class PickDisplayNameViewModel @AssistedInject constructor( @Assisted private val loadFailed: Boolean, - private val prefs: TextSecurePreferences, private val configFactory: ConfigFactoryProtocol, ): ViewModel() { private val isCreateAccount = !loadFailed diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/CommunicationPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/CommunicationPreferenceKeys.kt new file mode 100644 index 0000000000..44319047cf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/CommunicationPreferenceKeys.kt @@ -0,0 +1,11 @@ +package org.thoughtcrime.securesms.preferences + +import network.loki.messenger.BuildConfig + +object CommunicationPreferenceKeys { + val READ_RECEIPTS = PreferenceKey.boolean("pref_read_receipts", false) + val TYPING_INDICATORS = PreferenceKey.boolean("pref_typing_indicators", false) + val CALL_NOTIFICATIONS_ENABLED = PreferenceKey.boolean("pref_call_notifications_enabled", false) + val FORCE_POST_PRO = PreferenceKey.boolean("pref_force_post_pro", false) + val PUSH_ENABLED = PreferenceKey.boolean("pref_is_using_fcm${BuildConfig.PUSH_KEY_SUFFIX}", false) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/ChatsPreferenceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/ChatsPreferenceViewModel.kt index 25a6f90caa..39331652fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/ChatsPreferenceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/ChatsPreferenceViewModel.kt @@ -7,20 +7,19 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.observeBooleanKey +import org.thoughtcrime.securesms.preferences.PreferenceStorage import javax.inject.Inject @HiltViewModel class ChatsPreferenceViewModel @Inject constructor( - var prefs: TextSecurePreferences + private val prefs: PreferenceStorage ) : ViewModel() { val uiState: StateFlow = combine( - prefs.observeBooleanKey(TextSecurePreferences.THREAD_TRIM_ENABLED, default = false), - prefs.observeBooleanKey(TextSecurePreferences.SEND_WITH_ENTER, default = false), - prefs.observeBooleanKey(TextSecurePreferences.AUTOPLAY_AUDIO_MESSAGES, default = false), + prefs.watch(viewModelScope, ChatsPreferenceKeys.THREAD_TRIM_ENABLED), + prefs.watch(viewModelScope, ChatsPreferenceKeys.SEND_WITH_ENTER), + prefs.watch(viewModelScope, ChatsPreferenceKeys.AUTOPLAY_AUDIO_MESSAGES), ) { trim, enter, autoplay -> UIState( trimThreads = trim, @@ -31,17 +30,17 @@ class ChatsPreferenceViewModel @Inject constructor( scope = viewModelScope, started = SharingStarted.Eagerly, initialValue = UIState( - trimThreads = prefs.isThreadLengthTrimmingEnabled(), - sendWithEnter = prefs.isSendWithEnterEnabled(), - autoplayAudioMessage = prefs.isAutoplayAudioMessagesEnabled(), + trimThreads = prefs[ChatsPreferenceKeys.THREAD_TRIM_ENABLED], + sendWithEnter = prefs[ChatsPreferenceKeys.SEND_WITH_ENTER], + autoplayAudioMessage = prefs[ChatsPreferenceKeys.AUTOPLAY_AUDIO_MESSAGES], ) ) fun onCommand(command: Commands) { when (command) { - is Commands.ToggleTrimThreads -> prefs.setThreadLengthTrimmingEnabled(command.isEnabled) - is Commands.ToggleSendWithEnter -> prefs.setSendWithEnterEnabled(command.isEnabled) - is Commands.ToggleAutoplayAudioMessages -> prefs.setAutoplayAudioMessages(command.isEnabled) + is Commands.ToggleTrimThreads -> prefs[ChatsPreferenceKeys.THREAD_TRIM_ENABLED] = command.isEnabled + is Commands.ToggleSendWithEnter -> prefs[ChatsPreferenceKeys.SEND_WITH_ENTER] = command.isEnabled + is Commands.ToggleAutoplayAudioMessages -> prefs[ChatsPreferenceKeys.AUTOPLAY_AUDIO_MESSAGES] = command.isEnabled } } @@ -56,4 +55,4 @@ class ChatsPreferenceViewModel @Inject constructor( val sendWithEnter: Boolean = false, val autoplayAudioMessage: Boolean = false ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/NotificationsPreferenceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/NotificationsPreferenceViewModel.kt index 6b3850a009..f8c27b367c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/NotificationsPreferenceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/NotificationsPreferenceViewModel.kt @@ -18,17 +18,15 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.observeBooleanKey -import org.session.libsession.utilities.observeStringKey import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.ui.isWhitelistedFromDoze import javax.inject.Inject import kotlin.Boolean @HiltViewModel class NotificationsPreferenceViewModel @Inject constructor( - var prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, val application: Application, ) : ViewModel() { @@ -46,11 +44,11 @@ class NotificationsPreferenceViewModel @Inject constructor( private val notifPrefsFlow = combine( - prefs.observeBooleanKey(TextSecurePreferences.HAS_CHECKED_DOZE_WHITELIST, default = false), - prefs.observeStringKey(TextSecurePreferences.RINGTONE_PREF, default = null), - prefs.observeBooleanKey(TextSecurePreferences.SOUND_WHEN_OPEN, default = false), - prefs.observeBooleanKey(TextSecurePreferences.VIBRATE_PREF, default = true), - prefs.observeStringKey(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF, default = "all"), + prefs.watch(viewModelScope, NotificationsPreferenceKeys.HAS_CHECKED_DOZE_WHITELIST), + prefs.watch(viewModelScope, NotificationsPreferenceKeys.RINGTONE), + prefs.watch(viewModelScope, NotificationsPreferenceKeys.SOUND_WHEN_OPEN), + prefs.watch(viewModelScope, NotificationsPreferenceKeys.VIBRATE), + prefs.watch(viewModelScope, NotificationsPreferenceKeys.NOTIFICATION_PRIVACY), ) { checkedDozeWhitelist, ringtonePrefString, soundWhenOpen, vibrate, notificationPrivacy -> NotifPrefsData( checkedDozeWhitelist = checkedDozeWhitelist, @@ -62,7 +60,7 @@ class NotificationsPreferenceViewModel @Inject constructor( } init { - combine(prefs.pushEnabled, notifPrefsFlow) { strategy, notif -> + combine(prefs.watch(viewModelScope, NotificationsPreferenceKeys.PUSH_ENABLED), notifPrefsFlow) { strategy, notif -> strategy to notif }.onEach { (isPushEnabled, notif) -> _uiState.update { old -> @@ -122,11 +120,11 @@ class NotificationsPreferenceViewModel @Inject constructor( val currentState = uiState.value val isEnabled = command.isEnabled - prefs.setPushEnabled(isEnabled) + prefs[NotificationsPreferenceKeys.PUSH_ENABLED] = isEnabled if (!isEnabled && !currentState.checkedDozeWhitelist) { _uiState.update { it.copy(showWhitelistEnableDialog = true) } - prefs.setHasCheckedDozeWhitelist(true) + prefs[NotificationsPreferenceKeys.HAS_CHECKED_DOZE_WHITELIST] = true } } @@ -144,7 +142,7 @@ class NotificationsPreferenceViewModel @Inject constructor( } Commands.RingtoneClicked -> { - val current = prefs.getNotificationRingtone() + val current = getNotificationRingtone() val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER) intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true) intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true) @@ -171,20 +169,20 @@ class NotificationsPreferenceViewModel @Inject constructor( var ringtoneUri = command.uri if (Settings.System.DEFAULT_NOTIFICATION_URI == ringtoneUri) { NotificationChannels.updateMessageRingtone(application, ringtoneUri) - prefs.removeNotificationRingtone() + prefs.remove(NotificationsPreferenceKeys.RINGTONE) } else { ringtoneUri = command.uri ?: Uri.EMPTY NotificationChannels.updateMessageRingtone(application, ringtoneUri) - prefs.setNotificationRingtone(ringtoneUri.toString()) + prefs[NotificationsPreferenceKeys.RINGTONE] = ringtoneUri.toString() } } is Commands.ToggleSoundWhenOpen -> { - prefs.setSoundWhenAppIsOpenEnabled(command.isEnabled) + prefs[NotificationsPreferenceKeys.SOUND_WHEN_OPEN] = command.isEnabled } is Commands.ToggleVibrate -> { - prefs.setNotificationVibrateEnabled(command.isEnabled) + prefs[NotificationsPreferenceKeys.VIBRATE] = command.isEnabled } Commands.OpenSystemBgWhitelist -> { @@ -213,7 +211,7 @@ class NotificationsPreferenceViewModel @Inject constructor( } is Commands.SelectNotificationPrivacyOption -> { - prefs.setNotificationPrivacy(command.option) + prefs[NotificationsPreferenceKeys.NOTIFICATION_PRIVACY] = command.option hideNotificationPrivacyDialog() } } @@ -283,6 +281,14 @@ class NotificationsPreferenceViewModel @Inject constructor( data class NotificationPrivacyOption(val value: String, val label: String) + private fun getNotificationRingtone(): Uri { + var result = prefs[NotificationsPreferenceKeys.RINGTONE] + if (result != null && result.startsWith("file:")) { + result = Settings.System.DEFAULT_NOTIFICATION_URI.toString() + } + return Uri.parse(result ?: Settings.System.DEFAULT_NOTIFICATION_URI.toString()) + } + private fun getRingtoneName(string: String?): String { var uriString = string if (uriString != null && string.startsWith("file:")) { @@ -304,4 +310,4 @@ class NotificationsPreferenceViewModel @Inject constructor( val vibrate: Boolean, val notificationPrivacyValue: String, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/PrivacySettingsPreferenceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/PrivacySettingsPreferenceViewModel.kt index da63239f2f..c4f13069cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/PrivacySettingsPreferenceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/PrivacySettingsPreferenceViewModel.kt @@ -17,17 +17,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED -import org.session.libsession.utilities.TextSecurePreferences.Companion.DISABLE_PASSPHRASE_PREF -import org.session.libsession.utilities.TextSecurePreferences.Companion.INCOGNITO_KEYBOARD_PREF -import org.session.libsession.utilities.TextSecurePreferences.Companion.READ_RECEIPTS_PREF -import org.session.libsession.utilities.TextSecurePreferences.Companion.SCREEN_LOCK -import org.session.libsession.utilities.TextSecurePreferences.Companion.TYPING_INDICATORS -import org.session.libsession.utilities.TextSecurePreferences.Companion.LINK_PREVIEWS -import org.session.libsession.utilities.observeBooleanKey import org.session.libsession.utilities.withMutableUserConfigs import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.preferences.PreferenceKey +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.preferences.compose.PrivacySettingsPreferenceViewModel.Commands.ShowCallsWarningDialog import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.Companion.areNotificationsEnabled @@ -35,7 +28,7 @@ import javax.inject.Inject @HiltViewModel class PrivacySettingsPreferenceViewModel @Inject constructor( - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val configFactory: ConfigFactory, private val app: Application, private val typingStatusRepository: TypingStatusRepository, @@ -61,19 +54,19 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( // Use this to get index for UI item. We need the index to tell the listState where to scroll // list things ordered by how they appear on the list private var prefItemsOrder = listOf( - CALL_NOTIFICATIONS_ENABLED, - SCREEN_LOCK, + PrivacyPreferenceKeys.CALL_NOTIFICATIONS_ENABLED.name, + PrivacyPreferenceKeys.SCREEN_LOCK.name, "community_message_requests", - READ_RECEIPTS_PREF, - TYPING_INDICATORS, - LINK_PREVIEWS, - INCOGNITO_KEYBOARD_PREF + PrivacyPreferenceKeys.READ_RECEIPTS.name, + PrivacyPreferenceKeys.TYPING_INDICATORS.name, + PrivacyPreferenceKeys.LINK_PREVIEWS.name, + PrivacyPreferenceKeys.INCOGNITO_KEYBOARD.name ) private val screenLockFlow = combine( - prefs.observeBooleanKey(SCREEN_LOCK, default = false), - prefs.observeBooleanKey(DISABLE_PASSPHRASE_PREF, default = false), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.SCREEN_LOCK), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.DISABLE_PASSPHRASE), keyguardSecure ) { screenLockPref, isPasswordDisabled, keyguard -> @@ -86,11 +79,11 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( private val togglesFlow = combine( - prefs.observeBooleanKey(CALL_NOTIFICATIONS_ENABLED, default = false), - prefs.observeBooleanKey(READ_RECEIPTS_PREF, default = false), - prefs.observeBooleanKey(TYPING_INDICATORS, default = false), - prefs.observeBooleanKey(LINK_PREVIEWS, default = false), - prefs.observeBooleanKey(INCOGNITO_KEYBOARD_PREF, default = false), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.CALL_NOTIFICATIONS_ENABLED), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.READ_RECEIPTS), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.TYPING_INDICATORS), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.LINK_PREVIEWS), + prefs.watch(viewModelScope, PrivacyPreferenceKeys.INCOGNITO_KEYBOARD), ) { callsEnabled, readReceipts, typing, linkPreviews, incognito -> TogglePrefsData(callsEnabled, readReceipts, typing, linkPreviews, incognito) } @@ -169,12 +162,12 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( action.scrollAndToggleKey?.let { key -> when (key) { - CALL_NOTIFICATIONS_ENABLED -> { + PrivacyPreferenceKeys.CALL_NOTIFICATIONS_ENABLED.name -> { // need to do some checks before toggling onCommand(ShowCallsWarningDialog) } - else -> prefs.updateBooleanFromKey(key, true) + else -> prefs[PreferenceKey.boolean(key)] = true } } @@ -190,7 +183,7 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( fun onCommand(command: Commands) { when (command) { is Commands.ToggleCallsNotification -> { - prefs.setCallNotificationsEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.CALL_NOTIFICATIONS_ENABLED] = command.isEnabled if (command.isEnabled && !areNotificationsEnabled(app)) { _uiState.update { it.copy(showCallsNotificationDialog = true) } } @@ -199,7 +192,7 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( is Commands.ToggleLockApp -> { // if UI disabled it, ignore if (!uiState.value.screenLockEnabled) return - prefs.setScreenLockEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.SCREEN_LOCK] = command.isEnabled viewModelScope.launch { _uiEvents.emit(PrivacySettingsPreferenceEvent.StartLockToggledService) } @@ -215,20 +208,20 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( } is Commands.ToggleReadReceipts -> { - prefs.setReadReceiptsEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.READ_RECEIPTS] = command.isEnabled } is Commands.ToggleTypingIndicators -> { - prefs.setTypingIndicatorsEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.TYPING_INDICATORS] = command.isEnabled if (!command.isEnabled) typingStatusRepository.clear() } is Commands.ToggleLinkPreviews -> { - prefs.setLinkPreviewsEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.LINK_PREVIEWS] = command.isEnabled } is Commands.ToggleIncognitoKeyboard -> { - prefs.setIncognitoKeyboardEnabled(command.isEnabled) + prefs[PrivacyPreferenceKeys.INCOGNITO_KEYBOARD] = command.isEnabled } Commands.AskMicPermission -> { @@ -343,4 +336,4 @@ class PrivacySettingsPreferenceViewModel @Inject constructor( val linkPreviews: Boolean, val incognito: Boolean, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/SettingsPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/SettingsPreferenceKeys.kt new file mode 100644 index 0000000000..13eee4c280 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/compose/SettingsPreferenceKeys.kt @@ -0,0 +1,29 @@ +package org.thoughtcrime.securesms.preferences.compose + +import network.loki.messenger.BuildConfig +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object PrivacyPreferenceKeys { + val CALL_NOTIFICATIONS_ENABLED = PreferenceKey.boolean("pref_call_notifications_enabled", false) + val SCREEN_LOCK = PreferenceKey.boolean("pref_android_screen_lock", false) + val DISABLE_PASSPHRASE = PreferenceKey.boolean("pref_disable_passphrase", true) + val READ_RECEIPTS = PreferenceKey.boolean("pref_read_receipts", false) + val TYPING_INDICATORS = PreferenceKey.boolean("pref_typing_indicators", false) + val LINK_PREVIEWS = PreferenceKey.boolean("pref_link_previews", false) + val INCOGNITO_KEYBOARD = PreferenceKey.boolean("pref_incognito_keyboard", true) +} + +object NotificationsPreferenceKeys { + val PUSH_ENABLED = PreferenceKey.boolean("pref_is_using_fcm${BuildConfig.PUSH_KEY_SUFFIX}", false) + val HAS_CHECKED_DOZE_WHITELIST = PreferenceKey.boolean("has_checked_doze_whitelist", false) + val RINGTONE = PreferenceKey.string("pref_key_ringtone") + val SOUND_WHEN_OPEN = PreferenceKey.boolean("pref_sound_when_app_open", false) + val VIBRATE = PreferenceKey.boolean("pref_key_vibrate", true) + val NOTIFICATION_PRIVACY = PreferenceKey.string("pref_notification_privacy", "all") +} + +object ChatsPreferenceKeys { + val THREAD_TRIM_ENABLED = PreferenceKey.boolean("pref_trim_threads", true) + val SEND_WITH_ENTER = PreferenceKey.boolean("pref_enter_sends", false) + val AUTOPLAY_AUDIO_MESSAGES = PreferenceKey.boolean("pref_autoplay_audio", false) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsPreferenceKeys.kt new file mode 100644 index 0000000000..b8759ddcd2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsPreferenceKeys.kt @@ -0,0 +1,10 @@ +package org.thoughtcrime.securesms.preferences.prosettings + +import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object ProSettingsPreferenceKeys { + val FORCE_CURRENT_USER_PRO = PreferenceKey.boolean("pref_force_current_user_pro", false) + val DEBUG_WITHIN_QUICK_REFUND = PreferenceKey.boolean("debug_within_quick_refund", false) + val DEBUG_PRO_PLAN_STATUS = PreferenceKey.enum("debug_pro_plan_status") +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsViewModel.kt index ba32928537..3caa2331bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsViewModel.kt @@ -45,11 +45,11 @@ import org.session.libsession.utilities.StringSubstitutionConstants.PRO_KEY import org.session.libsession.utilities.StringSubstitutionConstants.SELECTED_PLAN_LENGTH_KEY import org.session.libsession.utilities.StringSubstitutionConstants.SELECTED_PLAN_LENGTH_SINGULAR_KEY import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.withMutableUserConfigs import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.debugmenu.DebugLogGroup import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.ShowOpenUrlDialog import org.thoughtcrime.securesms.pro.ProDataState import org.thoughtcrime.securesms.pro.ProDetailsRepository @@ -79,7 +79,7 @@ class ProSettingsViewModel @AssistedInject constructor( private val proStatusManager: ProStatusManager, private val subscriptionCoordinator: SubscriptionCoordinator, private val dateUtils: DateUtils, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val proDetailsRepository: ProDetailsRepository, private val configFactory: Lazy, private val storage: StorageProtocol, @@ -392,7 +392,9 @@ class ProSettingsViewModel @AssistedInject constructor( viewModelScope.launch { _refundPlanState.update { - val isQuickRefund = if(prefs.forceCurrentUserAsPro()) prefs.getDebugIsWithinQuickRefund()// debug mode + val isQuickRefund = if (prefs[ProSettingsPreferenceKeys.FORCE_CURRENT_USER_PRO]) { + prefs[ProSettingsPreferenceKeys.DEBUG_WITHIN_QUICK_REFUND] // debug mode + } else sub.isWithinQuickRefundWindow() State.Success( @@ -949,7 +951,7 @@ class ProSettingsViewModel @AssistedInject constructor( private fun refreshProStats(){ viewModelScope.launch { // if we have a debug toggle for the loading state, respect it - val currentDebugState = prefs.getDebugProPlanStatus() + val currentDebugState = prefs[ProSettingsPreferenceKeys.DEBUG_PRO_PLAN_STATUS] val debugState = when(currentDebugState) { DebugMenuViewModel.DebugProPlanStatus.LOADING -> State.Loading DebugMenuViewModel.DebugProPlanStatus.ERROR -> State.Error(Exception()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewManager.kt b/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewManager.kt index b00d41ffbb..dbbaa6f065 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewManager.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest @@ -17,15 +16,12 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.serialization.json.Json -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.dependencies.ManagerScope +import org.thoughtcrime.securesms.preferences.PreferenceStorage import java.util.EnumSet import javax.inject.Inject import javax.inject.Singleton @@ -35,18 +31,15 @@ import kotlin.time.Duration.Companion.days @Singleton class InAppReviewManager @Inject constructor( @param:ApplicationContext val context: Context, - private val prefs: TextSecurePreferences, - private val json: Json, + private val prefs: PreferenceStorage, private val storeReviewManager: StoreReviewManager, @param:ManagerScope private val scope: CoroutineScope, ) { - private val stateChangeNotification = MutableSharedFlow(extraBufferCapacity = 1) + private val reviewState = prefs.watch(scope, InAppReviewPreferenceKeys.REVIEW_STATE) private val eventsChannel: SendChannel @Suppress("OPT_IN_USAGE") - val shouldShowPrompt: StateFlow = stateChangeNotification - .onStart { emit(Unit) } - .map { prefs.reviewState } + val shouldShowPrompt: StateFlow = reviewState .flatMapLatest { state -> when (state) { InAppReviewState.DismissedForever, is InAppReviewState.WaitingForTrigger, null -> flowOf(false) @@ -74,7 +67,7 @@ class InAppReviewManager @Inject constructor( eventsChannel = channel scope.launch { - val startState = prefs.reviewState ?: run { + val startState = reviewState.value ?: run { if (storeReviewManager.supportsReviewFlow) { val pkg = context.packageManager.getPackageInfo(context.packageName, 0) InAppReviewState.WaitingForTrigger( @@ -131,7 +124,7 @@ class InAppReviewManager @Inject constructor( } .distinctUntilChanged() .collectLatest { - prefs.reviewState = it + prefs[InAppReviewPreferenceKeys.REVIEW_STATE] = it Log.d(TAG, "New review state is: $it") } } @@ -149,23 +142,10 @@ class InAppReviewManager @Inject constructor( Dismiss, } - private var TextSecurePreferences.reviewState - get() = prefs.inAppReviewState?.let { - runCatching { json.decodeFromString(it) } - .onFailure { Log.w(TAG, "Failed to decode review state", it) } - .getOrNull() - } - set(value) { - prefs.inAppReviewState = - value?.let { json.encodeToString(InAppReviewState.serializer(), it) } - stateChangeNotification.tryEmit(Unit) - } - - companion object { private const val TAG = "InAppReviewManager" @VisibleForTesting val REVIEW_REQUEST_DISMISS_DELAY = 14.days } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewPreferenceKeys.kt new file mode 100644 index 0000000000..e6eb952391 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/reviews/InAppReviewPreferenceKeys.kt @@ -0,0 +1,11 @@ +package org.thoughtcrime.securesms.reviews + +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object InAppReviewPreferenceKeys { + val REVIEW_STATE = PreferenceKey.json("in_app_review_state") + val SHOW_DONATION_CTA_FROM_POSITIVE_REVIEW = PreferenceKey.boolean( + "show_donation_cta_from_positive_review", + false + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/reviews/ui/InAppReviewViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/reviews/ui/InAppReviewViewModel.kt index ba1a6833b2..39b9ae1eb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reviews/ui/InAppReviewViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/reviews/ui/InAppReviewViewModel.kt @@ -14,10 +14,11 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.reviews.InAppReviewManager +import org.thoughtcrime.securesms.reviews.InAppReviewPreferenceKeys import org.thoughtcrime.securesms.reviews.StoreReviewManager +import org.thoughtcrime.securesms.preferences.PreferenceStorage import javax.inject.Inject private const val TAG = "InAppReviewViewModel" @@ -26,7 +27,7 @@ private const val TAG = "InAppReviewViewModel" class InAppReviewViewModel @Inject constructor( private val manager: InAppReviewManager, private val storeReviewManager: StoreReviewManager, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, ) : ViewModel() { private val commands = MutableSharedFlow(extraBufferCapacity = 1) @@ -51,7 +52,7 @@ class InAppReviewViewModel @Inject constructor( // "It's Great" button clicked UiCommand.NegativeButtonClicked -> { // mark the app as needing to display the donation post positive review - prefs.setShowDonationCTAFromPositiveReview(true) + prefs[InAppReviewPreferenceKeys.SHOW_DONATION_CTA_FROM_POSITIVE_REVIEW] = true UiState.PositivePrompt } @@ -146,4 +147,4 @@ class InAppReviewViewModel @Inject constructor( * Whether we actually show the prompt will be further controlled by us. */ private data object ShowPrompt : Event -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ReadReceiptManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ReadReceiptManager.kt index b685a86725..9c301b950c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ReadReceiptManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ReadReceiptManager.kt @@ -2,16 +2,17 @@ package org.thoughtcrime.securesms.sskenvironment import org.session.libsession.utilities.Address import org.session.libsession.utilities.ReadReceiptManagerProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId import org.thoughtcrime.securesms.database.MmsSmsDatabase +import org.thoughtcrime.securesms.preferences.CommunicationPreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage import javax.inject.Inject import javax.inject.Singleton @Singleton class ReadReceiptManager @Inject constructor( - private val textSecurePreferences: TextSecurePreferences, + private val prefs: PreferenceStorage, private val mmsSmsDatabase: MmsSmsDatabase, ): ReadReceiptManagerProtocol { @@ -20,7 +21,7 @@ class ReadReceiptManager @Inject constructor( sentTimestamps: List, readTimestamp: Long ) { - if (textSecurePreferences.isReadReceiptsEnabled()) { + if (prefs[CommunicationPreferenceKeys.READ_RECEIPTS]) { // Redirect message to master device conversation var address = Address.fromSerialized(fromRecipientId) @@ -30,4 +31,4 @@ class ReadReceiptManager @Inject constructor( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateTimePreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/util/DateTimePreferenceKeys.kt new file mode 100644 index 0000000000..82534f9096 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateTimePreferenceKeys.kt @@ -0,0 +1,8 @@ +package org.thoughtcrime.securesms.util + +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object DateTimePreferenceKeys { + val DATE_FORMAT = PreferenceKey.string("libsession.DATE_FORMAT_PREF") + val TIME_FORMAT = PreferenceKey.string("libsession.TIME_FORMAT_PREF") +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt index d1ba385308..c26ffe8461 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt @@ -7,10 +7,8 @@ import android.icu.util.MeasureUnit import android.text.format.DateFormat import dagger.hilt.android.qualifiers.ApplicationContext import network.loki.messenger.R -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.TextSecurePreferences.Companion.DATE_FORMAT_PREF -import org.session.libsession.utilities.TextSecurePreferences.Companion.TIME_FORMAT_PREF import org.session.libsignal.utilities.Log +import org.thoughtcrime.securesms.preferences.PreferenceStorage import java.time.Duration import java.time.Instant import java.time.LocalDate @@ -34,7 +32,7 @@ enum class RelativeDay { TODAY, YESTERDAY, TOMORROW } @Singleton class DateUtils @Inject constructor( @ApplicationContext private val context: Context, - private val textSecurePreferences: TextSecurePreferences + private val prefs: PreferenceStorage ) { private val tag = "DateUtils" @@ -63,20 +61,18 @@ class DateUtils @Inject constructor( // User preferences with property accessors private var userDateFormat: String - get() = textSecurePreferences.getStringPreference(DATE_FORMAT_PREF, defaultDateFormat)!! + get() = prefs[DateTimePreferenceKeys.DATE_FORMAT] ?: defaultDateFormat private set(value) { - textSecurePreferences.setStringPreference(DATE_FORMAT_PREF, value) + prefs[DateTimePreferenceKeys.DATE_FORMAT] = value } // The user time format is the one chosen by the user,if they chose one from the ui (not yet available but coming later) // Or we check for the system preference setting for 12 vs 24h format private var userTimeFormat: String - get() = textSecurePreferences.getStringPreference( - TIME_FORMAT_PREF, - if (DateFormat.is24HourFormat(context)) defaultTimeFormat else twelveHourFormat - )!! + get() = prefs[DateTimePreferenceKeys.TIME_FORMAT] + ?: if (DateFormat.is24HourFormat(context)) defaultTimeFormat else twelveHourFormat private set(value) { - textSecurePreferences.setStringPreference(TIME_FORMAT_PREF, value) + prefs[DateTimePreferenceKeys.TIME_FORMAT] = value } // Public getters @@ -318,4 +314,4 @@ class DateUtils @Inject constructor( fun getLocaleFormattedDate(timestamp: Long, specificPattern: String): String = formatTime(timestamp, specificPattern) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt index 9db2f108a9..7fa7f3e7d0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt @@ -9,7 +9,6 @@ import org.session.libsession.messaging.messages.control.CallMessage import org.session.libsession.messaging.utilities.WebRtcUtils import org.session.libsession.network.SnodeClock import org.session.libsession.utilities.Address -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import org.session.protos.SessionProtos.CallMessage.Type.ANSWER import org.session.protos.SessionProtos.CallMessage.Type.END_CALL @@ -21,6 +20,8 @@ import org.thoughtcrime.securesms.auth.AuthAwareComponent import org.thoughtcrime.securesms.auth.LoggedInState import org.thoughtcrime.securesms.database.RecipientRepository import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.preferences.CommunicationPreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.webrtc.IceCandidate import javax.inject.Inject import javax.inject.Singleton @@ -28,7 +29,7 @@ import javax.inject.Singleton @Singleton class CallMessageProcessor @Inject constructor( @param:ApplicationContext private val context: Context, - private val textSecurePreferences: TextSecurePreferences, + private val prefs: PreferenceStorage, private val storage: StorageProtocol, private val webRtcBridge: WebRtcCallBridge, private val recipientRepository: RecipientRepository, @@ -52,7 +53,7 @@ class CallMessageProcessor @Inject constructor( // If the user has not enabled voice/video calls or if the user has not granted audio/microphone permissions if ( - !textSecurePreferences.isCallNotificationsEnabled() || + !prefs[CommunicationPreferenceKeys.CALL_NOTIFICATIONS_ENABLED] || !Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO) ) { Log.d("Loki","Dropping call message if call notifications disabled") @@ -160,4 +161,4 @@ class CallMessageProcessor @Inject constructor( } } -} \ No newline at end of file +} From 79da96c4d53e75d824a34a8a48fd91e96f8cc00d Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Fri, 27 Feb 2026 08:53:07 +1100 Subject: [PATCH 2/4] Remove unused TextSecurePreferences observe helpers --- .../utilities/TextSecurePreferences.kt | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 81a63f787d..e731749759 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -10,15 +10,10 @@ import androidx.core.content.edit import androidx.preference.PreferenceManager.getDefaultSharedPreferences import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update import kotlinx.serialization.json.Json import network.loki.messenger.BuildConfig @@ -1548,23 +1543,3 @@ class AppTextSecurePreferences @Inject constructor( _events.tryEmit(key) } } - -fun TextSecurePreferences.observeBooleanKey( - key: String, - default: Boolean -): Flow = - TextSecurePreferences.events - .filter { it == key } - .onStart { emit(key) } // trigger initial read - .map { getBooleanPreference(key, default) } - .distinctUntilChanged() - -fun TextSecurePreferences.observeStringKey( - key: String, - default: String? -): Flow = - TextSecurePreferences.events - .filter { it == key } - .onStart { emit(key) } - .map { getStringPreference(key, default) } - .distinctUntilChanged() \ No newline at end of file From a7ef11406de7dbc3c7989830d4d2189faa08fefc Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Fri, 27 Feb 2026 09:36:42 +1100 Subject: [PATCH 3/4] Migrate pro/debug prefs off TextSecurePreferences --- .../utilities/TextSecurePreferences.kt | 155 ------------------ .../securesms/database/RecipientRepository.kt | 37 +++-- .../securesms/debugmenu/DebugMenuViewModel.kt | 59 ++++--- .../notifications/DefaultMessageNotifier.kt | 6 +- .../securesms/pro/FetchProDetailsWorker.kt | 8 +- .../securesms/pro/ProDetailsRepository.kt | 8 +- .../securesms/pro/ProPreferenceKeys.kt | 21 +++ .../securesms/pro/ProStatusManager.kt | 44 ++--- .../DefaultConversationRepository.kt | 14 +- .../PlayStoreSubscriptionManager.kt | 17 +- 10 files changed, 121 insertions(+), 248 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/pro/ProPreferenceKeys.kt diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index e731749759..1265eb3a9a 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -18,10 +18,6 @@ import kotlinx.coroutines.flow.update import kotlinx.serialization.json.Json import network.loki.messenger.BuildConfig import network.loki.messenger.R -import network.loki.messenger.libsession_util.protocol.ProFeature -import network.loki.messenger.libsession_util.protocol.ProMessageFeature -import network.loki.messenger.libsession_util.protocol.ProProfileFeature -import network.loki.messenger.libsession_util.util.toBitSet import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.file_server.FileServer import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY_AUDIO_MESSAGES @@ -38,7 +34,6 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.FORCED_S import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_CHECKED_DOZE_WHITELIST import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_COPIED_DONATION_URL import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_DONATED -import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_HIDDEN_MESSAGE_REQUESTS import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_SEEN_PRO_EXPIRED import org.session.libsession.utilities.TextSecurePreferences.Companion.HAS_SEEN_PRO_EXPIRING import org.session.libsession.utilities.TextSecurePreferences.Companion.HAVE_SHOWN_A_NOTIFICATION_ABOUT_TOKEN_PAGE @@ -55,18 +50,11 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.SEEN_DON import org.session.libsession.utilities.TextSecurePreferences.Companion.SELECTED_ACCENT_COLOR import org.session.libsession.utilities.TextSecurePreferences.Companion.SELECTED_STYLE import org.session.libsession.utilities.TextSecurePreferences.Companion.SEND_WITH_ENTER -import org.session.libsession.utilities.TextSecurePreferences.Companion.SET_FORCE_CURRENT_USER_PRO -import org.session.libsession.utilities.TextSecurePreferences.Companion.SET_FORCE_INCOMING_MESSAGE_PRO -import org.session.libsession.utilities.TextSecurePreferences.Companion.SET_FORCE_OTHER_USERS_PRO -import org.session.libsession.utilities.TextSecurePreferences.Companion.SET_FORCE_POST_PRO import org.session.libsession.utilities.TextSecurePreferences.Companion.SHOWN_CALL_NOTIFICATION import org.session.libsession.utilities.TextSecurePreferences.Companion.SHOWN_CALL_WARNING import org.session.libsession.utilities.TextSecurePreferences.Companion.SHOW_DONATION_CTA_FROM_POSITIVE_REVIEW import org.session.libsession.utilities.TextSecurePreferences.Companion._events import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel -import org.thoughtcrime.securesms.pro.toProMessageFeatures -import org.thoughtcrime.securesms.pro.toProProfileFeatures import java.io.IOException import java.time.ZonedDateTime import javax.inject.Inject @@ -161,21 +149,10 @@ interface TextSecurePreferences { fun setLastOpenDate() fun hasSeenLinkPreviewSuggestionDialog(): Boolean fun setHasSeenLinkPreviewSuggestionDialog() - fun hasHiddenMessageRequests(): Boolean - fun setHasHiddenMessageRequests(hidden: Boolean) - fun forceCurrentUserAsPro(): Boolean - fun setForceCurrentUserAsPro(isPro: Boolean) - fun forceOtherUsersAsPro(): Boolean - fun setForceOtherUsersAsPro(isPro: Boolean) - fun forceIncomingMessagesAsPro(): Boolean - fun setForceIncomingMessagesAsPro(isPro: Boolean) - fun forcePostPro(): Boolean - fun setForcePostPro(postPro: Boolean) fun hasSeenProExpiring(): Boolean fun setHasSeenProExpiring() fun hasSeenProExpired(): Boolean fun setHasSeenProExpired() - fun watchPostProStatus(): StateFlow fun setShownCallWarning(): Boolean fun setShownCallNotification(): Boolean fun setCallNotificationsEnabled(enabled : Boolean) @@ -208,18 +185,6 @@ interface TextSecurePreferences { fun forcedShortTTL(): Boolean fun setForcedShortTTL(value: Boolean) - fun getDebugMessageFeatures(): Set - fun setDebugMessageFeatures(features: Set) - - fun getDebugSubscriptionType(): DebugMenuViewModel.DebugSubscriptionStatus? - fun setDebugSubscriptionType(status: DebugMenuViewModel.DebugSubscriptionStatus?) - fun getDebugProPlanStatus(): DebugMenuViewModel.DebugProPlanStatus? - fun setDebugProPlanStatus(status: DebugMenuViewModel.DebugProPlanStatus?) - fun getDebugForceNoBilling(): Boolean - fun setDebugForceNoBilling(hasBilling: Boolean) - fun getDebugIsWithinQuickRefund(): Boolean - fun setDebugIsWithinQuickRefund(isWithin: Boolean) - fun setSubscriptionProvider(provider: String) fun getSubscriptionProvider(): String? @@ -321,11 +286,6 @@ interface TextSecurePreferences { const val CONFIGURATION_SYNCED = "pref_configuration_synced" const val PROFILE_PIC_EXPIRY = "profile_pic_expiry" const val LAST_OPEN_DATE = "pref_last_open_date" - const val HAS_HIDDEN_MESSAGE_REQUESTS = "pref_message_requests_hidden" - const val SET_FORCE_CURRENT_USER_PRO = "pref_force_current_user_pro" - const val SET_FORCE_OTHER_USERS_PRO = "pref_force_other_users_pro" - const val SET_FORCE_INCOMING_MESSAGE_PRO = "pref_force_incoming_message_pro" - const val SET_FORCE_POST_PRO = "pref_force_post_pro" const val HAS_SEEN_PRO_EXPIRING = "has_seen_pro_expiring" const val HAS_SEEN_PRO_EXPIRED = "has_seen_pro_expired" const val CALL_NOTIFICATIONS_ENABLED = "pref_call_notifications_enabled" @@ -388,13 +348,6 @@ interface TextSecurePreferences { const val IN_APP_REVIEW_STATE = "in_app_review_state" - const val DEBUG_PRO_MESSAGE_FEATURES = "debug_pro_message_features" - const val DEBUG_PRO_PROFILE_FEATURES = "debug_pro_profile_features" - const val DEBUG_SUBSCRIPTION_STATUS = "debug_subscription_status" - const val DEBUG_PRO_PLAN_STATUS = "debug_pro_plan_status" - const val DEBUG_FORCE_NO_BILLING = "debug_pro_has_billing" - const val DEBUG_WITHIN_QUICK_REFUND = "debug_within_quick_refund" - const val SUBSCRIPTION_PROVIDER = "session_subscription_provider" const val DEBUG_AVATAR_REUPLOAD = "debug_avatar_reupload" @@ -605,12 +558,6 @@ interface TextSecurePreferences { return getLongPreference(context, PROFILE_PIC_EXPIRY, 0) } - @JvmStatic - fun removeHasHiddenMessageRequests(context: Context) { - removePreference(context, HAS_HIDDEN_MESSAGE_REQUESTS) - _events.tryEmit(HAS_HIDDEN_MESSAGE_REQUESTS) - } - @JvmStatic fun isCallNotificationsEnabled(context: Context): Boolean { return getBooleanPreference(context, CALL_NOTIFICATIONS_ENABLED, false) @@ -655,7 +602,6 @@ class AppTextSecurePreferences @Inject constructor( @param:ApplicationContext private val context: Context, private val json: Json, ): TextSecurePreferences { - private val postProLaunchState = MutableStateFlow(getBooleanPreference(SET_FORCE_POST_PRO, if (BuildConfig.BUILD_TYPE != "release") true else false)) private val hiddenPasswordState = MutableStateFlow(getBooleanPreference(HIDE_PASSWORD, false)) override var migratedToGroupV2Config: Boolean @@ -1139,50 +1085,6 @@ class AppTextSecurePreferences @Inject constructor( return previousValue != setValue } - override fun hasHiddenMessageRequests(): Boolean { - return getBooleanPreference(HAS_HIDDEN_MESSAGE_REQUESTS, false) - } - - override fun setHasHiddenMessageRequests(hidden: Boolean) { - setBooleanPreference(HAS_HIDDEN_MESSAGE_REQUESTS, hidden) - _events.tryEmit(HAS_HIDDEN_MESSAGE_REQUESTS) - } - override fun forceCurrentUserAsPro(): Boolean { - return getBooleanPreference(SET_FORCE_CURRENT_USER_PRO, false) - } - - override fun setForceCurrentUserAsPro(isPro: Boolean) { - setBooleanPreference(SET_FORCE_CURRENT_USER_PRO, isPro) - _events.tryEmit(SET_FORCE_CURRENT_USER_PRO) - } - - override fun forceOtherUsersAsPro(): Boolean { - return getBooleanPreference(SET_FORCE_OTHER_USERS_PRO, false) - } - - override fun setForceOtherUsersAsPro(isPro: Boolean) { - setBooleanPreference(SET_FORCE_OTHER_USERS_PRO, isPro) - _events.tryEmit(SET_FORCE_OTHER_USERS_PRO) - } - - override fun forceIncomingMessagesAsPro(): Boolean { - return getBooleanPreference(SET_FORCE_INCOMING_MESSAGE_PRO, false) - } - - override fun setForceIncomingMessagesAsPro(isPro: Boolean) { - setBooleanPreference(SET_FORCE_INCOMING_MESSAGE_PRO, isPro) - } - - override fun forcePostPro(): Boolean { - return postProLaunchState.value - } - - override fun setForcePostPro(postPro: Boolean) { - setBooleanPreference(SET_FORCE_POST_PRO, postPro) - postProLaunchState.update { postPro } - _events.tryEmit(SET_FORCE_POST_PRO) - } - override fun hasSeenProExpiring(): Boolean { return getBooleanPreference(HAS_SEEN_PRO_EXPIRING, false) } @@ -1199,10 +1101,6 @@ class AppTextSecurePreferences @Inject constructor( setBooleanPreference(HAS_SEEN_PRO_EXPIRED, true) } - override fun watchPostProStatus(): StateFlow { - return postProLaunchState - } - override fun getFingerprintKeyGenerated(): Boolean { return getBooleanPreference(TextSecurePreferences.FINGERPRINT_KEY_GENERATED, false) } @@ -1307,7 +1205,6 @@ class AppTextSecurePreferences @Inject constructor( */ override fun clearAll() { pushEnabled.update { false } - postProLaunchState.update { false } hiddenPasswordState.update { false } getDefaultSharedPreferences(context).edit(commit = true) { clear() } @@ -1373,58 +1270,6 @@ class AppTextSecurePreferences @Inject constructor( setStringPreference(TextSecurePreferences.DEPRECATING_START_TIME_OVERRIDE, value.toString()) } } - override fun getDebugMessageFeatures(): Set { - return buildSet { - getLongPreference(TextSecurePreferences.DEBUG_PRO_MESSAGE_FEATURES, 0L).toProMessageFeatures(this) - getLongPreference(TextSecurePreferences.DEBUG_PRO_PROFILE_FEATURES, 0L).toProProfileFeatures(this) - } - } - - override fun setDebugMessageFeatures(features: Set) { - setLongPreference(TextSecurePreferences.DEBUG_PRO_MESSAGE_FEATURES, features.filterIsInstance().toBitSet().rawValue) - setLongPreference(TextSecurePreferences.DEBUG_PRO_PROFILE_FEATURES, features.filterIsInstance().toBitSet().rawValue) - } - - override fun getDebugSubscriptionType(): DebugMenuViewModel.DebugSubscriptionStatus? { - return getStringPreference(TextSecurePreferences.DEBUG_SUBSCRIPTION_STATUS, null)?.let { - DebugMenuViewModel.DebugSubscriptionStatus.valueOf(it) - } - } - - override fun setDebugSubscriptionType(status: DebugMenuViewModel.DebugSubscriptionStatus?) { - setStringPreference(TextSecurePreferences.DEBUG_SUBSCRIPTION_STATUS, status?.name) - _events.tryEmit(TextSecurePreferences.DEBUG_SUBSCRIPTION_STATUS) - } - - override fun getDebugProPlanStatus(): DebugMenuViewModel.DebugProPlanStatus? { - return getStringPreference(TextSecurePreferences.DEBUG_PRO_PLAN_STATUS, null)?.let { - DebugMenuViewModel.DebugProPlanStatus.valueOf(it) - } - } - - override fun setDebugProPlanStatus(status: DebugMenuViewModel.DebugProPlanStatus?) { - setStringPreference(TextSecurePreferences.DEBUG_PRO_PLAN_STATUS, status?.name) - _events.tryEmit(TextSecurePreferences.DEBUG_PRO_PLAN_STATUS) - } - - override fun getDebugForceNoBilling(): Boolean { - return getBooleanPreference(TextSecurePreferences.DEBUG_FORCE_NO_BILLING, false) - } - - override fun setDebugForceNoBilling(hasBilling: Boolean) { - setBooleanPreference(TextSecurePreferences.DEBUG_FORCE_NO_BILLING, hasBilling) - _events.tryEmit(TextSecurePreferences.DEBUG_FORCE_NO_BILLING) - } - - override fun getDebugIsWithinQuickRefund(): Boolean { - return getBooleanPreference(TextSecurePreferences.DEBUG_WITHIN_QUICK_REFUND, false) - } - - override fun setDebugIsWithinQuickRefund(isWithin: Boolean) { - setBooleanPreference(TextSecurePreferences.DEBUG_WITHIN_QUICK_REFUND, isWithin) - _events.tryEmit(TextSecurePreferences.DEBUG_FORCE_NO_BILLING) - } - override fun getSubscriptionProvider(): String? { return getStringPreference(TextSecurePreferences.SUBSCRIPTION_PROVIDER, null) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt index f009075a38..2a7c9561d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt @@ -40,7 +40,6 @@ import org.session.libsession.utilities.Address.Companion.toAddress import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.ConfigUpdateNotification import org.session.libsession.utilities.GroupRecord -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.getGroup import org.session.libsession.utilities.recipients.Recipient @@ -59,9 +58,12 @@ import org.thoughtcrime.securesms.database.model.RecipientSettings import org.thoughtcrime.securesms.dependencies.ManagerScope import org.thoughtcrime.securesms.groups.GroupMemberComparator import org.thoughtcrime.securesms.pro.ProDataState +import org.thoughtcrime.securesms.pro.ProPreferenceKeys import org.thoughtcrime.securesms.pro.ProStatus import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.pro.db.ProDatabase +import org.thoughtcrime.securesms.preferences.PreferenceKey +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.util.DateUtils.Companion.secondsToInstant import java.lang.ref.WeakReference import java.time.Duration @@ -82,7 +84,7 @@ import javax.inject.Singleton @Singleton class RecipientRepository @Inject constructor( private val configFactory: ConfigFactoryProtocol, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val groupDatabase: GroupDatabase, private val recipientSettingsDatabase: RecipientSettingsDatabase, private val blindedIdMappingRepository: BlindMappingRepository, @@ -95,6 +97,11 @@ class RecipientRepository @Inject constructor( ) { private val recipientFlowCache = LruCache>>(512) + private fun proDebugPreferenceChanges(vararg keys: PreferenceKey<*>): Flow> { + val names = keys.mapTo(hashSetOf()) { it.name } + return prefs.changes().filter { it.name in names } + } + fun observeRecipient(address: Address): Flow { val cache = recipientFlowCache[address]?.get() if (cache != null) { @@ -213,10 +220,10 @@ class RecipientRepository @Inject constructor( changeSources = if (needFlow) { arrayListOf( configFactory.userConfigsChanged(onlyConfigTypes = EnumSet.of(UserConfigType.USER_PROFILE)), - TextSecurePreferences.events.filter { - it == TextSecurePreferences.SET_FORCE_CURRENT_USER_PRO - || it == TextSecurePreferences.DEBUG_SUBSCRIPTION_STATUS - }, + proDebugPreferenceChanges( + ProPreferenceKeys.FORCE_CURRENT_USER_PRO, + ProPreferenceKeys.DEBUG_SUBSCRIPTION_STATUS, + ), ) } else { null @@ -229,7 +236,7 @@ class RecipientRepository @Inject constructor( changeSources = if (needFlow) { arrayListOf( configFactory.userConfigsChanged(onlyConfigTypes = EnumSet.of(UserConfigType.CONTACTS)), - TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_OTHER_USERS_PRO }, + proDebugPreferenceChanges(ProPreferenceKeys.FORCE_OTHER_USERS_PRO), proDatabase.revocationChangeNotification, ) } else { @@ -248,7 +255,7 @@ class RecipientRepository @Inject constructor( arrayListOf( configFactory.userConfigsChanged(onlyConfigTypes = EnumSet.of(UserConfigType.CONTACTS)), recipientSettingsDatabase.changeNotification.filter { it == address }, - TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_OTHER_USERS_PRO }, + proDebugPreferenceChanges(ProPreferenceKeys.FORCE_OTHER_USERS_PRO), proDatabase.revocationChangeNotification, ) } else { @@ -279,7 +286,7 @@ class RecipientRepository @Inject constructor( it ) }, - TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_OTHER_USERS_PRO }, + proDebugPreferenceChanges(ProPreferenceKeys.FORCE_OTHER_USERS_PRO), proDatabase.revocationChangeNotification, ) } else { @@ -403,7 +410,7 @@ class RecipientRepository @Inject constructor( .filter { it.groupId == address.accountId }, configFactory.userConfigsChanged(), recipientSettingsDatabase.changeNotification.filter { it == address }, - TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_OTHER_USERS_PRO }, + proDebugPreferenceChanges(ProPreferenceKeys.FORCE_OTHER_USERS_PRO), configFactory.userConfigsChanged(EnumSet.of(UserConfigType.USER_PROFILE)), ) } else { @@ -421,7 +428,7 @@ class RecipientRepository @Inject constructor( changeSources = if (needFlow) { arrayListOf( recipientSettingsDatabase.changeNotification.filter { it == address }, - TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_OTHER_USERS_PRO }, + proDebugPreferenceChanges(ProPreferenceKeys.FORCE_OTHER_USERS_PRO), configFactory.userConfigsChanged(EnumSet.of(UserConfigType.USER_PROFILE)), ) } else { @@ -506,12 +513,12 @@ class RecipientRepository @Inject constructor( } // 3. Apply Debug Overrides - if (recipient.isSelf && proData == null && prefs.forceCurrentUserAsPro()) { + if (recipient.isSelf && proData == null && prefs[ProPreferenceKeys.FORCE_CURRENT_USER_PRO]) { proData = RecipientData.ProData(showProBadge = true) } else if (!recipient.isSelf && (recipient.address is Address.Standard) && proData == null - && prefs.forceOtherUsersAsPro() + && prefs[ProPreferenceKeys.FORCE_OTHER_USERS_PRO] ) { proData = RecipientData.ProData(showProBadge = true) } @@ -660,7 +667,7 @@ class RecipientRepository @Inject constructor( configFactory.withUserConfigs { configs -> val pro = configs.userProfile.getProConfig() - if (prefs.forceCurrentUserAsPro()) { + if (prefs[ProPreferenceKeys.FORCE_CURRENT_USER_PRO]) { proDataContext?.addProData( RecipientSettings.ProData( showProBadge = configs.userProfile.getProFeatures().contains( @@ -963,4 +970,4 @@ class RecipientRepository @Inject constructor( ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt index cbf062a599..f9a45ae1fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt @@ -27,7 +27,10 @@ import network.loki.messenger.libsession_util.ED25519 import network.loki.messenger.libsession_util.PRIORITY_HIDDEN import network.loki.messenger.libsession_util.PRIORITY_VISIBLE import network.loki.messenger.libsession_util.protocol.ProFeature +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.protocol.ProProfileFeature import network.loki.messenger.libsession_util.util.BlindKeyAPI +import network.loki.messenger.libsession_util.util.toBitSet import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.file_server.FileServer import org.session.libsession.messaging.file_server.FileServerApis @@ -48,6 +51,11 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase import org.thoughtcrime.securesms.database.RecipientSettingsDatabase import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.home.HomePreferenceKeys +import org.thoughtcrime.securesms.preferences.PreferenceStorage +import org.thoughtcrime.securesms.pro.ProPreferenceKeys +import org.thoughtcrime.securesms.pro.toProMessageFeatures +import org.thoughtcrime.securesms.pro.toProProfileFeatures import org.thoughtcrime.securesms.pro.subscription.SubscriptionManager import org.thoughtcrime.securesms.repository.ConversationRepository import org.thoughtcrime.securesms.tokenpage.TokenPageNotificationManager @@ -62,6 +70,7 @@ class DebugMenuViewModel @AssistedInject constructor( @Assisted private val navigator: UINavigator, @param:ApplicationContext private val context: Context, private val textSecurePreferences: TextSecurePreferences, + private val preferenceStorage: PreferenceStorage, private val tokenPageNotificationManager: TokenPageNotificationManager, private val configFactory: ConfigFactory, private val storage: StorageProtocol, @@ -92,20 +101,23 @@ class DebugMenuViewModel @AssistedInject constructor( showEnvironmentWarningDialog = false, showLoadingDialog = false, showDeprecatedStateWarningDialog = false, - hideMessageRequests = textSecurePreferences.hasHiddenMessageRequests(), + hideMessageRequests = preferenceStorage[HomePreferenceKeys.HAS_HIDDEN_MESSAGE_REQUESTS], hideNoteToSelf = configFactory.withUserConfigs { it.userProfile.getNtsPriority() == PRIORITY_HIDDEN }, forceDeprecationState = deprecationManager.deprecationStateOverride.value, forceDeterministicEncryption = textSecurePreferences.forcesDeterministicAttachmentEncryption, availableDeprecationState = listOf(null) + LegacyGroupDeprecationManager.DeprecationState.entries.toList(), deprecatedTime = deprecationManager.deprecatedTime.value, deprecatingStartTime = deprecationManager.deprecatingStartTime.value, - forceCurrentUserAsPro = textSecurePreferences.forceCurrentUserAsPro(), - forceOtherUsersAsPro = textSecurePreferences.forceOtherUsersAsPro(), - forceIncomingMessagesAsPro = textSecurePreferences.forceIncomingMessagesAsPro(), - forcePostPro = textSecurePreferences.forcePostPro(), + forceCurrentUserAsPro = preferenceStorage[ProPreferenceKeys.FORCE_CURRENT_USER_PRO], + forceOtherUsersAsPro = preferenceStorage[ProPreferenceKeys.FORCE_OTHER_USERS_PRO], + forceIncomingMessagesAsPro = preferenceStorage[ProPreferenceKeys.FORCE_INCOMING_MESSAGE_PRO], + forcePostPro = preferenceStorage[ProPreferenceKeys.FORCE_POST_PRO], forceShortTTl = textSecurePreferences.forcedShortTTL(), debugAvatarReupload = textSecurePreferences.debugAvatarReupload, - messageProFeature = textSecurePreferences.getDebugMessageFeatures(), + messageProFeature = buildSet { + preferenceStorage[ProPreferenceKeys.DEBUG_PRO_MESSAGE_FEATURES].toProMessageFeatures(this) + preferenceStorage[ProPreferenceKeys.DEBUG_PRO_PROFILE_FEATURES].toProProfileFeatures(this) + }, dbInspectorState = DatabaseInspectorState.NOT_AVAILABLE, debugSubscriptionStatuses = setOf( DebugSubscriptionStatus.AUTO_GOOGLE, @@ -118,18 +130,20 @@ class DebugMenuViewModel @AssistedInject constructor( DebugSubscriptionStatus.EXPIRED_APPLE, DebugSubscriptionStatus.AUTO_APPLE_REFUNDING, ), - selectedDebugSubscriptionStatus = textSecurePreferences.getDebugSubscriptionType() ?: DebugSubscriptionStatus.AUTO_GOOGLE, + selectedDebugSubscriptionStatus = preferenceStorage[ProPreferenceKeys.DEBUG_SUBSCRIPTION_STATUS] + ?: DebugSubscriptionStatus.AUTO_GOOGLE, debugProPlanStatus = setOf( DebugProPlanStatus.NORMAL, DebugProPlanStatus.LOADING, DebugProPlanStatus.ERROR, ), - selectedDebugProPlanStatus = textSecurePreferences.getDebugProPlanStatus() ?: DebugProPlanStatus.NORMAL, + selectedDebugProPlanStatus = preferenceStorage[ProPreferenceKeys.DEBUG_PRO_PLAN_STATUS] + ?: DebugProPlanStatus.NORMAL, debugProPlans = subscriptionManagers.asSequence() .flatMap { it.availablePlans.asSequence().map { plan -> DebugProPlan(it, plan) } } .toList(), - forceNoBilling = textSecurePreferences.getDebugForceNoBilling(), - withinQuickRefund = textSecurePreferences.getDebugIsWithinQuickRefund(), + forceNoBilling = preferenceStorage[ProPreferenceKeys.DEBUG_FORCE_NO_BILLING], + withinQuickRefund = preferenceStorage[ProPreferenceKeys.DEBUG_WITHIN_QUICK_REFUND], availableAltFileServers = TEST_FILE_SERVERS, alternativeFileServer = textSecurePreferences.alternativeFileServer, showToastForGroups = getDebugGroupToastPref(), @@ -235,7 +249,7 @@ class DebugMenuViewModel @AssistedInject constructor( } is Commands.HideMessageRequest -> { - textSecurePreferences.setHasHiddenMessageRequests(command.hide) + preferenceStorage[HomePreferenceKeys.HAS_HIDDEN_MESSAGE_REQUESTS] = command.hide _uiState.value = _uiState.value.copy(hideMessageRequests = command.hide) } @@ -309,42 +323,42 @@ class DebugMenuViewModel @AssistedInject constructor( } is Commands.ForceCurrentUserAsPro -> { - textSecurePreferences.setForceCurrentUserAsPro(command.set) + preferenceStorage[ProPreferenceKeys.FORCE_CURRENT_USER_PRO] = command.set _uiState.update { it.copy(forceCurrentUserAsPro = command.set) } } is Commands.ForceOtherUsersAsPro -> { - textSecurePreferences.setForceOtherUsersAsPro(command.set) + preferenceStorage[ProPreferenceKeys.FORCE_OTHER_USERS_PRO] = command.set _uiState.update { it.copy(forceOtherUsersAsPro = command.set) } } is Commands.ForceIncomingMessagesAsPro -> { - textSecurePreferences.setForceIncomingMessagesAsPro(command.set) + preferenceStorage[ProPreferenceKeys.FORCE_INCOMING_MESSAGE_PRO] = command.set _uiState.update { it.copy(forceIncomingMessagesAsPro = command.set) } } is Commands.ForceNoBilling -> { - textSecurePreferences.setDebugForceNoBilling(command.set) + preferenceStorage[ProPreferenceKeys.DEBUG_FORCE_NO_BILLING] = command.set _uiState.update { it.copy(forceNoBilling = command.set) } } is Commands.WithinQuickRefund -> { - textSecurePreferences.setDebugIsWithinQuickRefund(command.set) + preferenceStorage[ProPreferenceKeys.DEBUG_WITHIN_QUICK_REFUND] = command.set _uiState.update { it.copy(withinQuickRefund = command.set) } } is Commands.ForcePostPro -> { - textSecurePreferences.setForcePostPro(command.set) + preferenceStorage[ProPreferenceKeys.FORCE_POST_PRO] = command.set _uiState.update { it.copy(forcePostPro = command.set) } @@ -360,7 +374,10 @@ class DebugMenuViewModel @AssistedInject constructor( is Commands.SetMessageProFeature -> { val features = ArraySet(_uiState.value.messageProFeature) if(command.set) features.add(command.feature) else features.remove(command.feature) - textSecurePreferences.setDebugMessageFeatures(features) + preferenceStorage[ProPreferenceKeys.DEBUG_PRO_MESSAGE_FEATURES] = + features.filterIsInstance().toBitSet().rawValue + preferenceStorage[ProPreferenceKeys.DEBUG_PRO_PROFILE_FEATURES] = + features.filterIsInstance().toBitSet().rawValue _uiState.update { it.copy(messageProFeature = features) } @@ -377,14 +394,14 @@ class DebugMenuViewModel @AssistedInject constructor( } is Commands.SetDebugSubscriptionStatus -> { - textSecurePreferences.setDebugSubscriptionType(command.status) + preferenceStorage[ProPreferenceKeys.DEBUG_SUBSCRIPTION_STATUS] = command.status _uiState.update { it.copy(selectedDebugSubscriptionStatus = command.status) } } is Commands.SetDebugProPlanStatus -> { - textSecurePreferences.setDebugProPlanStatus(command.status) + preferenceStorage[ProPreferenceKeys.DEBUG_PRO_PLAN_STATUS] = command.status _uiState.update { it.copy(selectedDebugProPlanStatus = command.status) } @@ -737,4 +754,4 @@ class DebugMenuViewModel @AssistedInject constructor( val SEEN_3 = "3" val SEEN_4 = "4" } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt index 652c45371a..62e9153a26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt @@ -33,7 +33,6 @@ import org.session.libsession.utilities.ServiceUtil import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY import org.session.libsession.utilities.TextSecurePreferences.Companion.getNotificationPrivacy import org.session.libsession.utilities.TextSecurePreferences.Companion.isNotificationsEnabled -import org.session.libsession.utilities.TextSecurePreferences.Companion.removeHasHiddenMessageRequests import org.session.libsession.utilities.recipients.RecipientData import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.IdPrefix @@ -49,7 +48,9 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.NotifyType +import org.thoughtcrime.securesms.home.HomePreferenceKeys import org.thoughtcrime.securesms.mms.SlideDeck +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.util.AvatarUtils import org.thoughtcrime.securesms.util.SessionMetaProtocol.canUserReplyToNotification @@ -75,6 +76,7 @@ class DefaultMessageNotifier @Inject constructor( private val mmsSmsDatabase: MmsSmsDatabase, private val imageLoader: Provider, private val loginStateRepository: LoginStateRepository, + private val prefs: PreferenceStorage, private val messageFormatter: Lazy, ) : MessageNotifier { override fun setVisibleThread(threadId: Long) { @@ -169,7 +171,7 @@ class DefaultMessageNotifier @Inject constructor( ) == 1 && !(recipient.approved || threadDatabase.getLastSeenAndHasSent(threadId).second()) ) { - removeHasHiddenMessageRequests(context) + prefs.remove(HomePreferenceKeys.HAS_HIDDEN_MESSAGE_REQUESTS) } if (!isNotificationsEnabled(context) || diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/FetchProDetailsWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/FetchProDetailsWorker.kt index 6efe79e888..7725a27954 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/FetchProDetailsWorker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/FetchProDetailsWorker.kt @@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.mapNotNull import org.session.libsession.network.SnodeClock import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.withMutableUserConfigs import org.session.libsession.utilities.withUserConfigs import org.session.libsignal.exceptions.NonRetryableException @@ -28,6 +27,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.api.server.ServerApiExecutor import org.thoughtcrime.securesms.api.server.execute import org.thoughtcrime.securesms.auth.LoginStateRepository +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.pro.api.GetProDetailsApi import org.thoughtcrime.securesms.pro.api.ProDetails import org.thoughtcrime.securesms.pro.api.ServerApiRequest @@ -55,10 +55,10 @@ class FetchProDetailsWorker @AssistedInject constructor( private val loginStateRepository: LoginStateRepository, private val snodeClock: SnodeClock, private val configFactory: ConfigFactoryProtocol, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { - if (!prefs.forcePostPro()) { + if (!prefs[ProPreferenceKeys.FORCE_POST_PRO]) { Log.d(TAG, "Pro details fetch skipped because pro is not enabled") return Result.success() } @@ -186,4 +186,4 @@ class FetchProDetailsWorker @AssistedInject constructor( .await() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt index 5f66b6cda1..5ddb9696f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt @@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import org.session.libsession.network.SnodeClock -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.debugmenu.DebugLogGroup import org.thoughtcrime.securesms.dependencies.ManagerScope +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.pro.api.ProDetails import org.thoughtcrime.securesms.pro.db.ProDatabase import org.thoughtcrime.securesms.util.NetworkConnectivity @@ -31,7 +31,7 @@ class ProDetailsRepository @Inject constructor( private val snodeClock: SnodeClock, @ManagerScope scope: CoroutineScope, loginStateRepository: LoginStateRepository, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val networkConnectivity: NetworkConnectivity, ) { sealed interface LoadState { @@ -89,7 +89,7 @@ class ProDetailsRepository @Inject constructor( * made regardless of the freshness of the last update. */ fun requestRefresh(force: Boolean = false) { - if (!prefs.forcePostPro()) { + if (!prefs[ProPreferenceKeys.FORCE_POST_PRO]) { Log.d(DebugLogGroup.PRO_DATA.label, "Pro hasn't been enabled, skipping refresh") return } @@ -110,4 +110,4 @@ class ProDetailsRepository @Inject constructor( companion object { private const val MIN_UPDATE_INTERVAL_SECONDS = 60L } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProPreferenceKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProPreferenceKeys.kt new file mode 100644 index 0000000000..76148dc1e6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProPreferenceKeys.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.pro + +import network.loki.messenger.BuildConfig +import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel +import org.thoughtcrime.securesms.preferences.PreferenceKey + +object ProPreferenceKeys { + val FORCE_CURRENT_USER_PRO = PreferenceKey.boolean("pref_force_current_user_pro", false) + val FORCE_OTHER_USERS_PRO = PreferenceKey.boolean("pref_force_other_users_pro", false) + val FORCE_INCOMING_MESSAGE_PRO = PreferenceKey.boolean("pref_force_incoming_message_pro", false) + val FORCE_POST_PRO = PreferenceKey.boolean("pref_force_post_pro", BuildConfig.BUILD_TYPE != "release") + + val DEBUG_PRO_MESSAGE_FEATURES = PreferenceKey.long("debug_pro_message_features", 0L) + val DEBUG_PRO_PROFILE_FEATURES = PreferenceKey.long("debug_pro_profile_features", 0L) + val DEBUG_SUBSCRIPTION_STATUS = + PreferenceKey.enum("debug_subscription_status") + val DEBUG_PRO_PLAN_STATUS = + PreferenceKey.enum("debug_pro_plan_status") + val DEBUG_FORCE_NO_BILLING = PreferenceKey.boolean("debug_pro_has_billing", false) + val DEBUG_WITHIN_QUICK_REFUND = PreferenceKey.boolean("debug_within_quick_refund", false) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt index a5e1a98820..f4ec406f9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt @@ -9,8 +9,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest @@ -27,7 +25,6 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest -import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withTimeoutOrNull @@ -46,7 +43,6 @@ import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.network.SnodeClock import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.userConfigsChanged @@ -63,6 +59,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.debugmenu.DebugLogGroup import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel import org.thoughtcrime.securesms.dependencies.ManagerScope +import org.thoughtcrime.securesms.preferences.PreferenceStorage import org.thoughtcrime.securesms.pro.api.AddPaymentErrorStatus import org.thoughtcrime.securesms.pro.api.AddProPaymentApi import org.thoughtcrime.securesms.pro.api.ProApiResponse @@ -84,7 +81,7 @@ import kotlin.time.Duration.Companion.milliseconds @Singleton class ProStatusManager @Inject constructor( private val application: Application, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, @param:ManagerScope private val scope: CoroutineScope, private val serverApiExecutor: ServerApiExecutor, private val addProPaymentApiFactory: AddProPaymentApi.Factory, @@ -108,15 +105,9 @@ class ProStatusManager @Inject constructor( } .distinctUntilChanged(), proDetailsRepository.get().loadState, - (TextSecurePreferences.events.filter { it == TextSecurePreferences.DEBUG_SUBSCRIPTION_STATUS } as Flow<*>) - .onStart { emit(Unit) } - .map { prefs.getDebugSubscriptionType() }, - (TextSecurePreferences.events.filter { it == TextSecurePreferences.DEBUG_PRO_PLAN_STATUS } as Flow<*>) - .onStart { emit(Unit) } - .map { prefs.getDebugProPlanStatus() }, - (TextSecurePreferences.events.filter { it == TextSecurePreferences.SET_FORCE_CURRENT_USER_PRO } as Flow<*>) - .onStart { emit(Unit) } - .map { prefs.forceCurrentUserAsPro() }, + prefs.watch(scope, ProPreferenceKeys.DEBUG_SUBSCRIPTION_STATUS), + prefs.watch(scope, ProPreferenceKeys.DEBUG_PRO_PLAN_STATUS), + prefs.watch(scope, ProPreferenceKeys.FORCE_CURRENT_USER_PRO), ){ showProBadgePreference, proDetailsState, debugSubscription, debugProPlanStatus, forceCurrentUserAsPro -> val proDataRefreshState = when(debugProPlanStatus){ @@ -225,17 +216,7 @@ class ProStatusManager @Inject constructor( initialValue = getDefaultSubscriptionStateData() ) - private val _postProLaunchStatus = MutableStateFlow(isPostPro()) - val postProLaunchStatus: StateFlow = _postProLaunchStatus - - - init { - scope.launch { - prefs.watchPostProStatus().collect { - _postProLaunchStatus.update { isPostPro() } - } - } - } + val postProLaunchStatus: StateFlow = prefs.watch(scope, ProPreferenceKeys.FORCE_POST_PRO) override suspend fun doWhileLoggedIn(loggedInState: LoggedInState): Unit = supervisorScope { launch { @@ -412,7 +393,7 @@ class ProStatusManager @Inject constructor( fun getIncomingMessageMaxLength(message: VisibleMessage): Int { // if the debug is set, return that // of if we are in pre-pro world - if (prefs.forceIncomingMessagesAsPro() || !isPostPro()) return MAX_CHARACTER_PRO + if (prefs[ProPreferenceKeys.FORCE_INCOMING_MESSAGE_PRO] || !isPostPro()) return MAX_CHARACTER_PRO if (message.proFeatures.contains(ProMessageFeature.HIGHER_CHARACTER_LIMIT)) { return MAX_CHARACTER_PRO @@ -423,7 +404,7 @@ class ProStatusManager @Inject constructor( // Temporary method and concept that we should remove once Pro is out fun isPostPro(): Boolean { - return prefs.forcePostPro() + return postProLaunchStatus.value } fun getCharacterLimit(isPro: Boolean): Int { @@ -441,8 +422,11 @@ class ProStatusManager @Inject constructor( */ fun getMessageProFeatures(message: MessageRecord): Set { // use debug values if any - if(prefs.forceIncomingMessagesAsPro()){ - return prefs.getDebugMessageFeatures() + if(prefs[ProPreferenceKeys.FORCE_INCOMING_MESSAGE_PRO]){ + return buildSet { + prefs[ProPreferenceKeys.DEBUG_PRO_MESSAGE_FEATURES].toProMessageFeatures(this) + prefs[ProPreferenceKeys.DEBUG_PRO_PROFILE_FEATURES].toProProfileFeatures(this) + } } return message.proFeatures @@ -576,4 +560,4 @@ class ProStatusManager @Inject constructor( const val DEFAULT_GOOGLE_STORE = "Google Play Store" const val DEFAULT_APPLE_STORE = "Apple App Store" } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/DefaultConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/DefaultConversationRepository.kt index d772ade0d5..41e16b4946 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/DefaultConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/DefaultConversationRepository.kt @@ -33,7 +33,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.network.SnodeClock import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.toAddress -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.isGroupV2 import org.session.libsession.utilities.isLegacyGroup @@ -63,6 +62,8 @@ import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.preferences.PreferenceStorage +import org.thoughtcrime.securesms.pro.ProPreferenceKeys import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.util.castAwayType import java.util.EnumSet @@ -88,6 +89,7 @@ class DefaultConversationRepository @Inject constructor( private val recipientRepository: RecipientRepository, private val messageSender: MessageSender, private val loginStateRepository: LoginStateRepository, + private val prefs: PreferenceStorage, private val proStatusManager: ProStatusManager, private val swarmApiExecutor: SwarmApiExecutor, private val communityApiExecutor: CommunityApiExecutor, @@ -174,10 +176,10 @@ class DefaultConversationRepository @Inject constructor( communityDatabase.changeNotification.filter { it in allAddresses }, threadDb.updateNotifications, // If pro status pref changes, the convo is likely needing changes too - TextSecurePreferences.Companion.events.filter { - it == TextSecurePreferences.Companion.SET_FORCE_OTHER_USERS_PRO || - it == TextSecurePreferences.Companion.SET_FORCE_CURRENT_USER_PRO - it == TextSecurePreferences.Companion.SET_FORCE_POST_PRO + prefs.changes().filter { + it.name == ProPreferenceKeys.FORCE_OTHER_USERS_PRO.name || + it.name == ProPreferenceKeys.FORCE_CURRENT_USER_PRO.name || + it.name == ProPreferenceKeys.FORCE_POST_PRO.name } ).debounce(500) .onStart { emit(allAddresses) } @@ -641,4 +643,4 @@ class DefaultConversationRepository @Inject constructor( override fun getInvitingAdmin(threadId: Long): Address? { return lokiMessageDb.groupInviteReferrer(threadId)?.let(Address.Companion::fromSerialized) } -} \ No newline at end of file +} diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/pro/subscription/PlayStoreSubscriptionManager.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/pro/subscription/PlayStoreSubscriptionManager.kt index 3b1e0ae088..3da0d18896 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/pro/subscription/PlayStoreSubscriptionManager.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/pro/subscription/PlayStoreSubscriptionManager.kt @@ -16,23 +16,20 @@ import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.debugmenu.DebugLogGroup import org.thoughtcrime.securesms.dependencies.ManagerScope +import org.thoughtcrime.securesms.preferences.PreferenceStorage +import org.thoughtcrime.securesms.pro.ProPreferenceKeys import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.util.CurrentActivityObserver import javax.inject.Inject @@ -45,7 +42,7 @@ import javax.inject.Singleton class PlayStoreSubscriptionManager @Inject constructor( private val application: Application, private val currentActivityObserver: CurrentActivityObserver, - private val prefs: TextSecurePreferences, + private val prefs: PreferenceStorage, private val loginStateRepository: LoginStateRepository, proStatusManager: ProStatusManager, @param:ManagerScope scope: CoroutineScope, @@ -61,9 +58,7 @@ class PlayStoreSubscriptionManager @Inject constructor( // generic billing support method. Uses the property above and also checks the debug pref override val supportsBilling: StateFlow = combine( _playBillingAvailable, - (TextSecurePreferences.events.filter { it == TextSecurePreferences.DEBUG_FORCE_NO_BILLING } as Flow<*>) - .onStart { emit(Unit) } - .map { prefs.getDebugForceNoBilling() }, + prefs.watch(scope, ProPreferenceKeys.DEBUG_FORCE_NO_BILLING), ){ available, forceNoBilling -> !forceNoBilling && available } @@ -280,7 +275,7 @@ class PlayStoreSubscriptionManager @Inject constructor( override suspend fun hasValidSubscription(): Boolean { // if in debug mode, always return true - return if(prefs.forceCurrentUserAsPro()) true + return if(prefs[ProPreferenceKeys.FORCE_CURRENT_USER_PRO]) true else getExistingSubscription() != null } @@ -328,4 +323,4 @@ class PlayStoreSubscriptionManager @Inject constructor( companion object { private const val TAG = "PlayStoreSubscriptionManager" } -} \ No newline at end of file +} From d5920fa2c95673b5146dc2632c39ff148ac8ce2a Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Fri, 27 Feb 2026 09:52:24 +1100 Subject: [PATCH 4/4] Fix unit tests for PreferenceStorage migration --- .../preferences/TestPreferenceStorage.kt | 56 ++++++++++ .../reviews/InAppReviewManagerTest.kt | 14 +-- .../securesms/util/DateUtilsTimeSpanTests.kt | 102 ++++++------------ 3 files changed, 89 insertions(+), 83 deletions(-) create mode 100644 app/src/test/java/org/thoughtcrime/securesms/preferences/TestPreferenceStorage.kt diff --git a/app/src/test/java/org/thoughtcrime/securesms/preferences/TestPreferenceStorage.kt b/app/src/test/java/org/thoughtcrime/securesms/preferences/TestPreferenceStorage.kt new file mode 100644 index 0000000000..79435f987e --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/preferences/TestPreferenceStorage.kt @@ -0,0 +1,56 @@ +package org.thoughtcrime.securesms.preferences + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class TestPreferenceStorage : PreferenceStorage { + private val preferenceStates = mutableMapOf>() + private val changes = MutableSharedFlow>(extraBufferCapacity = 32) + + override fun set(key: PreferenceKey, value: T) { + stateFor(key).value = value + changes.tryEmit(key) + } + + override fun remove(key: PreferenceKey<*>) { + stateForUntyped(key).value = defaultValueFor(key) + changes.tryEmit(key) + } + + override fun get(key: PreferenceKey): T { + return stateFor(key).value + } + + override fun changes(): Flow> = changes + + override fun watch(scope: CoroutineScope, key: PreferenceKey): StateFlow { + return stateFor(key) + } + + @Suppress("UNCHECKED_CAST") + private fun stateFor(key: PreferenceKey): MutableStateFlow { + return stateForUntyped(key) as MutableStateFlow + } + + private fun stateForUntyped(key: PreferenceKey<*>): MutableStateFlow { + return preferenceStates.getOrPut(key.name) { + MutableStateFlow(defaultValueFor(key)) + } + } + + private fun defaultValueFor(key: PreferenceKey<*>): Any? { + return when (val strategy = key.strategy) { + is PreferenceKey.Strategy.PrimitiveInt -> strategy.defaultValue + is PreferenceKey.Strategy.PrimitiveLong -> strategy.defaultValue + is PreferenceKey.Strategy.PrimitiveFloat -> strategy.defaultValue + is PreferenceKey.Strategy.PrimitiveBoolean -> strategy.defaultValue + is PreferenceKey.Strategy.PrimitiveString -> strategy.defaultValue + is PreferenceKey.Strategy.Enum<*> -> strategy.defaultValue + is PreferenceKey.Strategy.Json<*> -> null + is PreferenceKey.Strategy.Bytes -> null + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/reviews/InAppReviewManagerTest.kt b/app/src/test/java/org/thoughtcrime/securesms/reviews/InAppReviewManagerTest.kt index 7fb7639772..3016644e2e 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/reviews/InAppReviewManagerTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/reviews/InAppReviewManagerTest.kt @@ -6,8 +6,6 @@ import app.cash.turbine.test import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.plus import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Rule @@ -15,9 +13,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.kotlin.any -import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.thoughtcrime.securesms.preferences.TestPreferenceStorage import org.thoughtcrime.securesms.util.MockLoggingRule import java.util.EnumSet @@ -180,20 +178,12 @@ fun TestScope.createManager( } } - var reviewState: String? = null - return InAppReviewManager( context = mock { on { packageManager } doReturn pm on { packageName } doReturn "mypackage.name" }, - prefs = mock { - on { inAppReviewState } doAnswer { reviewState } - on { inAppReviewState = any() } doAnswer { reviewState = it.arguments[0] as? String } - }, - json = Json { - serializersModule += ReviewsSerializerModule().provideReviewsSerializersModule() - }, + prefs = TestPreferenceStorage(), storeReviewManager = mock { on { supportsReviewFlow } doReturn supportInAppReviewFlow }, diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/DateUtilsTimeSpanTests.kt b/app/src/test/java/org/thoughtcrime/securesms/util/DateUtilsTimeSpanTests.kt index 185f1b686a..a431730e90 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/util/DateUtilsTimeSpanTests.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/util/DateUtilsTimeSpanTests.kt @@ -6,12 +6,10 @@ import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.* import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.TextSecurePreferences.Companion.DATE_FORMAT_PREF -import org.session.libsession.utilities.TextSecurePreferences.Companion.TIME_FORMAT_PREF +import org.thoughtcrime.securesms.preferences.PreferenceStorage +import org.thoughtcrime.securesms.preferences.TestPreferenceStorage import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime @@ -29,7 +27,7 @@ import java.util.concurrent.TimeUnit class DateUtilsTest { private lateinit var context: Context - private lateinit var preferences: TextSecurePreferences + private lateinit var preferences: PreferenceStorage private lateinit var dateUtils: DateUtils // Fixed test timestamps @@ -46,17 +44,10 @@ class DateUtilsTest { // Get Robolectric context - this will be a real implementation, not a mock context = org.robolectric.RuntimeEnvironment.getApplication() - // Mock preferences with explicit system behavior handling - preferences = mock(TextSecurePreferences::class.java) - - // Mock the date format preference - `when`(preferences.getStringPreference(DATE_FORMAT_PREF, "dd/MM/yyyy")) - .thenReturn("dd/MM/yyyy") - - // For time format, we need to account for the system's 24-hour format setting - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(preferences.getStringPreference(TIME_FORMAT_PREF, systemDefault)) - .thenReturn("HH:mm") // Force 24-hour format for consistent testing + preferences = TestPreferenceStorage().apply { + this[DateTimePreferenceKeys.DATE_FORMAT] = "dd/MM/yyyy" + this[DateTimePreferenceKeys.TIME_FORMAT] = "HH:mm" // Force 24-hour for consistent testing + } // Create the real DateUtils with a Robolectric context dateUtils = DateUtils(context, preferences) @@ -67,13 +58,10 @@ class DateUtilsTest { assertEquals("dd/MM/yyyy", dateUtils.getDateFormat()) // Test with a different preference value - val newPreferences = mock(TextSecurePreferences::class.java) - `when`(newPreferences.getStringPreference(DATE_FORMAT_PREF, "dd/MM/yyyy")) - .thenReturn("yyyy-MM-dd") - - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(newPreferences.getStringPreference(TIME_FORMAT_PREF, systemDefault)) - .thenReturn("HH:mm") + val newPreferences = TestPreferenceStorage().apply { + this[DateTimePreferenceKeys.DATE_FORMAT] = "yyyy-MM-dd" + this[DateTimePreferenceKeys.TIME_FORMAT] = "HH:mm" + } val newDateUtils = DateUtils(context, newPreferences) assertEquals("yyyy-MM-dd", newDateUtils.getDateFormat()) @@ -84,13 +72,10 @@ class DateUtilsTest { assertEquals("HH:mm", dateUtils.getTimeFormat()) // Test with a different preference value - val newPreferences = mock(TextSecurePreferences::class.java) - `when`(newPreferences.getStringPreference(DATE_FORMAT_PREF, "dd/MM/yyyy")) - .thenReturn("dd/MM/yyyy") - - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(newPreferences.getStringPreference(TIME_FORMAT_PREF, systemDefault)) - .thenReturn("h:mm") + val newPreferences = TestPreferenceStorage().apply { + this[DateTimePreferenceKeys.DATE_FORMAT] = "dd/MM/yyyy" + this[DateTimePreferenceKeys.TIME_FORMAT] = "h:mm" + } val newDateUtils = DateUtils(context, newPreferences) assertEquals("h:mm", newDateUtils.getTimeFormat()) @@ -138,8 +123,6 @@ class DateUtilsTest { @Test fun `test updatePreferredDateFormat with valid format`() { - val captor = org.mockito.ArgumentCaptor.forClass(String::class.java) - // First, get the list of valid patterns to ensure we use one that exists val validPatterns = dateUtils.getUiPrintableDatePatterns() println("Available date patterns: $validPatterns") @@ -158,14 +141,11 @@ class DateUtilsTest { dateUtils.updatePreferredDateFormat(validFormat) - verify(preferences).setStringPreference(eq(DATE_FORMAT_PREF), captor.capture()) - assertEquals(validFormat, captor.value) + assertEquals(validFormat, preferences[DateTimePreferenceKeys.DATE_FORMAT]) } @Test fun `test updatePreferredDateFormat validates against actual valid patterns`() { - val captor = org.mockito.ArgumentCaptor.forClass(String::class.java) - // Get the actual valid patterns from the DateUtils implementation val validPatterns = dateUtils.getUiPrintableDatePatterns() @@ -183,51 +163,38 @@ class DateUtilsTest { dateUtils.updatePreferredDateFormat(testPattern) - verify(preferences).setStringPreference(eq(DATE_FORMAT_PREF), captor.capture()) + val storedValue = preferences[DateTimePreferenceKeys.DATE_FORMAT] // The captured value should be the test pattern if it was valid, // or the default if it wasn't assertTrue("Expected either the test pattern or default pattern", - captor.value == testPattern || captor.value == "dd/MM/yyyy") + storedValue == testPattern || storedValue == "dd/MM/yyyy") // If it's not the test pattern, it should be because the pattern wasn't actually valid - if (captor.value != testPattern) { + if (storedValue != testPattern) { println("Pattern '$testPattern' was not accepted, fell back to default") } } @Test fun `test updatePreferredDateFormat with invalid format falls back to default`() { - val captor = org.mockito.ArgumentCaptor.forClass(String::class.java) - // Use an invalid format dateUtils.updatePreferredDateFormat("invalid-format") - verify(preferences).setStringPreference(eq(DATE_FORMAT_PREF), captor.capture()) - assertEquals("dd/MM/yyyy", captor.value) // Should fall back to default + assertEquals("dd/MM/yyyy", preferences[DateTimePreferenceKeys.DATE_FORMAT]) // Should fall back to default } @Test fun `test updatePreferredTimeFormat updates preference`() { - val captor = org.mockito.ArgumentCaptor.forClass(String::class.java) - // Update with valid format dateUtils.updatePreferredTimeFormat("h:mm") - verify(preferences).setStringPreference(eq(TIME_FORMAT_PREF), captor.capture()) - assertEquals("h:mm", captor.value) - - // Reset for next test - reset(preferences) - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(preferences.getStringPreference(TIME_FORMAT_PREF, systemDefault)) - .thenReturn("HH:mm") + assertEquals("h:mm", preferences[DateTimePreferenceKeys.TIME_FORMAT]) // Test with invalid format dateUtils.updatePreferredTimeFormat("invalid-format") - verify(preferences).setStringPreference(eq(TIME_FORMAT_PREF), captor.capture()) - assertEquals("HH:mm", captor.value) // Should default to 24-hour format + assertEquals("HH:mm", preferences[DateTimePreferenceKeys.TIME_FORMAT]) // Should default to 24-hour format } @Test @@ -257,7 +224,7 @@ class DateUtilsTest { class DateUtilsTimeSpanTest { private lateinit var context: Context - private lateinit var preferences: TextSecurePreferences + private lateinit var preferences: PreferenceStorage private lateinit var dateUtils: DateUtils // Fixed point in time to base all tests on - April 15, 2023 at 12:00 UTC @@ -271,14 +238,10 @@ class DateUtilsTimeSpanTest { // Get Robolectric context context = org.robolectric.RuntimeEnvironment.getApplication() - // Mock preferences accounting for system 24-hour format - preferences = mock(TextSecurePreferences::class.java) - `when`(preferences.getStringPreference(DATE_FORMAT_PREF, "dd/MM/yyyy")) - .thenReturn("dd/MM/yyyy") - - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(preferences.getStringPreference(TIME_FORMAT_PREF, systemDefault)) - .thenReturn("HH:mm") // Force 24-hour for consistent testing + preferences = TestPreferenceStorage().apply { + this[DateTimePreferenceKeys.DATE_FORMAT] = "dd/MM/yyyy" + this[DateTimePreferenceKeys.TIME_FORMAT] = "HH:mm" // Force 24-hour for consistent testing + } dateUtils = DateUtils(context, preferences) } @@ -322,12 +285,9 @@ class DateUtilsTimeSpanTest { @Test fun `getDisplayFormattedTimeSpanString adapts based on preferred time format`() { // Create preferences with 12-hour time format - val twelveHourPrefs = mock(TextSecurePreferences::class.java).apply { - `when`(getStringPreference(DATE_FORMAT_PREF, "dd/MM/yyyy")).thenReturn("dd/MM/yyyy") - - // Mock both possible system defaults to ensure we get what we want - val systemDefault = if (DateFormat.is24HourFormat(context)) "HH:mm" else "h:mm a" - `when`(getStringPreference(TIME_FORMAT_PREF, systemDefault)).thenReturn("h:mm") + val twelveHourPrefs = TestPreferenceStorage().apply { + this[DateTimePreferenceKeys.DATE_FORMAT] = "dd/MM/yyyy" + this[DateTimePreferenceKeys.TIME_FORMAT] = "h:mm" } val twelveHourDateUtils = DateUtils(context, twelveHourPrefs) @@ -356,4 +316,4 @@ class DateUtilsTimeSpanTest { .toInstant() .toEpochMilli() } -} \ No newline at end of file +}