BackupStatusRow to display backup restore status in settings fragment.

This commit is contained in:
Alex Hart
2024-10-15 16:55:45 -03:00
committed by Greyson Parrelli
parent b073005ff9
commit dd4d674a61
4 changed files with 261 additions and 10 deletions

View File

@@ -55,7 +55,7 @@ private const val NONE = -1
*/
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun BackupStatus(
fun BackupStatusBanner(
data: BackupStatusData,
onSkipClick: () -> Unit = {},
onDismissClick: () -> Unit = {},
@@ -150,13 +150,13 @@ fun BackupStatus(
fun BackupStatusBannerPreview() {
Previews.Preview {
Column {
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.RestoringMedia(5755000.bytes, 1253.mebiBytes)
)
HorizontalDivider()
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.RestoringMedia(
bytesDownloaded = 55000.bytes,
bytesTotal = 1253.mebiBytes,
@@ -166,7 +166,7 @@ fun BackupStatusBannerPreview() {
HorizontalDivider()
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.RestoringMedia(
bytesDownloaded = 55000.bytes,
bytesTotal = 1253.mebiBytes,
@@ -176,7 +176,7 @@ fun BackupStatusBannerPreview() {
HorizontalDivider()
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.RestoringMedia(
bytesDownloaded = 55000.bytes,
bytesTotal = 1253.mebiBytes,
@@ -186,13 +186,13 @@ fun BackupStatusBannerPreview() {
HorizontalDivider()
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.NotEnoughFreeSpace(40900.kibiBytes)
)
HorizontalDivider()
BackupStatus(
BackupStatusBanner(
data = BackupStatusData.CouldNotCompleteBackup
)
}
@@ -241,7 +241,7 @@ sealed interface BackupStatusData {
class NotEnoughFreeSpace(
requiredSpace: ByteSize
) : BackupStatusData {
private val requiredSpace = requiredSpace.toUnitString(maxPlaces = 2)
val requiredSpace = requiredSpace.toUnitString(maxPlaces = 2)
override val iconRes: Int = R.drawable.symbol_backup_error_24

View File

@@ -0,0 +1,240 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2.ui.status
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
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.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.signal.core.ui.Previews
import org.signal.core.ui.Rows
import org.signal.core.ui.SignalPreview
import org.signal.core.util.ByteSize
import org.thoughtcrime.securesms.R
import kotlin.math.roundToInt
/**
* Backup status displayable as a row on a settings page.
*/
@Composable
fun BackupStatusRow(
backupStatusData: BackupStatusData,
onSkipClick: () -> Unit = {},
onCancelClick: () -> Unit = {}
) {
Column {
if (backupStatusData !is BackupStatusData.CouldNotCompleteBackup) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter))
) {
LinearProgressIndicator(
color = progressColor(backupStatusData),
progress = { backupStatusData.progress },
modifier = Modifier.weight(1f)
)
IconButton(
onClick = onCancelClick
) {
Icon(
painter = painterResource(R.drawable.symbol_x_24),
contentDescription = stringResource(R.string.BackupStatusRow__cancel_download)
)
}
}
}
when (backupStatusData) {
is BackupStatusData.RestoringMedia -> {
Text(
text = getRestoringMediaString(backupStatusData),
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter))
)
}
is BackupStatusData.NotEnoughFreeSpace -> {
Text(
text = stringResource(
R.string.BackupStatusRow__not_enough_space,
backupStatusData.requiredSpace,
"%d".format((backupStatusData.progress * 100).roundToInt())
),
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter))
)
Rows.TextRow(
text = stringResource(R.string.BackupStatusRow__skip_download),
onClick = onSkipClick
)
}
BackupStatusData.CouldNotCompleteBackup -> {
val inlineContentMap = mapOf(
"yellow_bullet" to InlineTextContent(
Placeholder(12.sp, 12.sp, PlaceholderVerticalAlign.TextCenter)
) {
Box(
modifier = Modifier
.size(12.dp)
.background(color = backupStatusData.iconColors.foreground, shape = CircleShape)
)
}
)
Text(
text = buildAnnotatedString {
appendInlineContent("yellow_bullet")
append(" ")
append(stringResource(R.string.BackupStatusRow__your_last_backup))
},
inlineContent = inlineContentMap,
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter))
)
}
}
}
}
@Composable
private fun getRestoringMediaString(backupStatusData: BackupStatusData.RestoringMedia): String {
return when (backupStatusData.restoreStatus) {
BackupStatusData.RestoreStatus.NORMAL -> {
stringResource(
R.string.BackupStatusRow__downloading_s_of_s_s,
backupStatusData.bytesDownloaded.toUnitString(2),
backupStatusData.bytesTotal.toUnitString(2),
"%d".format((backupStatusData.progress * 100).roundToInt())
)
}
BackupStatusData.RestoreStatus.LOW_BATTERY -> stringResource(R.string.BackupStatus__status_device_has_low_battery)
BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET -> stringResource(R.string.BackupStatus__status_no_internet)
BackupStatusData.RestoreStatus.WAITING_FOR_WIFI -> stringResource(R.string.BackupStatus__status_waiting_for_wifi)
BackupStatusData.RestoreStatus.FINISHED -> stringResource(R.string.BackupStatus__restore_complete)
}
}
@Composable
private fun progressColor(backupStatusData: BackupStatusData): Color {
return when (backupStatusData) {
is BackupStatusData.RestoringMedia -> MaterialTheme.colorScheme.primary
else -> backupStatusData.iconColors.foreground
}
}
@SignalPreview
@Composable
fun BackupStatusRowNormalPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.RestoringMedia(
bytesTotal = ByteSize(100),
bytesDownloaded = ByteSize(50),
restoreStatus = BackupStatusData.RestoreStatus.NORMAL
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowWaitingForWifiPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.RestoringMedia(
bytesTotal = ByteSize(100),
bytesDownloaded = ByteSize(50),
restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_WIFI
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowWaitingForInternetPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.RestoringMedia(
bytesTotal = ByteSize(100),
bytesDownloaded = ByteSize(50),
restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowLowBatteryPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.RestoringMedia(
bytesTotal = ByteSize(100),
bytesDownloaded = ByteSize(50),
restoreStatus = BackupStatusData.RestoreStatus.LOW_BATTERY
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowFinishedPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.RestoringMedia(
bytesTotal = ByteSize(100),
bytesDownloaded = ByteSize(50),
restoreStatus = BackupStatusData.RestoreStatus.FINISHED
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowNotEnoughFreeSpacePreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.NotEnoughFreeSpace(
requiredSpace = ByteSize(50)
)
)
}
}
@SignalPreview
@Composable
fun BackupStatusRowCouldNotCompleteBackupPreview() {
Previews.Preview {
BackupStatusRow(
backupStatusData = BackupStatusData.CouldNotCompleteBackup
)
}
}

View File

@@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.signal.core.util.bytes
import org.signal.core.util.throttleLatest
import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatus
import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusBanner
import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusData
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.database.DatabaseObserver
@@ -71,7 +71,7 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList
@Composable
override fun DisplayBanner(model: BackupStatusData, contentPadding: PaddingValues) {
BackupStatus(
BackupStatusBanner(
data = model,
onSkipClick = listener::onSkip,
onDismissClick = listener::onDismissComplete

View File

@@ -7443,6 +7443,17 @@
<!-- Status subtitle for banner when restoring media. Placeholders are size already restored and total size to restore. e.g., 4.5MB of 100MB -->
<string name="BackupStatus__status_size_of_size">%1$s of %2$s</string>
<!-- BackupStatusRow -->
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Cancel download</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Downloading: %1$s of %2$s (%3$s%%)</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Not enough space to download your Backup. To continue free up %1$s of space.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Skip download</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-F and tap "Back up now" to try again.</string>
<!-- BackupsTypeSettingsFragment -->
<!-- Displayed as the user\'s payment method as a label in a preference row -->