diff --git a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreen.kt index 3cb7ade114..aaa293fd83 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreen.kt @@ -55,6 +55,8 @@ import org.signal.core.ui.compose.Previews import org.signal.core.ui.compose.SignalIcons import org.signal.core.util.mebiBytes import org.signal.registration.R +import org.signal.registration.screens.shared.RestoreProgress +import org.signal.registration.screens.shared.RestoreProgressDialog import org.signal.registration.test.TestTags import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -95,10 +97,18 @@ fun LocalBackupRestoreScreen( NoBackupFoundContent(onEvent = onEvent, modifier = modifier) } LocalBackupRestoreState.RestorePhase.Preparing -> { - PreparingContent(modifier = modifier) + if (state.backupInfo?.type == LocalBackupInfo.BackupType.V2) { + V2RestoreInProgressContent(state = state, onEvent = onEvent, modifier = modifier) + } else { + V1PreparingContent(modifier = modifier) + } } LocalBackupRestoreState.RestorePhase.InProgress -> { - InProgressContent(progressFraction = state.progressFraction, onEvent = onEvent, modifier = modifier) + if (state.backupInfo?.type == LocalBackupInfo.BackupType.V2) { + V2RestoreInProgressContent(state = state, onEvent = onEvent, modifier = modifier) + } else { + V1InProgressContent(progressFraction = state.progressFraction, onEvent = onEvent, modifier = modifier) + } } LocalBackupRestoreState.RestorePhase.IncorrectCredential -> { IncorrectCredentialContent(backupType = state.backupInfo?.type, onEvent = onEvent, modifier = modifier) @@ -488,7 +498,7 @@ private fun BackupOptionCard( } @Composable -private fun PreparingContent(modifier: Modifier = Modifier) { +private fun V1PreparingContent(modifier: Modifier = Modifier) { Loading( label = stringResource(R.string.LocalBackupRestoreScreen__preparing_restore), modifier = modifier @@ -496,7 +506,7 @@ private fun PreparingContent(modifier: Modifier = Modifier) { } @Composable -private fun InProgressContent( +private fun V1InProgressContent( progressFraction: Float, onEvent: (LocalBackupRestoreEvents) -> Unit, modifier: Modifier = Modifier @@ -533,6 +543,22 @@ private fun InProgressContent( ) } +@Composable +private fun V2RestoreInProgressContent( + state: LocalBackupRestoreState, + onEvent: (LocalBackupRestoreEvents) -> Unit, + modifier: Modifier = Modifier +) { + BackupFoundContent( + backupInfo = state.backupInfo!!, + allBackups = state.allBackups, + onEvent = onEvent, + modifier = modifier + ) + + RestoreProgressDialog(restoreProgress = state.restoreProgress) +} + @Composable private fun IncorrectCredentialContent( backupType: LocalBackupInfo.BackupType?, @@ -699,6 +725,31 @@ private fun LocalBackupRestoreScreenInProgressPreview() { } } +@AllDevicePreviews +@Composable +private fun LocalBackupRestoreScreenV2InProgressPreview() { + Previews.Preview { + LocalBackupRestoreScreen( + state = LocalBackupRestoreState( + restorePhase = LocalBackupRestoreState.RestorePhase.InProgress, + backupInfo = LocalBackupInfo( + type = LocalBackupInfo.BackupType.V2, + date = LocalDateTime.of(2026, 3, 15, 14, 30, 0), + name = "signal-backup-2026-03-15-14-30-00", + uri = Uri.EMPTY, + sizeBytes = 511.mebiBytes.bytes + ), + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Restoring, + bytesCompleted = 332.mebiBytes.bytes, + totalBytes = 511.mebiBytes.bytes + ) + ), + onEvent = {} + ) + } +} + @AllDevicePreviews @Composable private fun LocalBackupRestoreScreenErrorPreview() { diff --git a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreState.kt b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreState.kt index 5e2929db53..910de6918a 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreState.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreState.kt @@ -8,6 +8,7 @@ package org.signal.registration.screens.localbackuprestore import android.net.Uri import org.signal.core.models.AccountEntropyPool import org.signal.core.util.censor +import org.signal.registration.screens.shared.RestoreProgress data class LocalBackupRestoreState( val restorePhase: RestorePhase = RestorePhase.SelectFolder, @@ -15,6 +16,7 @@ data class LocalBackupRestoreState( val allBackups: List = emptyList(), val selectedFolderUri: Uri? = null, val progressFraction: Float = 0f, + val restoreProgress: RestoreProgress? = null, val errorMessage: String? = null, val launchFolderPicker: Boolean = false, val aep: AccountEntropyPool? = null, @@ -22,7 +24,7 @@ data class LocalBackupRestoreState( val storageCapable: Boolean = true ) { - override fun toString(): String = "LocalBackupRestoreState(restorePhase=$restorePhase, backupInfo=$backupInfo, allBackups=$allBackups, selectedFolderUri=$selectedFolderUri, progressFraction=$progressFraction, errorMessage=$errorMessage, launchFolderPicker=$launchFolderPicker, aep=${aep?.displayValue?.censor()}, v1Passphrase=${v1Passphrase?.censor()}, storageCapable=$storageCapable)" + override fun toString(): String = "LocalBackupRestoreState(restorePhase=$restorePhase, backupInfo=$backupInfo, allBackups=$allBackups, selectedFolderUri=$selectedFolderUri, progressFraction=$progressFraction, restoreProgress=$restoreProgress, errorMessage=$errorMessage, launchFolderPicker=$launchFolderPicker, aep=${aep?.displayValue?.censor()}, v1Passphrase=${v1Passphrase?.censor()}, storageCapable=$storageCapable)" enum class RestorePhase { /** Waiting for user to select a backup folder. */ diff --git a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreViewModel.kt b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreViewModel.kt index 6857b6a393..4998c0cf0e 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreViewModel.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreViewModel.kt @@ -27,6 +27,7 @@ import org.signal.registration.RegistrationFlowState import org.signal.registration.RegistrationRepository import org.signal.registration.RegistrationRoute import org.signal.registration.RestoreDecision +import org.signal.registration.screens.shared.RestoreProgress import org.signal.registration.screens.util.navigateBack import org.signal.registration.screens.util.navigateTo @@ -223,18 +224,19 @@ class LocalBackupRestoreViewModel( } restoreFlow.collect { progress -> _state.value = when (progress) { - is LocalBackupRestoreProgress.Preparing -> LocalBackupRestoreState( + is LocalBackupRestoreProgress.Preparing -> currentState.copy( restorePhase = LocalBackupRestoreState.RestorePhase.Preparing, - aep = currentState.aep, - v1Passphrase = currentState.v1Passphrase, - storageCapable = currentState.storageCapable + progressFraction = 0f, + restoreProgress = null ) - is LocalBackupRestoreProgress.InProgress -> LocalBackupRestoreState( + is LocalBackupRestoreProgress.InProgress -> currentState.copy( restorePhase = LocalBackupRestoreState.RestorePhase.InProgress, progressFraction = progress.progressFraction, - aep = currentState.aep, - v1Passphrase = currentState.v1Passphrase, - storageCapable = currentState.storageCapable + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Restoring, + bytesCompleted = progress.bytesRead, + totalBytes = progress.totalBytes + ) ) is LocalBackupRestoreProgress.Complete -> { onRestoreComplete(_state.value.copy(aep = aep, v1Passphrase = currentState.v1Passphrase, storageCapable = currentState.storageCapable), progress, backup.type) diff --git a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt index e2086ef58f..9e8671fbd1 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt @@ -6,10 +6,8 @@ package org.signal.registration.screens.remotebackuprestore import android.text.format.DateFormat -import android.text.format.Formatter import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -19,13 +17,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -52,6 +46,7 @@ import org.signal.registration.screens.OnePaneRegistrationScaffold import org.signal.registration.screens.RegistrationScaffold import org.signal.registration.screens.TwoPaneRegistrationScaffold import org.signal.registration.screens.attachDebugLogHelper +import org.signal.registration.screens.shared.RestoreProgressDialog import org.signal.registration.test.TestTags import java.util.Date @@ -356,69 +351,6 @@ private fun RestoreStateDialogs( } } -@Composable -private fun RestoreProgressDialog(restoreProgress: RemoteBackupRestoreState.RestoreProgress?) { - val context = LocalContext.current - - AlertDialog( - onDismissRequest = {}, - confirmButton = {}, - dismissButton = {}, - text = { - Box( - contentAlignment = Alignment.Center, - modifier = Modifier.fillMaxWidth() - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.wrapContentSize() - ) { - if (restoreProgress == null || restoreProgress.phase == RemoteBackupRestoreState.RestoreProgress.Phase.Finalizing) { - CircularProgressIndicator( - modifier = Modifier - .padding(top = 55.dp, bottom = 16.dp) - .width(48.dp) - .height(48.dp) - ) - } else { - CircularProgressIndicator( - progress = { restoreProgress.progress }, - modifier = Modifier - .padding(top = 55.dp, bottom = 16.dp) - .width(48.dp) - .height(48.dp) - ) - } - - val progressText = when (restoreProgress?.phase) { - RemoteBackupRestoreState.RestoreProgress.Phase.Downloading -> stringResource(R.string.RemoteRestoreScreen__downloading_backup) - RemoteBackupRestoreState.RestoreProgress.Phase.Restoring -> stringResource(R.string.RemoteRestoreScreen__restoring_messages) - RemoteBackupRestoreState.RestoreProgress.Phase.Finalizing -> stringResource(R.string.RemoteRestoreScreen__finishing_restore) - null -> stringResource(R.string.RemoteRestoreScreen__restoring) - } - - Text( - text = progressText, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 12.dp) - ) - - if (restoreProgress != null && restoreProgress.phase != RemoteBackupRestoreState.RestoreProgress.Phase.Finalizing && restoreProgress.totalBytes > 0) { - val progressBytes = Formatter.formatShortFileSize(context, restoreProgress.bytesCompleted) - val totalBytes = Formatter.formatShortFileSize(context, restoreProgress.totalBytes) - Text( - text = stringResource(R.string.RemoteRestoreScreen__s_of_s_s, progressBytes, totalBytes, "%.2f%%".format(restoreProgress.progress * 100)), - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(bottom = 12.dp) - ) - } - } - } - }, - modifier = Modifier.width(212.dp) - ) -} - @AllDevicePreviews @Composable private fun RemoteRestoreScreenLoadedPreview() { diff --git a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreState.kt b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreState.kt index 26fdf33ccb..e23f282d9a 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreState.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreState.kt @@ -7,6 +7,7 @@ package org.signal.registration.screens.remotebackuprestore import org.signal.core.models.AccountEntropyPool import org.signal.core.util.censor +import org.signal.registration.screens.shared.RestoreProgress data class RemoteBackupRestoreState( val aep: AccountEntropyPool, @@ -42,19 +43,4 @@ data class RemoteBackupRestoreState( data object Failed : RestoreState } - - data class RestoreProgress( - val phase: Phase, - val bytesCompleted: Long, - val totalBytes: Long - ) { - val progress: Float - get() = if (totalBytes > 0) bytesCompleted.toFloat() / totalBytes.toFloat() else 0f - - enum class Phase { - Downloading, - Restoring, - Finalizing - } - } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModel.kt b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModel.kt index 9316ee5f12..e9e4bda3cb 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModel.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModel.kt @@ -27,6 +27,7 @@ import org.signal.registration.RegistrationFlowState import org.signal.registration.RegistrationRepository import org.signal.registration.RegistrationRoute import org.signal.registration.RestoreDecision +import org.signal.registration.screens.shared.RestoreProgress import org.signal.registration.screens.util.navigateBack import org.signal.registration.screens.util.navigateTo import kotlin.coroutines.CoroutineContext @@ -89,8 +90,8 @@ class RemoteBackupRestoreViewModel( Log.i(TAG, "[restoreBackup] Restoring...") _state.value = _state.value.copy( restoreState = RemoteBackupRestoreState.RestoreState.InProgress, - restoreProgress = RemoteBackupRestoreState.RestoreProgress( - phase = RemoteBackupRestoreState.RestoreProgress.Phase.Downloading, + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Downloading, bytesCompleted = progress.bytesDownloaded, totalBytes = progress.totalBytes ) @@ -100,8 +101,8 @@ class RemoteBackupRestoreViewModel( Log.i(TAG, "[restoreBackup] Restoring...") _state.value = _state.value.copy( restoreState = RemoteBackupRestoreState.RestoreState.InProgress, - restoreProgress = RemoteBackupRestoreState.RestoreProgress( - phase = RemoteBackupRestoreState.RestoreProgress.Phase.Restoring, + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Restoring, bytesCompleted = progress.bytesRead, totalBytes = progress.totalBytes ) @@ -111,8 +112,8 @@ class RemoteBackupRestoreViewModel( Log.i(TAG, "[restoreBackup] Finalizing...") _state.value = _state.value.copy( restoreState = RemoteBackupRestoreState.RestoreState.InProgress, - restoreProgress = RemoteBackupRestoreState.RestoreProgress( - phase = RemoteBackupRestoreState.RestoreProgress.Phase.Finalizing, + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Finalizing, bytesCompleted = 0, totalBytes = 0 ) diff --git a/feature/registration/src/main/java/org/signal/registration/screens/shared/RestoreProgressDialog.kt b/feature/registration/src/main/java/org/signal/registration/screens/shared/RestoreProgressDialog.kt new file mode 100644 index 0000000000..5accb366bb --- /dev/null +++ b/feature/registration/src/main/java/org/signal/registration/screens/shared/RestoreProgressDialog.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.registration.screens.shared + +import android.text.format.Formatter +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import org.signal.core.ui.compose.AllDevicePreviews +import org.signal.core.ui.compose.Previews +import org.signal.registration.R + +/** + * Non-dismissable popup showing restore progress: a circular indicator, a phase label, and a byte count. + * A null [restoreProgress] (or the [RestoreProgress.Phase.Finalizing] phase) renders an indeterminate spinner. + */ +@Composable +fun RestoreProgressDialog(restoreProgress: RestoreProgress?) { + val context = LocalContext.current + + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + dismissButton = {}, + text = { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxWidth() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.wrapContentSize() + ) { + if (restoreProgress == null || restoreProgress.phase == RestoreProgress.Phase.Finalizing) { + CircularProgressIndicator( + modifier = Modifier + .padding(top = 55.dp, bottom = 16.dp) + .width(48.dp) + .height(48.dp) + ) + } else { + CircularProgressIndicator( + progress = { restoreProgress.progress }, + modifier = Modifier + .padding(top = 55.dp, bottom = 16.dp) + .width(48.dp) + .height(48.dp) + ) + } + + val progressText = when (restoreProgress?.phase) { + RestoreProgress.Phase.Downloading -> stringResource(R.string.RemoteRestoreScreen__downloading_backup) + RestoreProgress.Phase.Restoring -> stringResource(R.string.RemoteRestoreScreen__restoring_messages) + RestoreProgress.Phase.Finalizing -> stringResource(R.string.RemoteRestoreScreen__finishing_restore) + null -> stringResource(R.string.RemoteRestoreScreen__restoring) + } + + Text( + text = progressText, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 12.dp) + ) + + if (restoreProgress != null && restoreProgress.phase != RestoreProgress.Phase.Finalizing && restoreProgress.totalBytes > 0) { + val progressBytes = Formatter.formatShortFileSize(context, restoreProgress.bytesCompleted) + val totalBytes = Formatter.formatShortFileSize(context, restoreProgress.totalBytes) + Text( + text = stringResource(R.string.RemoteRestoreScreen__s_of_s_s, progressBytes, totalBytes, "%.2f%%".format(restoreProgress.progress * 100)), + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(bottom = 12.dp) + ) + } + } + } + }, + modifier = Modifier.width(212.dp) + ) +} + +@AllDevicePreviews +@Composable +private fun RestoreProgressDialogPreview() { + Previews.Preview { + RestoreProgressDialog( + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Restoring, + bytesCompleted = 512_000, + totalBytes = 1_024_000 + ) + ) + } +} + +/** + * Progress of an in-flight backup restore, shared between the remote and local restore flows. + */ +data class RestoreProgress( + val phase: Phase, + val bytesCompleted: Long, + val totalBytes: Long +) { + val progress: Float + get() = if (totalBytes > 0) bytesCompleted.toFloat() / totalBytes.toFloat() else 0f + + enum class Phase { + Downloading, + Restoring, + Finalizing + } +} diff --git a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt index f5f63c2247..c5e2d9e9a1 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt @@ -372,7 +372,7 @@ private fun PrimaryDeviceCallToActionButtons( Text(stringResource(R.string.RegistrationActivity_continue)) } - Spacer(modifier = Modifier.height(17.dp)) + Spacer(modifier = Modifier.height(16.dp)) Buttons.LargeTonal( onClick = onRestoreOrTransferClick, diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (day)_41c0d57c_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (day)_41c0d57c_0.png index 34058c1eeb..78fd993805 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (day)_41c0d57c_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (day)_41c0d57c_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca027a262cd5878c40e4abd08f966c0ee6bf8f02eeef787f9832e6b9235a5d37 -size 279488 +oid sha256:8d91eb170a945e2ae40b73cfdf00cd7ed694b705d391fc24ef7097a586aad3c4 +size 279157 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (night)_9dae7e43_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (night)_9dae7e43_0.png index 8b6b518f0a..1d246b3770 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (night)_9dae7e43_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable landscape (night)_9dae7e43_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6df52102839dd5fff99d087b01b3f59e4bb7ecf4b7cfc97f28e33075b667ab26 -size 253635 +oid sha256:a89fcaf8db0b61f35e952bb6bdc8bbba68b2545e6b19d182a03fc8d0be98e6ea +size 253292 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable portrait (day)_bf09cc79_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable portrait (day)_bf09cc79_0.png index 1af71d3e82..e676786b99 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable portrait (day)_bf09cc79_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_foldable portrait (day)_bf09cc79_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3af78bd0c9865d3788e824d9075b9d285dc5b721375688a06a5927d1561eff5e -size 265333 +oid sha256:bdfb102aca844d4428e203c022e2fbfb0768248108224fcc03b1efa2c1257a7d +size 265605 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (day)_38357cfa_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (day)_38357cfa_0.png index be0452fe99..95ce9b4b56 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (day)_38357cfa_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (day)_38357cfa_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e224fd2c468d206149d54cb7d42e156d9bc1ef43000e5b6f8c71a3e920c6ecb -size 52900 +oid sha256:9c648dec6bfe1a7db0cc9847e8cfca1ac3dadad5f3f3a9c177b59209ea259854 +size 52917 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (night)_2eebbf71_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (night)_2eebbf71_0.png index 0bbfd40083..80cc998ee6 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (night)_2eebbf71_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone landscape (night)_2eebbf71_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00c24756136f9ec19f52114a3b3505588b3ace0f35df07272a9c16823ef4c1dd -size 53201 +oid sha256:ef5676fc65e169f0a82efbccc458187788e0c561ad91f97a07392430ed0e9160 +size 53219 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone portrait (day)_1526b652_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone portrait (day)_1526b652_0.png index 05e01daa65..09c62c2dff 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone portrait (day)_1526b652_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_phone portrait (day)_1526b652_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00ee05dceb245e7789fabe844e7d8b78a317edafe913ad7d599909f05565d7a1 -size 104487 +oid sha256:0b33a06e42560c13c29c3ecdd383a5016bcd20a151bf5b076aa6cec2361d9343 +size 105020 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_rtl_5cbb5cfd_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_rtl_5cbb5cfd_0.png index 671359ace4..60f6b75691 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_rtl_5cbb5cfd_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_rtl_5cbb5cfd_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bdcf51c97838c57ddede736892461939d7ad13d88b266eddd589a3b30388b3f -size 241919 +oid sha256:097e505e759ac652ea162b2a9571429daac0bd9ede00418e7350ba81adcf6e57 +size 241783 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (day)_de1bb092_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (day)_de1bb092_0.png index 670d6100b0..dd4c7363d2 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (day)_de1bb092_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (day)_de1bb092_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d6ef9c041521feeb0cda423cbb612f2ecdffe29a4eb97a9310e5c76192f27e6 -size 192932 +oid sha256:3c972a56a010492bf63eeafd107fd1a898f104f0f413d2c8ea018f3706487094 +size 192897 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (night)_fe962c61_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (night)_fe962c61_0.png index c3a43d6621..908fad6925 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (night)_fe962c61_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable landscape (night)_fe962c61_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6ba17b3a59fc897a23e55f3a76c4ae9613a6f547919f60c077ee8ea59f8d231 -size 182338 +oid sha256:1f2cd9e0964d19f3833ebc6c647d6d4787d3ca1da345e237f9d445b583258033 +size 182541 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable portrait (day)_e269713d_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable portrait (day)_e269713d_0.png index 4fdcc279ab..60719d75e5 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable portrait (day)_e269713d_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_small foldable portrait (day)_e269713d_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c27372e794bcb63f49bf0e89cb495cf29c12d7024db7aeffde0db301f0951432 -size 165171 +oid sha256:8604d50efb86b60216707b41b67eba3894e489be5948d97467e28b9f6cf3a289 +size 165254 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (day)_308754cb_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (day)_308754cb_0.png index 3a168e047b..ac23040f92 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (day)_308754cb_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (day)_308754cb_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b333000ff39c5d726307d9be2691de4657032cbf4a9a5d946afa94ce3001c73 -size 435557 +oid sha256:cd6b3679f8458db22ae3c86bc9c659ee9e89da975fa87618f41c45981bb4a236 +size 435556 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (night)_d25401fe_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (night)_d25401fe_0.png index 4e8880f843..bf04f871c7 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (night)_d25401fe_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet landscape (night)_d25401fe_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58ab68ce8d815d9f7aa094e0261075759083f67fcd4d40d8a38b519c9ac3ee78 -size 395238 +oid sha256:7c99fdb26eca7d0aa2c95fa6bd45cc79b592cc4ac2d9dae33269551f3685b916 +size 395279 diff --git a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet portrait (day)_ae17fb11_0.png b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet portrait (day)_ae17fb11_0.png index 6325fc6ea7..d4cc96a7dd 100644 --- a/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet portrait (day)_ae17fb11_0.png +++ b/feature/registration/src/screenshotTestDebug/reference/org/signal/registration/screens/welcome/WelcomeScreenScreenshotTests/WelcomeScreenPhonePreview_tablet portrait (day)_ae17fb11_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34263308f2b19c65983e82c7d775478612529b9174a98c11a23ec3b3e3dbebb6 -size 297926 +oid sha256:e6c58d8f3e5f9ac5b386747fb81d4422f5e40ec8913a9bd179086536dee1b633 +size 297909 diff --git a/feature/registration/src/test/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreenTest.kt b/feature/registration/src/test/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreenTest.kt index 0f8e58d5bf..d7622fa868 100644 --- a/feature/registration/src/test/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreenTest.kt +++ b/feature/registration/src/test/java/org/signal/registration/screens/localbackuprestore/LocalBackupRestoreScreenTest.kt @@ -8,8 +8,10 @@ package org.signal.registration.screens.localbackuprestore import android.app.Application import android.net.Uri import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.test.core.app.ApplicationProvider @@ -20,6 +22,8 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.signal.core.ui.CoreUiDependenciesRule import org.signal.core.ui.compose.theme.SignalTheme +import org.signal.registration.R +import org.signal.registration.screens.shared.RestoreProgress import org.signal.registration.test.TestTags import java.time.LocalDateTime @@ -95,12 +99,19 @@ class LocalBackupRestoreScreenTest { } @Test - fun `InProgress phase displays progress bar`() { + fun `V1 InProgress phase displays progress bar`() { composeTestRule.setContent { SignalTheme { LocalBackupRestoreScreen( state = LocalBackupRestoreState( restorePhase = LocalBackupRestoreState.RestorePhase.InProgress, + backupInfo = LocalBackupInfo( + type = LocalBackupInfo.BackupType.V1, + date = LocalDateTime.of(2026, 3, 15, 14, 30, 0), + name = "signal-2026-03-15-14-30-00.backup", + uri = Uri.EMPTY, + sizeBytes = 1_482_184_499 + ), progressFraction = 0.5f ), onEvent = {} @@ -110,4 +121,35 @@ class LocalBackupRestoreScreenTest { composeTestRule.onNodeWithTag(TestTags.LOCAL_BACKUP_RESTORE_PROGRESS_BAR).performScrollTo().assertIsDisplayed() } + + @Test + fun `V2 InProgress phase displays restore progress dialog and not the linear progress bar`() { + val context = ApplicationProvider.getApplicationContext() + + composeTestRule.setContent { + SignalTheme { + LocalBackupRestoreScreen( + state = LocalBackupRestoreState( + restorePhase = LocalBackupRestoreState.RestorePhase.InProgress, + backupInfo = LocalBackupInfo( + type = LocalBackupInfo.BackupType.V2, + date = LocalDateTime.of(2026, 3, 15, 14, 30, 0), + name = "signal-backup-2026-03-15-14-30-00", + uri = Uri.EMPTY, + sizeBytes = 1_482_184_499 + ), + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Restoring, + bytesCompleted = 50, + totalBytes = 100 + ) + ), + onEvent = {} + ) + } + } + + composeTestRule.onNodeWithText(context.getString(R.string.RemoteRestoreScreen__restoring_messages)).assertIsDisplayed() + composeTestRule.onNodeWithTag(TestTags.LOCAL_BACKUP_RESTORE_PROGRESS_BAR).assertIsNotDisplayed() + } } diff --git a/feature/registration/src/test/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModelTest.kt b/feature/registration/src/test/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModelTest.kt index 202ed198e7..574d3fa071 100644 --- a/feature/registration/src/test/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModelTest.kt +++ b/feature/registration/src/test/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreViewModelTest.kt @@ -35,6 +35,7 @@ import org.signal.registration.RegistrationFlowState import org.signal.registration.RegistrationRepository import org.signal.registration.RegistrationRoute import org.signal.registration.RestoreDecision +import org.signal.registration.screens.shared.RestoreProgress import java.io.IOException import kotlin.time.Duration.Companion.seconds @@ -156,8 +157,8 @@ class RemoteBackupRestoreViewModelTest { val initialState = RemoteBackupRestoreState( aep = aep, restoreState = RemoteBackupRestoreState.RestoreState.Failed, - restoreProgress = RemoteBackupRestoreState.RestoreProgress( - phase = RemoteBackupRestoreState.RestoreProgress.Phase.Downloading, + restoreProgress = RestoreProgress( + phase = RestoreProgress.Phase.Downloading, bytesCompleted = 50, totalBytes = 100 ) @@ -313,7 +314,7 @@ class RemoteBackupRestoreViewModelTest { val last = states.last() assertThat(last.restoreState).isEqualTo(RemoteBackupRestoreState.RestoreState.InProgress) - assertThat(last.restoreProgress?.phase).isEqualTo(RemoteBackupRestoreState.RestoreProgress.Phase.Downloading) + assertThat(last.restoreProgress?.phase).isEqualTo(RestoreProgress.Phase.Downloading) assertThat(last.restoreProgress?.bytesCompleted).isEqualTo(30L) assertThat(last.restoreProgress?.totalBytes).isEqualTo(100L) } @@ -330,7 +331,7 @@ class RemoteBackupRestoreViewModelTest { val last = states.last() assertThat(last.restoreState).isEqualTo(RemoteBackupRestoreState.RestoreState.InProgress) - assertThat(last.restoreProgress?.phase).isEqualTo(RemoteBackupRestoreState.RestoreProgress.Phase.Restoring) + assertThat(last.restoreProgress?.phase).isEqualTo(RestoreProgress.Phase.Restoring) assertThat(last.restoreProgress?.bytesCompleted).isEqualTo(75L) } @@ -346,7 +347,7 @@ class RemoteBackupRestoreViewModelTest { val last = states.last() assertThat(last.restoreState).isEqualTo(RemoteBackupRestoreState.RestoreState.InProgress) - assertThat(last.restoreProgress?.phase).isEqualTo(RemoteBackupRestoreState.RestoreProgress.Phase.Finalizing) + assertThat(last.restoreProgress?.phase).isEqualTo(RestoreProgress.Phase.Finalizing) } @Test