mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-21 13:05:00 +01:00
Store restoreDecision separately in regV5.
This commit is contained in:
committed by
Michelle Tang
parent
2c35a6a3fe
commit
e07a3e05bf
+3
-11
@@ -27,10 +27,10 @@ import org.signal.core.util.StreamUtil
|
||||
import org.signal.core.util.crypto.AttachmentSecretProvider
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.registration.PreExistingRegistrationData
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.StorageController
|
||||
import org.signal.registration.StoredProfileData
|
||||
import org.signal.registration.proto.RegistrationData
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.screens.localbackuprestore.LocalBackupInfo
|
||||
import org.signal.registration.screens.remotebackuprestore.RemoteBackupRestoreProgress
|
||||
import org.thoughtcrime.securesms.backup.FullBackupImporter
|
||||
@@ -244,26 +244,18 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
|
||||
SignalStore.svr.masterKeyForInitialDataRestore = MasterKey(data.temporaryMasterKey.toByteArray())
|
||||
}
|
||||
|
||||
applyRestoreDecision(data.restoreDecision)
|
||||
|
||||
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the registration module's [RestoreDecision] into the app's [RestoreDecisionState] so the rest of the app
|
||||
* knows whether we're a fresh account, skipped a restore, or successfully restored data. Only applied while the
|
||||
* decision is still pending, since the state machine is otherwise terminal.
|
||||
*/
|
||||
private fun applyRestoreDecision(decision: RestoreDecision) {
|
||||
override suspend fun setRestoreDecision(decision: RestoreDecision) = withContext(Dispatchers.IO) {
|
||||
if (!SignalStore.registration.restoreDecisionState.isDecisionPending) {
|
||||
return
|
||||
return@withContext
|
||||
}
|
||||
|
||||
SignalStore.registration.restoreDecisionState = when (decision) {
|
||||
RestoreDecision.NEW_ACCOUNT -> RestoreDecisionState.NewAccount
|
||||
RestoreDecision.SKIPPED -> RestoreDecisionState.Skipped
|
||||
RestoreDecision.COMPLETED -> RestoreDecisionState.Completed
|
||||
RestoreDecision.UNSET -> return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -27,6 +27,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.signal.registration.NetworkController
|
||||
import org.signal.registration.NewRegistrationData
|
||||
import org.signal.registration.PreExistingRegistrationData
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.StorageController
|
||||
import org.signal.registration.StoredProfileData
|
||||
import org.signal.registration.proto.ProvisioningData
|
||||
@@ -196,6 +197,11 @@ class DemoStorageController(private val context: Context) : StorageController {
|
||||
Unit
|
||||
}
|
||||
|
||||
override suspend fun setRestoreDecision(decision: RestoreDecision) = withContext(Dispatchers.IO) {
|
||||
Log.i(TAG, "[setRestoreDecision] Recording restore decision: $decision")
|
||||
RegistrationPreferences.restoreDecision = decision
|
||||
}
|
||||
|
||||
override suspend fun scanLocalBackupFolder(folderUri: Uri): List<LocalBackupInfo> = withContext(Dispatchers.IO) {
|
||||
val folder = DocumentFile.fromTreeUri(context, folderUri) ?: return@withContext emptyList()
|
||||
val children = folder.listFiles()
|
||||
|
||||
+3
-1
@@ -225,6 +225,7 @@ private fun RegistrationInfo(data: MainScreenState.ExistingRegistrationState) {
|
||||
RegistrationField(label = "PNI", value = data.pni)
|
||||
RegistrationField(label = "AEP", value = data.aep)
|
||||
RegistrationField(label = "Temporary Master Key", value = data.temporaryMasterKey ?: "null")
|
||||
RegistrationField(label = "Restore Decision", value = data.restoreDecision ?: "(unset)")
|
||||
if (data.pinsOptedOut) {
|
||||
RegistrationField(label = "PINs Opted Out", value = "Yes")
|
||||
} else {
|
||||
@@ -377,7 +378,8 @@ private fun MainScreenWithRegistrationPreview() {
|
||||
pin = "1234",
|
||||
registrationLockEnabled = true,
|
||||
pinsOptedOut = false,
|
||||
temporaryMasterKey = null
|
||||
temporaryMasterKey = null,
|
||||
restoreDecision = "COMPLETED"
|
||||
),
|
||||
profileState = MainScreenState.ProfileState(
|
||||
givenName = "Ada",
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@ data class MainScreenState(
|
||||
val pin: String?,
|
||||
val registrationLockEnabled: Boolean,
|
||||
val pinsOptedOut: Boolean,
|
||||
val temporaryMasterKey: String?
|
||||
val temporaryMasterKey: String?,
|
||||
val restoreDecision: String?
|
||||
)
|
||||
|
||||
data class ProfileState(
|
||||
|
||||
+2
-1
@@ -74,7 +74,8 @@ class MainScreenViewModel(
|
||||
pinsOptedOut = RegistrationPreferences.pinsOptedOut,
|
||||
temporaryMasterKey = RegistrationPreferences.temporaryMasterKey?.let {
|
||||
Base64.encodeWithPadding(it.serialize())
|
||||
}
|
||||
},
|
||||
restoreDecision = RegistrationPreferences.restoreDecision?.name
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
||||
+6
@@ -19,6 +19,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.signal.registration.NetworkController
|
||||
import org.signal.registration.NewRegistrationData
|
||||
import org.signal.registration.PreExistingRegistrationData
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
/**
|
||||
* SharedPreferences-based storage for registration data that doesn't need
|
||||
@@ -58,6 +59,7 @@ object RegistrationPreferences {
|
||||
private const val KEY_PROFILE_FAMILY_NAME = "profile_family_name"
|
||||
private const val KEY_PROFILE_AVATAR = "profile_avatar"
|
||||
private const val KEY_PROFILE_DISCOVERABLE = "profile_discoverable"
|
||||
private const val KEY_RESTORE_DECISION = "restore_decision"
|
||||
|
||||
fun init(context: Application) {
|
||||
this.context = context
|
||||
@@ -141,6 +143,10 @@ object RegistrationPreferences {
|
||||
get() = prefs.getBoolean(KEY_FETCHES_MESSAGES, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_FETCHES_MESSAGES, value) }
|
||||
|
||||
var restoreDecision: RestoreDecision?
|
||||
get() = prefs.getString(KEY_RESTORE_DECISION, null)?.let { runCatching { RestoreDecision.valueOf(it) }.getOrNull() }
|
||||
set(value) = prefs.edit { putString(KEY_RESTORE_DECISION, value?.name) }
|
||||
|
||||
var profileGivenName: String
|
||||
get() = prefs.getString(KEY_PROFILE_GIVEN_NAME, "") ?: ""
|
||||
set(value) = prefs.edit { putString(KEY_PROFILE_GIVEN_NAME, value) }
|
||||
|
||||
+1
-5
@@ -41,7 +41,6 @@ import org.signal.registration.NetworkController.SessionMetadata
|
||||
import org.signal.registration.NetworkController.SvrCredentials
|
||||
import org.signal.registration.NetworkController.UpdateSessionError
|
||||
import org.signal.registration.proto.ProvisioningData
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.proto.SvrCredential
|
||||
import org.signal.registration.screens.localbackuprestore.LocalBackupInfo
|
||||
import org.signal.registration.screens.remotebackuprestore.RemoteBackupRestoreProgress
|
||||
@@ -543,10 +542,7 @@ class RegistrationRepository(val context: Context, val networkController: Networ
|
||||
*/
|
||||
suspend fun setRestoreDecision(decision: RestoreDecision): Unit = withContext(Dispatchers.IO) {
|
||||
Log.i(TAG, "[setRestoreDecision] Recording restore decision: $decision")
|
||||
storageController.updateInProgressRegistrationData {
|
||||
this.restoreDecision = decision
|
||||
}
|
||||
storageController.commitRegistrationData()
|
||||
storageController.setRestoreDecision(decision)
|
||||
}
|
||||
|
||||
suspend fun getPreExistingRegistrationData(): PreExistingRegistrationData? {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.registration
|
||||
|
||||
/**
|
||||
* The terminal restore decision the user reached during registration. Mirrors the terminal states of the app's
|
||||
* RestoreDecisionState. We intentionally do not model the transient pending states (START / INTEND_TO_RESTORE)
|
||||
* here, as the registration flow performs any restore inline before completing.
|
||||
*/
|
||||
enum class RestoreDecision {
|
||||
NEW_ACCOUNT,
|
||||
SKIPPED,
|
||||
COMPLETED
|
||||
}
|
||||
@@ -83,6 +83,12 @@ interface StorageController {
|
||||
*/
|
||||
suspend fun commitRegistrationData()
|
||||
|
||||
/**
|
||||
* Persists the terminal [RestoreDecision] the user reached during registration directly to permanent app state,
|
||||
* so the rest of the app knows whether we're a fresh account, skipped a restore, or successfully restored data.
|
||||
*/
|
||||
suspend fun setRestoreDecision(decision: RestoreDecision)
|
||||
|
||||
/**
|
||||
* Begins restoring from a V1 (.backup) file identified by the given [uri].
|
||||
*
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
|
||||
class DeviceTransferCompleteViewModel(
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.signal.core.util.logging.Log
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
import org.signal.registration.screens.util.navigateBack
|
||||
import org.signal.registration.screens.util.navigateTo
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import org.signal.registration.NetworkController
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
import org.signal.registration.screens.util.navigateTo
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.signal.libsignal.net.RequestResult
|
||||
import org.signal.registration.NetworkController
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
import org.signal.registration.screens.util.navigateBack
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import org.signal.registration.screens.EventDrivenViewModel
|
||||
import org.signal.registration.screens.util.navigateTo
|
||||
|
||||
|
||||
@@ -52,20 +52,6 @@ message RegistrationData {
|
||||
|
||||
// JSON-serialized flow state snapshot (from saveFlowState/restoreFlowState)
|
||||
string flowStateJson = 21;
|
||||
|
||||
// The terminal restore decision the user reached during this flow. The app translates this into its own
|
||||
// RestoreDecisionState when committing, so the rest of the app knows whether we're a fresh account, skipped a
|
||||
// restore, or successfully restored data.
|
||||
RestoreDecision restoreDecision = 25;
|
||||
}
|
||||
|
||||
// Mirrors the terminal states of the app's RestoreDecisionState. We intentionally do not model the transient
|
||||
// pending states (START / INTEND_TO_RESTORE) here, as the new flow performs any restore inline before completing.
|
||||
enum RestoreDecision {
|
||||
UNSET = 0;
|
||||
NEW_ACCOUNT = 1;
|
||||
SKIPPED = 2;
|
||||
COMPLETED = 3;
|
||||
}
|
||||
|
||||
message SvrCredential {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DeviceTransferCompleteViewModelTest {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import org.signal.core.ui.navigation.ResultEventBus
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import org.signal.registration.NetworkController
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class PinCreationViewModelTest {
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
class PinEntryForSvrRestoreViewModelTest {
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import org.signal.libsignal.net.RequestResult
|
||||
import org.signal.registration.NetworkController
|
||||
import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RemoteBackupRestoreViewModelTest {
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.signal.registration.RegistrationFlowEvent
|
||||
import org.signal.registration.RegistrationFlowState
|
||||
import org.signal.registration.RegistrationRepository
|
||||
import org.signal.registration.RegistrationRoute
|
||||
import org.signal.registration.proto.RestoreDecision
|
||||
import org.signal.registration.RestoreDecision
|
||||
|
||||
class ArchiveRestoreSelectionViewModelTest {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user