From a0cb12be8c08cc86c3f1ad53a9ddee4138e1eb5f Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Wed, 22 Jan 2025 10:50:24 -0500 Subject: [PATCH] Add UI for mismatched backup versions. --- .../securesms/backup/v2/BackupRepository.kt | 3 ++ .../securesms/jobs/BackupRestoreJob.kt | 6 ++- .../securesms/keyvalue/BackupValues.kt | 2 + .../ui/restore/RemoteRestoreActivity.kt | 47 +++++++++++++++++-- app/src/main/res/values/strings.xml | 8 ++++ 5 files changed, 60 insertions(+), 6 deletions(-) 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 24499263f3..2c1b96df0f 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 @@ -693,11 +693,14 @@ object BackupRepository { val header = frameReader.getHeader() if (header == null) { Log.e(TAG, "[import] Backup is missing header!") + SignalStore.backup.hasInvalidBackupVersion = false return ImportResult.Failure } else if (header.version > VERSION) { Log.e(TAG, "[import] Backup version is newer than we understand: ${header.version}") + SignalStore.backup.hasInvalidBackupVersion = true return ImportResult.Failure } + SignalStore.backup.hasInvalidBackupVersion = false try { // Removing all the data from the various tables is *very* expensive (i.e. can take *several* minutes) if we don't do some pre-work. diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt index 1f60bb4416..d31f9480b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRestoreJob.kt @@ -12,6 +12,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.RestoreState import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.backup.v2.ImportResult import org.thoughtcrime.securesms.backup.v2.RestoreV2Event import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job @@ -103,7 +104,10 @@ class BackupRestoreJob private constructor(parameters: Parameters) : BaseJob(par val self = Recipient.self() val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey)) - BackupRepository.import(length = tempBackupFile.length(), inputStreamFactory = tempBackupFile::inputStream, selfData = selfData, backupKey = SignalStore.backup.messageBackupKey, cancellationSignal = { isCanceled }) + val result = BackupRepository.import(length = tempBackupFile.length(), inputStreamFactory = tempBackupFile::inputStream, selfData = selfData, backupKey = SignalStore.backup.messageBackupKey, cancellationSignal = { isCanceled }) + if (result == ImportResult.Failure) { + throw IOException("Failed to import backup") + } SignalStore.backup.restoreState = RestoreState.RESTORING_MEDIA } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt index 89c1a49e6b..58d26ab470 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -65,6 +65,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_BACKUP_FAIL_SHEET_SNOOZE_TIME = "backup.failed.sheet.snooze" private const val KEY_BACKUP_FAIL_SPACE_REMAINING = "backup.failed.space.remaining" private const val KEY_BACKUP_ALREADY_REDEEMED = "backup.already.redeemed" + private const val KEY_INVALID_BACKUP_VERSION = "backup.invalid.version" private const val KEY_USER_MANUALLY_SKIPPED_MEDIA_RESTORE = "backup.user.manually.skipped.media.restore" @@ -217,6 +218,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { val nextBackupFailureSheetSnoozeTime: Duration get() = getLong(KEY_BACKUP_FAIL_SHEET_SNOOZE_TIME, getNextBackupFailureSheetSnoozeTime(lastBackupTime.milliseconds).inWholeMilliseconds).milliseconds var hasBackupAlreadyRedeemedError: Boolean by booleanValue(KEY_BACKUP_ALREADY_REDEEMED, false) + var hasInvalidBackupVersion: Boolean by booleanValue(KEY_INVALID_BACKUP_VERSION, false) /** * Denotes how many bytes are still available on the disk for writing. Used to display diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt index 39b0222403..212660a849 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt @@ -55,9 +55,11 @@ import org.thoughtcrime.securesms.backup.v2.RestoreV2Event import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeature import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeatureRow import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.PlayStoreUtil import org.thoughtcrime.securesms.util.viewModel import java.util.Locale @@ -111,7 +113,10 @@ class RemoteRestoreActivity : BaseActivity() { finish() }, - onErrorDialogDismiss = { viewModel.clearError() } + onErrorDialogDismiss = { viewModel.clearError() }, + onUpdateSignal = { + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(this) + } ) } } @@ -145,7 +150,8 @@ private fun RestoreFromBackupContent( state: RemoteRestoreViewModel.ScreenState, onRestoreBackupClick: () -> Unit = {}, onCancelClick: () -> Unit = {}, - onErrorDialogDismiss: () -> Unit = {} + onErrorDialogDismiss: () -> Unit = {}, + onUpdateSignal: () -> Unit = {} ) { when (state.loadState) { RemoteRestoreViewModel.ScreenState.LoadState.LOADING -> { @@ -159,7 +165,8 @@ private fun RestoreFromBackupContent( state = state, onRestoreBackupClick = onRestoreBackupClick, onCancelClick = onCancelClick, - onErrorDialogDismiss = onErrorDialogDismiss + onErrorDialogDismiss = onErrorDialogDismiss, + onUpdateSignal = onUpdateSignal ) } @@ -178,7 +185,8 @@ private fun BackupAvailableContent( state: RemoteRestoreViewModel.ScreenState, onRestoreBackupClick: () -> Unit, onCancelClick: () -> Unit, - onErrorDialogDismiss: () -> Unit + onErrorDialogDismiss: () -> Unit, + onUpdateSignal: () -> Unit ) { val subtitle = if (state.backupSize.bytes > 0) { stringResource( @@ -244,7 +252,13 @@ private fun BackupAvailableContent( RemoteRestoreViewModel.ImportState.None -> Unit RemoteRestoreViewModel.ImportState.InProgress -> RestoreProgressDialog(state.restoreProgress) is RemoteRestoreViewModel.ImportState.Restored -> Unit - RemoteRestoreViewModel.ImportState.Failed -> RestoreFailedDialog(onDismiss = onErrorDialogDismiss) + RemoteRestoreViewModel.ImportState.Failed -> { + if (SignalStore.backup.hasInvalidBackupVersion) { + InvalidBackupVersionDialog(onUpdateSignal = onUpdateSignal, onDismiss = onErrorDialogDismiss) + } else { + RestoreFailedDialog(onDismiss = onErrorDialogDismiss) + } + } } } } @@ -409,3 +423,26 @@ private fun RestoreFailedDialogPreview() { RestoreFailedDialog() } } + +@Composable +fun InvalidBackupVersionDialog( + onUpdateSignal: () -> Unit = {}, + onDismiss: () -> Unit = {} +) { + Dialogs.SimpleAlertDialog( + title = stringResource(R.string.RemoteRestoreActivity__couldnt_restore), + body = stringResource(R.string.RemoteRestoreActivity__update_latest), + confirm = stringResource(R.string.RemoteRestoreActivity__update_signal), + onConfirm = onUpdateSignal, + dismiss = stringResource(R.string.RemoteRestoreActivity__not_now), + onDismiss = onDismiss + ) +} + +@SignalPreview +@Composable +private fun InvalidBackupVersionDialogPreview() { + Previews.Preview { + InvalidBackupVersionDialog() + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2c69a8cb4f..91bc4560b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1411,6 +1411,14 @@ Couldn\'t finish transfer An error occurred and your account couldn’t be transferred. Try again by choosing your transfer method. + + Couldn\'t restore this backup + + This version of Signal can\'t restore your backup. Update to the latest version and try again. + + Update Signal + + Not now Notify me for Mentions