From 3e1e702b84d5c0caa839e579dcebfaa835c5c53e Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:58:07 +1100 Subject: [PATCH 1/2] Improve lifecycle handling of restoring account and a few dependency injection optimisations --- .../securesms/configs/ConfigToDatabaseSync.kt | 1 - .../loadaccount/LoadAccountActivity.kt | 9 +- .../manager/CreateAccountManager.kt | 41 ++-- .../onboarding/manager/LoadAccountManager.kt | 23 +- .../MessageNotificationsViewModel.kt | 3 +- .../securesms/pro/ProDetailsRepository.kt | 46 ++-- .../securesms/pro/ProStatusManager.kt | 204 +++++++++--------- .../securesms/webrtc/CallMessageProcessor.kt | 5 +- 8 files changed, 161 insertions(+), 171 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt b/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt index e0c5e46541..bec825c725 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt @@ -80,7 +80,6 @@ class ConfigToDatabaseSync @Inject constructor( private val lokiAPIDatabase: LokiAPIDatabase, private val receivedMessageHashDatabase: ReceivedMessageHashDatabase, private val clock: SnodeClock, - private val preferences: TextSecurePreferences, private val conversationRepository: ConversationRepository, private val mmsSmsDatabase: MmsSmsDatabase, private val lokiMessageDatabase: LokiMessageDatabase, 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 ede53b87af..bbb688785e 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 @@ -5,8 +5,11 @@ import android.view.View import androidx.activity.viewModels import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle 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 @@ -44,9 +47,11 @@ class LoadAccountActivity : BaseActionBarActivity() { supportActionBar?.setTitle(R.string.loadAccount) prefs.setConfigurationMessageSynced(false) + lifecycleScope.launch { - viewModel.events.collect { - loadAccountManager.load(it.mnemonic) + repeatOnLifecycle(Lifecycle.State.STARTED) { + val mnemonic = viewModel.events.first().mnemonic + loadAccountManager.load(mnemonic) start() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt index 6e6dd857de..32faab0652 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt @@ -1,47 +1,44 @@ package org.thoughtcrime.securesms.onboarding.manager -import android.app.Application +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import network.loki.messenger.libsession_util.PRIORITY_HIDDEN -import org.session.libsession.snode.SnodeModule import org.session.libsession.utilities.ConfigFactoryProtocol -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.thoughtcrime.securesms.auth.LoggedInState import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase import org.thoughtcrime.securesms.util.VersionDataFetcher import javax.inject.Inject -import javax.inject.Singleton -@Singleton class CreateAccountManager @Inject constructor( private val versionDataFetcher: VersionDataFetcher, private val configFactory: ConfigFactoryProtocol, private val receivedMessageHashDatabase: ReceivedMessageHashDatabase, private val loginStateRepository: LoginStateRepository, + private val database: LokiAPIDatabaseProtocol, ) { - private val database: LokiAPIDatabaseProtocol - get() = SnodeModule.shared.storage + suspend fun createAccount(displayName: String) { + withContext(Dispatchers.Default) { + // This is here to resolve a case where the app restarts before a user completes onboarding + // which can result in an invalid database state + database.clearAllLastMessageHashes() + receivedMessageHashDatabase.removeAll() - fun createAccount(displayName: String) { - // This is here to resolve a case where the app restarts before a user completes onboarding - // which can result in an invalid database state - database.clearAllLastMessageHashes() - receivedMessageHashDatabase.removeAll() + loginStateRepository.update { oldState -> + require(oldState == null) { + "Attempting to create a new account when one already exists!" + } - loginStateRepository.update { oldState -> - require(oldState == null) { - "Attempting to create a new account when one already exists!" + LoggedInState.generate(seed = null) } - LoggedInState.generate(seed = null) - } + configFactory.withMutableUserConfigs { + it.userProfile.setName(displayName) + it.userProfile.setNtsPriority(PRIORITY_HIDDEN) + } - configFactory.withMutableUserConfigs { - it.userProfile.setName(displayName) - it.userProfile.setNtsPriority(PRIORITY_HIDDEN) + versionDataFetcher.startTimedVersionCheck() } - - versionDataFetcher.startTimedVersionCheck() } } \ No newline at end of file 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 d1a033182b..0d69f556fd 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 @@ -1,11 +1,7 @@ package org.thoughtcrime.securesms.onboarding.manager -import android.content.Context -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import org.session.libsession.snode.SnodeModule +import kotlinx.coroutines.withContext import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.thoughtcrime.securesms.auth.LoggedInState @@ -13,28 +9,17 @@ import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase import org.thoughtcrime.securesms.util.VersionDataFetcher import javax.inject.Inject -import javax.inject.Singleton -@Singleton class LoadAccountManager @Inject constructor( - @param:dagger.hilt.android.qualifiers.ApplicationContext private val context: Context, private val prefs: TextSecurePreferences, private val versionDataFetcher: VersionDataFetcher, private val receivedMessageHashDatabase: ReceivedMessageHashDatabase, private val loginStateRepository: LoginStateRepository, -) { private val database: LokiAPIDatabaseProtocol - get() = SnodeModule.shared.storage - - private var restoreJob: Job? = null - - private val scope = CoroutineScope(Dispatchers.IO) - - fun load(seed: ByteArray) { - // only have one sync job running at a time (prevent QR from trying to spawn a new job) - if (restoreJob?.isActive == true) return +) { - restoreJob = scope.launch { + suspend fun load(seed: ByteArray) { + withContext(Dispatchers.Default) { // This is here to resolve a case where the app restarts before a user completes onboarding // which can result in an invalid database state database.clearAllLastMessageHashes() 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 8ea92dffc1..da904db003 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 @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -37,7 +36,7 @@ internal class MessageNotificationsViewModel( } fun onContinue() { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch { if (state is State.CreateAccount) createAccountManager.createAccount(state.displayName) prefs.setPushEnabled(uiStates.value.pushEnabled) 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 947a260654..c674d04cbc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import org.session.libsession.snode.SnodeClock 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.pro.api.ProDetails @@ -27,6 +28,7 @@ class ProDetailsRepository @Inject constructor( private val db: ProDatabase, private val snodeClock: SnodeClock, @ManagerScope scope: CoroutineScope, + loginStateRepository: LoginStateRepository, ) { sealed interface LoadState { val lastUpdated: Pair? @@ -46,31 +48,33 @@ class ProDetailsRepository @Inject constructor( } - val loadState: StateFlow = combine( - FetchProDetailsWorker.watch(application) - .map { it.state } - .distinctUntilChanged(), + val loadState: StateFlow = loginStateRepository.flowWithLoggedInState { + combine( + FetchProDetailsWorker.watch(application) + .map { it.state } + .distinctUntilChanged(), - db.proDetailsChangeNotification - .onStart { emit(Unit) } - .map { db.getProDetailsAndLastUpdated() } - ) { state, last -> - when (state) { - WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true) - WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false) - WorkInfo.State.SUCCEEDED -> { - if (last != null) { - Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend") - LoadState.Loaded(last) - } else { - // This should never happen, but just in case... - LoadState.Error(null) + db.proDetailsChangeNotification + .onStart { emit(Unit) } + .map { db.getProDetailsAndLastUpdated() } + ) { state, last -> + when (state) { + WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true) + WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false) + WorkInfo.State.SUCCEEDED -> { + if (last != null) { + Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend") + LoadState.Loaded(last) + } else { + // This should never happen, but just in case... + LoadState.Error(null) + } } - } - WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last) + WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last) + } } - }.stateIn(scope, SharingStarted.Eagerly, LoadState.Init) + } .stateIn(scope, SharingStarted.Eagerly, LoadState.Init) /** 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 a1720da497..6ced57906e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt @@ -51,7 +51,6 @@ import org.thoughtcrime.securesms.database.RecipientRepository import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.debugmenu.DebugLogGroup import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel -import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.ManagerScope import org.thoughtcrime.securesms.dependencies.OnAppStartupComponent import org.thoughtcrime.securesms.pro.api.AddPaymentErrorStatus @@ -79,118 +78,119 @@ class ProStatusManager @Inject constructor( private val loginState: LoginStateRepository, private val proDatabase: ProDatabase, private val snodeClock: SnodeClock, - private val proDetailsRepository: ProDetailsRepository, + private val proDetailsRepository: Lazy, private val configFactory: Lazy, ) : OnAppStartupComponent { - val proDataState: StateFlow = combine( - recipientRepository.observeSelf().map { it.shouldShowProBadge }.distinctUntilChanged(), - proDetailsRepository.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() }, - ){ shouldShowProBadge, proDetailsState, debugSubscription, debugProPlanStatus, forceCurrentUserAsPro -> - val proDataRefreshState = when(debugProPlanStatus){ - DebugMenuViewModel.DebugProPlanStatus.LOADING -> State.Loading - DebugMenuViewModel.DebugProPlanStatus.ERROR -> State.Error(Exception()) - else -> { - // calculate the real refresh state here - when(proDetailsState){ - is ProDetailsRepository.LoadState.Loading -> State.Loading - is ProDetailsRepository.LoadState.Error -> State.Error(Exception()) - else -> State.Success(Unit) + val proDataState: StateFlow = loginState.flowWithLoggedInState { + combine( + recipientRepository.observeSelf().map { it.shouldShowProBadge }.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() }, + ){ shouldShowProBadge, proDetailsState, debugSubscription, debugProPlanStatus, forceCurrentUserAsPro -> + val proDataRefreshState = when(debugProPlanStatus){ + DebugMenuViewModel.DebugProPlanStatus.LOADING -> State.Loading + DebugMenuViewModel.DebugProPlanStatus.ERROR -> State.Error(Exception()) + else -> { + // calculate the real refresh state here + when(proDetailsState){ + is ProDetailsRepository.LoadState.Loading -> State.Loading + is ProDetailsRepository.LoadState.Error -> State.Error(Exception()) + else -> State.Success(Unit) + } } } - } - if(!forceCurrentUserAsPro){ - Log.d(DebugLogGroup.PRO_DATA.label, "ProStatusManager: Getting REAL Pro data state") - - ProDataState( - type = proDetailsState.lastUpdated?.first?.toProStatus() ?: ProStatus.NeverSubscribed, - showProBadge = shouldShowProBadge, - refreshState = proDataRefreshState - ) - }// debug data - else { - Log.d(DebugLogGroup.PRO_DATA.label, "ProStatusManager: Getting DEBUG Pro data state") - val subscriptionState = debugSubscription ?: DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE - - ProDataState( - type = when(subscriptionState){ - DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE -> ProStatus.Active.AutoRenewing( - validUntil = Instant.now() + Duration.ofDays(14), - duration = ProSubscriptionDuration.THREE_MONTHS, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = false - ) + if(!forceCurrentUserAsPro){ + Log.d(DebugLogGroup.PRO_DATA.label, "ProStatusManager: Getting REAL Pro data state") + + ProDataState( + type = proDetailsState.lastUpdated?.first?.toProStatus() ?: ProStatus.NeverSubscribed, + showProBadge = shouldShowProBadge, + refreshState = proDataRefreshState + ) + }// debug data + else { + Log.d(DebugLogGroup.PRO_DATA.label, "ProStatusManager: Getting DEBUG Pro data state") + val subscriptionState = debugSubscription ?: DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE + + ProDataState( + type = when(subscriptionState){ + DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE -> ProStatus.Active.AutoRenewing( + validUntil = Instant.now() + Duration.ofDays(14), + duration = ProSubscriptionDuration.THREE_MONTHS, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) - DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE_REFUNDING -> ProStatus.Active.AutoRenewing( - validUntil = Instant.now() + Duration.ofDays(14), - duration = ProSubscriptionDuration.THREE_MONTHS, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = true - ) + DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE_REFUNDING -> ProStatus.Active.AutoRenewing( + validUntil = Instant.now() + Duration.ofDays(14), + duration = ProSubscriptionDuration.THREE_MONTHS, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = true + ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE -> ProStatus.Active.Expiring( - validUntil = Instant.now() + Duration.ofDays(2), - duration = ProSubscriptionDuration.TWELVE_MONTHS, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = false - ) + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE -> ProStatus.Active.Expiring( + validUntil = Instant.now() + Duration.ofDays(2), + duration = ProSubscriptionDuration.TWELVE_MONTHS, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE_LATER -> ProStatus.Active.Expiring( - validUntil = Instant.now() + Duration.ofDays(40), - duration = ProSubscriptionDuration.TWELVE_MONTHS, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = false - ) + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE_LATER -> ProStatus.Active.Expiring( + validUntil = Instant.now() + Duration.ofDays(40), + duration = ProSubscriptionDuration.TWELVE_MONTHS, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) - DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE -> ProStatus.Active.AutoRenewing( - validUntil = Instant.now() + Duration.ofDays(14), - duration = ProSubscriptionDuration.ONE_MONTH, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = false - ) + DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE -> ProStatus.Active.AutoRenewing( + validUntil = Instant.now() + Duration.ofDays(14), + duration = ProSubscriptionDuration.ONE_MONTH, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_APPLE -> ProStatus.Active.Expiring( - validUntil = Instant.now() + Duration.ofDays(2), - duration = ProSubscriptionDuration.ONE_MONTH, - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7), - refundInProgress = false - ) + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_APPLE -> ProStatus.Active.Expiring( + validUntil = Instant.now() + Duration.ofDays(2), + duration = ProSubscriptionDuration.ONE_MONTH, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED -> ProStatus.Expired( - expiredAt = Instant.now() - Duration.ofDays(14), - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!! - ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED_EARLIER -> ProStatus.Expired( - expiredAt = Instant.now() - Duration.ofDays(60), - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!! - ) - DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED_APPLE -> ProStatus.Expired( - expiredAt = Instant.now() - Duration.ofDays(14), - providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!! - ) - }, + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED -> ProStatus.Expired( + expiredAt = Instant.now() - Duration.ofDays(14), + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!! + ) + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED_EARLIER -> ProStatus.Expired( + expiredAt = Instant.now() - Duration.ofDays(60), + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!! + ) + DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED_APPLE -> ProStatus.Expired( + expiredAt = Instant.now() - Duration.ofDays(14), + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!! + ) + }, - refreshState = proDataRefreshState, - showProBadge = shouldShowProBadge, - ) + refreshState = proDataRefreshState, + showProBadge = shouldShowProBadge, + ) + } } - }.stateIn(scope, SharingStarted.Eagerly, initialValue = getDefaultSubscriptionStateData() ) @@ -278,7 +278,7 @@ class ProStatusManager @Inject constructor( .distinctUntilChanged() .map { "ProAccessExpiry in config changes" }, - proDetailsRepository.loadState + proDetailsRepository.get().loadState .mapNotNull { it.lastUpdated?.first?.expiry } .distinctUntilChanged() .mapLatest { expiry -> @@ -325,7 +325,7 @@ class ProStatusManager @Inject constructor( "Scheduling ProDetails fetch due to: $refreshReason" ) - proDetailsRepository.requestRefresh() + proDetailsRepository.get().requestRefresh() } } else { FetchProDetailsWorker.cancel(application) @@ -474,7 +474,7 @@ class ProStatusManager @Inject constructor( configs.userProfile.setProBadge(true) } // refresh the pro details - proDetailsRepository.requestRefresh() + proDetailsRepository.get().requestRefresh() } is ProApiResponse.Failure -> { 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 ffddd440f2..a4614ccdd3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.webrtc import android.Manifest import android.content.Context +import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -35,7 +36,7 @@ class CallMessageProcessor @Inject constructor( private val textSecurePreferences: TextSecurePreferences, private val storage: StorageProtocol, private val webRtcBridge: WebRtcCallBridge, - private val recipientRepository: RecipientRepository, + private val recipientRepository: Lazy, @ManagerScope scope: CoroutineScope ) : OnAppStartupComponent { @@ -50,7 +51,7 @@ class CallMessageProcessor @Inject constructor( val nextMessage = WebRtcUtils.SIGNAL_QUEUE.receive() Log.d("Loki", nextMessage.type?.name ?: "CALL MESSAGE RECEIVED") val sender = nextMessage.sender ?: continue - val approvedContact = recipientRepository.getRecipient(Address.fromSerialized(sender))?.approved == true + val approvedContact = recipientRepository.get().getRecipient(Address.fromSerialized(sender))?.approved == true Log.i("Loki", "Contact is approved?: $approvedContact") if (!approvedContact && storage.getUserPublicKey() != sender) continue From 083efaced83c369c685b35efde7eb87b360d18dd Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:33:21 +1100 Subject: [PATCH 2/2] Not crashing when accounts are loaded --- .../securesms/onboarding/manager/CreateAccountManager.kt | 6 ++++-- .../securesms/onboarding/manager/LoadAccountManager.kt | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt index 32faab0652..67b75450f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.withContext import network.loki.messenger.libsession_util.PRIORITY_HIDDEN import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.database.LokiAPIDatabaseProtocol +import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.auth.LoggedInState import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase @@ -26,8 +27,9 @@ class CreateAccountManager @Inject constructor( receivedMessageHashDatabase.removeAll() loginStateRepository.update { oldState -> - require(oldState == null) { - "Attempting to create a new account when one already exists!" + if (oldState != null) { + Log.wtf("CreateAccountManager", "Tried to create account when already logged in!") + return@update oldState } LoggedInState.generate(seed = null) 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 0d69f556fd..917a6d1c6c 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 @@ -4,6 +4,7 @@ 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 import org.thoughtcrime.securesms.auth.LoginStateRepository import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase @@ -25,9 +26,10 @@ class LoadAccountManager @Inject constructor( database.clearAllLastMessageHashes() receivedMessageHashDatabase.removeAll() - loginStateRepository.update { - require(it == null) { - "Attempting to restore an account when one already exists!" + loginStateRepository.update { old -> + if (old != null) { + Log.wtf("LoadAccountManager", "Tried to load account when already logged in!") + return@update old } LoggedInState.generate(seed)