mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-08-04 20:34:14 +01:00
Use better progress for local backup v2 in regV5.
This commit is contained in:
committed by
Cody Henthorne
parent
fd83ad9625
commit
2f70d780d0
+55
-4
@@ -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() {
|
||||
|
||||
+3
-1
@@ -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<LocalBackupInfo> = 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. */
|
||||
|
||||
+10
-8
@@ -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)
|
||||
|
||||
+1
-69
@@ -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() {
|
||||
|
||||
+1
-15
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -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
|
||||
)
|
||||
|
||||
+127
@@ -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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca027a262cd5878c40e4abd08f966c0ee6bf8f02eeef787f9832e6b9235a5d37
|
||||
size 279488
|
||||
oid sha256:8d91eb170a945e2ae40b73cfdf00cd7ed694b705d391fc24ef7097a586aad3c4
|
||||
size 279157
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6df52102839dd5fff99d087b01b3f59e4bb7ecf4b7cfc97f28e33075b667ab26
|
||||
size 253635
|
||||
oid sha256:a89fcaf8db0b61f35e952bb6bdc8bbba68b2545e6b19d182a03fc8d0be98e6ea
|
||||
size 253292
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3af78bd0c9865d3788e824d9075b9d285dc5b721375688a06a5927d1561eff5e
|
||||
size 265333
|
||||
oid sha256:bdfb102aca844d4428e203c022e2fbfb0768248108224fcc03b1efa2c1257a7d
|
||||
size 265605
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4e224fd2c468d206149d54cb7d42e156d9bc1ef43000e5b6f8c71a3e920c6ecb
|
||||
size 52900
|
||||
oid sha256:9c648dec6bfe1a7db0cc9847e8cfca1ac3dadad5f3f3a9c177b59209ea259854
|
||||
size 52917
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:00c24756136f9ec19f52114a3b3505588b3ace0f35df07272a9c16823ef4c1dd
|
||||
size 53201
|
||||
oid sha256:ef5676fc65e169f0a82efbccc458187788e0c561ad91f97a07392430ed0e9160
|
||||
size 53219
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:00ee05dceb245e7789fabe844e7d8b78a317edafe913ad7d599909f05565d7a1
|
||||
size 104487
|
||||
oid sha256:0b33a06e42560c13c29c3ecdd383a5016bcd20a151bf5b076aa6cec2361d9343
|
||||
size 105020
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8bdcf51c97838c57ddede736892461939d7ad13d88b266eddd589a3b30388b3f
|
||||
size 241919
|
||||
oid sha256:097e505e759ac652ea162b2a9571429daac0bd9ede00418e7350ba81adcf6e57
|
||||
size 241783
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7d6ef9c041521feeb0cda423cbb612f2ecdffe29a4eb97a9310e5c76192f27e6
|
||||
size 192932
|
||||
oid sha256:3c972a56a010492bf63eeafd107fd1a898f104f0f413d2c8ea018f3706487094
|
||||
size 192897
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a6ba17b3a59fc897a23e55f3a76c4ae9613a6f547919f60c077ee8ea59f8d231
|
||||
size 182338
|
||||
oid sha256:1f2cd9e0964d19f3833ebc6c647d6d4787d3ca1da345e237f9d445b583258033
|
||||
size 182541
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c27372e794bcb63f49bf0e89cb495cf29c12d7024db7aeffde0db301f0951432
|
||||
size 165171
|
||||
oid sha256:8604d50efb86b60216707b41b67eba3894e489be5948d97467e28b9f6cf3a289
|
||||
size 165254
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9b333000ff39c5d726307d9be2691de4657032cbf4a9a5d946afa94ce3001c73
|
||||
size 435557
|
||||
oid sha256:cd6b3679f8458db22ae3c86bc9c659ee9e89da975fa87618f41c45981bb4a236
|
||||
size 435556
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:58ab68ce8d815d9f7aa094e0261075759083f67fcd4d40d8a38b519c9ac3ee78
|
||||
size 395238
|
||||
oid sha256:7c99fdb26eca7d0aa2c95fa6bd45cc79b592cc4ac2d9dae33269551f3685b916
|
||||
size 395279
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:34263308f2b19c65983e82c7d775478612529b9174a98c11a23ec3b3e3dbebb6
|
||||
size 297926
|
||||
oid sha256:e6c58d8f3e5f9ac5b386747fb81d4422f5e40ec8913a9bd179086536dee1b633
|
||||
size 297909
|
||||
|
||||
+43
-1
@@ -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<Application>()
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user