diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt index f7a2073e6c..6f861efb00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt @@ -104,6 +104,10 @@ object ArchiveUploadProgress { updateState(PROGRESS_NONE) } + fun onValidationFailure() { + updateState(PROGRESS_NONE) + } + private fun updateState(state: ArchiveUploadProgressState, notify: Boolean = true) { uploadProgress = state SignalStore.backup.archiveUploadState = state diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 9c57ebabc7..4515e752a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -204,14 +204,27 @@ object BackupRepository { } /** - * Whether the "Could not complete backup" row should be displayed in settings. + * Whether the "Backup Failed" row should be displayed in settings. + * Shown when the initial backup creation has failed */ fun shouldDisplayBackupFailedSettingsRow(): Boolean { if (shouldNotDisplayBackupFailedMessaging()) { return false } - return SignalStore.backup.hasBackupFailure + return !SignalStore.backup.hasBackupBeenUploaded && SignalStore.backup.hasBackupFailure + } + + /** + * Whether the "Could not complete backup" row should be displayed in settings. + * Shown when a new backup could not be created but there is an existing one already + */ + fun shouldDisplayCouldNotCompleteBackupSettingsRow(): Boolean { + if (shouldNotDisplayBackupFailedMessaging()) { + return false + } + + return SignalStore.backup.hasBackupBeenUploaded && SignalStore.backup.hasBackupFailure } /** @@ -230,7 +243,8 @@ object BackupRepository { } /** - * Whether or not the "Could not complete backup" sheet should be displayed. + * Whether or not the "Backup failed" sheet should be displayed. + * Should only be displayed if this is the failure of the initial backup creation. */ @JvmStatic fun shouldDisplayBackupFailedSheet(): Boolean { @@ -238,7 +252,19 @@ object BackupRepository { return false } - return System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime + return !SignalStore.backup.hasBackupBeenUploaded && System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime + } + + /** + * Whether or not the "Could not complete backup" sheet should be displayed. + */ + @JvmStatic + fun shouldDisplayCouldNotCompleteBackupSheet(): Boolean { + if (shouldNotDisplayBackupFailedMessaging()) { + return false + } + + return SignalStore.backup.hasBackupBeenUploaded && System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime } fun snoozeYourMediaWillBeDeletedTodaySheet() { @@ -249,7 +275,7 @@ object BackupRepository { * Whether or not the "Your media will be deleted today" sheet should be displayed. */ suspend fun shouldDisplayYourMediaWillBeDeletedTodaySheet(): Boolean { - if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.optimizeStorage) { + if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.hasBackupBeenUploaded || !SignalStore.backup.optimizeStorage) { return false } @@ -285,7 +311,7 @@ object BackupRepository { } private fun shouldNotDisplayBackupFailedMessaging(): Boolean { - return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled || !SignalStore.backup.hasBackupBeenUploaded + return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index 436c410271..44070c7671 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -57,6 +57,8 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.payments.FiatMoneyUtil +import org.thoughtcrime.securesms.util.CommunicationActions +import org.thoughtcrime.securesms.util.PlayStoreUtil import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.milliseconds @@ -67,6 +69,8 @@ import org.signal.core.ui.R as CoreUiR */ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { + override val peekHeightPercentage: Float = 0.75f + companion object { private const val ARG_ALERT = "alert" @@ -126,6 +130,8 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { } is BackupAlert.DiskFull -> Unit + is BackupAlert.BackupFailed -> + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()) } dismissAllowingStateLoss() @@ -144,6 +150,8 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { is BackupAlert.DiskFull -> { displaySkipRestoreDialog() } + // TODO [backups] - Update support URL with backups page + BackupAlert.BackupFailed -> CommunicationActions.openBrowserLink(requireContext(), requireContext().getString(R.string.backup_support_url)) } dismissAllowingStateLoss() @@ -153,7 +161,7 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { super.onDismiss(dialog) when (backupAlert) { - is BackupAlert.CouldNotCompleteBackup -> BackupRepository.markBackupFailedSheetDismissed() + is BackupAlert.CouldNotCompleteBackup, BackupAlert.BackupFailed -> BackupRepository.markBackupFailedSheetDismissed() is BackupAlert.MediaWillBeDeletedToday -> BackupRepository.snoozeYourMediaWillBeDeletedTodaySheet() else -> Unit } @@ -261,6 +269,7 @@ private fun BackupAlertSheetContent( is BackupAlert.MediaBackupsAreOff -> MediaBackupsAreOffBody(backupAlert.endOfPeriodSeconds, mediaTtl) BackupAlert.MediaWillBeDeletedToday -> MediaWillBeDeletedTodayBody() is BackupAlert.DiskFull -> DiskFullBody(requiredSpace = backupAlert.requiredSpace) + BackupAlert.BackupFailed -> BackupFailedBody() } val secondaryActionResource = rememberSecondaryActionResource(backupAlert = backupAlert) @@ -366,12 +375,22 @@ private fun DiskFullBody(requiredSpace: String) { ) } +@Composable +private fun BackupFailedBody() { + Text( + text = stringResource(id = R.string.BackupAlertBottomSheet__an_error_occurred), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = 36.dp) + ) +} + @Composable private fun rememberBackupsIconColors(backupAlert: BackupAlert): BackupsIconColors { return remember(backupAlert) { when (backupAlert) { BackupAlert.FailedToRenew, is BackupAlert.MediaBackupsAreOff -> error("Not icon-based options.") - is BackupAlert.CouldNotCompleteBackup, is BackupAlert.DiskFull -> BackupsIconColors.Warning + is BackupAlert.CouldNotCompleteBackup, BackupAlert.BackupFailed, is BackupAlert.DiskFull -> BackupsIconColors.Warning BackupAlert.MediaWillBeDeletedToday -> BackupsIconColors.Error } } @@ -385,6 +404,7 @@ private fun titleString(backupAlert: BackupAlert): String { is BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__your_backups_subscription_expired) BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__your_media_will_be_deleted_today) is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__free_up_s_on_this_device, backupAlert.requiredSpace) + BackupAlert.BackupFailed -> stringResource(R.string.BackupAlertBottomSheet__backup_failed) } } @@ -399,6 +419,7 @@ private fun primaryActionString( is BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__subscribe_for_s_month, pricePerMonth) BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__download_media_now) is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__got_it) + is BackupAlert.BackupFailed -> stringResource(R.string.BackupAlertBottomSheet__check_for_update) } } @@ -411,6 +432,7 @@ private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int { is BackupAlert.MediaBackupsAreOff -> R.string.BackupAlertBottomSheet__not_now BackupAlert.MediaWillBeDeletedToday -> R.string.BackupAlertBottomSheet__dont_download_media is BackupAlert.DiskFull -> R.string.BackupAlertBottomSheet__skip_restore + is BackupAlert.BackupFailed -> R.string.BackupAlertBottomSheet__learn_more } } } @@ -471,6 +493,17 @@ private fun BackupAlertSheetContentPreviewDiskFull() { } } +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewBackupFailed() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.BackupFailed, + mediaTtl = 60.days + ) + } +} + /** * All necessary information to display the sheet should be handed in through the specific alert. */ @@ -485,6 +518,12 @@ sealed class BackupAlert : Parcelable { val daysSinceLastBackup: Int ) : BackupAlert() + /** + * This value is driven by the same watermarking system for [CouldNotCompleteBackup] so that only one of these sheets is shown by the system + * This value is driven by failure to complete the initial backup. + */ + data object BackupFailed : BackupAlert() + /** * This value is driven by InAppPayment state, and will be automatically cleared when the sheet is displayed. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt index 18b7249b80..f6057c6840 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt @@ -22,11 +22,11 @@ object BackupAlertDelegate { lifecycle.coroutineScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { if (BackupRepository.shouldDisplayBackupFailedSheet()) { + BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(fragmentManager, null) + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSheet()) { BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null) - } - - if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { - BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday) + } else if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { + BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday).show(fragmentManager, null) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt index 2e00cf5d81..b69594ff8e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt @@ -195,6 +195,12 @@ fun BackupStatusBannerPreview() { BackupStatusBanner( data = BackupStatusData.CouldNotCompleteBackup ) + + HorizontalDivider() + + BackupStatusBanner( + data = BackupStatusData.BackupFailed + ) } } } @@ -235,6 +241,19 @@ sealed interface BackupStatusData { override val iconColors: BackupsIconColors = BackupsIconColors.Warning } + /** + * Initial backup creation failure + */ + data object BackupFailed : BackupStatusData { + override val iconRes: Int = R.drawable.symbol_backup_error_24 + + override val title: String + @Composable + get() = stringResource(androidx.biometric.R.string.default_error_msg) + + override val iconColors: BackupsIconColors = BackupsIconColors.Warning + } + /** * User does not have enough space on their device to complete backup restoration */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt index 4dd17140f2..d39f831a5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt @@ -20,17 +20,19 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue 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.LinkAnnotation import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.PlaceholderVerticalAlign +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withLink import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.signal.core.ui.Previews @@ -48,10 +50,13 @@ import org.signal.core.ui.R as CoreUiR fun BackupStatusRow( backupStatusData: BackupStatusData, onSkipClick: () -> Unit = {}, - onCancelClick: () -> Unit = {} + onCancelClick: () -> Unit = {}, + onLearnMoreClick: () -> Unit = {} ) { Column { - if (backupStatusData !is BackupStatusData.CouldNotCompleteBackup) { + if (backupStatusData !is BackupStatusData.CouldNotCompleteBackup && + backupStatusData !is BackupStatusData.BackupFailed + ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) @@ -120,6 +125,40 @@ fun BackupStatusRow( modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) ) } + BackupStatusData.BackupFailed -> { + 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_latest_version)) + append(" ") + withLink( + LinkAnnotation.Clickable( + stringResource(R.string.BackupStatusRow__learn_more), + styles = TextLinkStyles(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) + ) { + onLearnMoreClick() + } + ) { + append(stringResource(R.string.BackupStatusRow__learn_more)) + } + }, + inlineContent = inlineContentMap, + modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) + ) + } } } } @@ -241,3 +280,13 @@ fun BackupStatusRowCouldNotCompleteBackupPreview() { ) } } + +@SignalPreview +@Composable +fun BackupStatusRowBackupFailedPreview() { + Previews.Preview { + BackupStatusRow( + backupStatusData = BackupStatusData.BackupFailed + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index 2e6c21768c..ba77211323 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -215,7 +215,7 @@ private fun AppSettingsContent( } } - BackupFailureState.COULD_NOT_COMPLETE_BACKUP -> { + BackupFailureState.BACKUP_FAILED, BackupFailureState.COULD_NOT_COMPLETE_BACKUP -> { item { Dividers.Default() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt index 8b30532c5f..7d0ba2ddfa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt @@ -72,6 +72,8 @@ class AppSettingsViewModel : ViewModel() { private fun getBackupFailureState(): BackupFailureState { return if (BackupRepository.shouldDisplayBackupFailedSettingsRow()) { + BackupFailureState.BACKUP_FAILED + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSettingsRow()) { BackupFailureState.COULD_NOT_COMPLETE_BACKUP } else if (SignalStore.backup.subscriptionStateMismatchDetected) { BackupFailureState.SUBSCRIPTION_STATE_MISMATCH diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt index d971b2d322..c3b3666d09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt @@ -10,6 +10,7 @@ package org.thoughtcrime.securesms.components.settings.app */ enum class BackupFailureState { NONE, + BACKUP_FAILED, COULD_NOT_COMPLETE_BACKUP, SUBSCRIPTION_STATE_MISMATCH } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index 671dc83bb6..873dffc941 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -83,6 +83,8 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlert +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlertBottomSheet import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusData import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusRow import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType @@ -233,6 +235,10 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { requireActivity().finish() requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.REMOTE_BACKUPS_INDEX)) } + + override fun onLearnMoreAboutBackupFailure() { + BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(parentFragmentManager, null) + } } private fun displayBackupKey() { @@ -314,6 +320,7 @@ private interface ContentCallbacks { fun onRenewLostSubscription() = Unit fun onLearnMoreAboutLostSubscription() = Unit fun onContactSupport() = Unit + fun onLearnMoreAboutBackupFailure() = Unit } @Composable @@ -392,7 +399,8 @@ private fun RemoteBackupsSettingsContent( BackupStatusRow( backupStatusData = backupRestoreState.backupStatusData, onCancelClick = contentCallbacks::onCancelMediaRestore, - onSkipClick = contentCallbacks::onSkipMediaRestore + onSkipClick = contentCallbacks::onSkipMediaRestore, + onLearnMoreClick = contentCallbacks::onLearnMoreAboutBackupFailure ) } } else if (backupRestoreState is BackupRestoreState.Ready && backupState is RemoteBackupsSettingsState.BackupState.Canceled) { @@ -420,7 +428,8 @@ private fun RemoteBackupsSettingsContent( BackupStatusRow( backupStatusData = backupRestoreState.backupStatusData, onCancelClick = contentCallbacks::onCancelMediaRestore, - onSkipClick = contentCallbacks::onSkipMediaRestore + onSkipClick = contentCallbacks::onSkipMediaRestore, + onLearnMoreClick = contentCallbacks::onLearnMoreAboutBackupFailure ) } } @@ -920,8 +929,14 @@ private fun InProgressBackupRow( ) } + val inProgressText = if (totalProgress == null || totalProgress == 0) { + stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup) + } else { + stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, progress ?: 0, totalProgress) + } + Text( - text = stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, progress ?: 0, totalProgress ?: 0), + text = inProgressText, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt index e2852c2deb..915af9618a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt @@ -92,6 +92,8 @@ class RemoteBackupsSettingsViewModel : ViewModel() { } else if (SignalStore.backup.totalRestorableAttachmentSize > 0L) { _restoreState.update { BackupRestoreState.Ready(SignalStore.backup.totalRestorableAttachmentSize.bytes.toUnitString()) } } else if (BackupRepository.shouldDisplayBackupFailedSettingsRow()) { + _restoreState.update { BackupRestoreState.FromBackupStatusData(BackupStatusData.BackupFailed) } + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSettingsRow()) { _restoreState.update { BackupRestoreState.FromBackupStatusData(BackupStatusData.CouldNotCompleteBackup) } } else { _restoreState.update { BackupRestoreState.None } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt index 0516e1af7d..f2c06ba2e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -101,8 +101,8 @@ class BackupMessagesJob private constructor(parameters: Parameters) : Job(parame return Result.retry(defaultBackoff()) } is ArchiveValidator.ValidationResult.ValidationError -> { - // TODO [backup] UX Log.w(TAG, "The backup file fails validation! Message: " + result.exception.message) + ArchiveUploadProgress.onValidationFailure() return Result.failure() } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ba143f27c..fe4257b44c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7504,6 +7504,12 @@ Skip restore? If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7536,7 +7542,11 @@ Skip download - Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-F and tap "Back up now" to try again. + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7740,6 +7750,8 @@ Renew Learn more + + Processing backup… You have %1$s of backup data that’s not on this device. Your backup will be deleted when your subscription ends in %2$d day.