Add UI for mismatched backup versions.

This commit is contained in:
Michelle Tang 2025-01-22 10:50:24 -05:00 committed by GitHub
parent ec4fdfa7d3
commit a0cb12be8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 60 additions and 6 deletions

View file

@ -693,11 +693,14 @@ object BackupRepository {
val header = frameReader.getHeader() val header = frameReader.getHeader()
if (header == null) { if (header == null) {
Log.e(TAG, "[import] Backup is missing header!") Log.e(TAG, "[import] Backup is missing header!")
SignalStore.backup.hasInvalidBackupVersion = false
return ImportResult.Failure return ImportResult.Failure
} else if (header.version > VERSION) { } else if (header.version > VERSION) {
Log.e(TAG, "[import] Backup version is newer than we understand: ${header.version}") Log.e(TAG, "[import] Backup version is newer than we understand: ${header.version}")
SignalStore.backup.hasInvalidBackupVersion = true
return ImportResult.Failure return ImportResult.Failure
} }
SignalStore.backup.hasInvalidBackupVersion = false
try { 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. // 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.

View file

@ -12,6 +12,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.RestoreState import org.thoughtcrime.securesms.backup.RestoreState
import org.thoughtcrime.securesms.backup.v2.BackupRepository 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.backup.v2.RestoreV2Event
import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.Job
@ -103,7 +104,10 @@ class BackupRestoreJob private constructor(parameters: Parameters) : BaseJob(par
val self = Recipient.self() val self = Recipient.self()
val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey)) 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 SignalStore.backup.restoreState = RestoreState.RESTORING_MEDIA
} }

View file

@ -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_SHEET_SNOOZE_TIME = "backup.failed.sheet.snooze"
private const val KEY_BACKUP_FAIL_SPACE_REMAINING = "backup.failed.space.remaining" 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_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" 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 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 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 * Denotes how many bytes are still available on the disk for writing. Used to display

View file

@ -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.MessageBackupsTypeFeature
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeatureRow import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeatureRow
import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity
import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.PlayStoreUtil
import org.thoughtcrime.securesms.util.viewModel import org.thoughtcrime.securesms.util.viewModel
import java.util.Locale import java.util.Locale
@ -111,7 +113,10 @@ class RemoteRestoreActivity : BaseActivity() {
finish() finish()
}, },
onErrorDialogDismiss = { viewModel.clearError() } onErrorDialogDismiss = { viewModel.clearError() },
onUpdateSignal = {
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(this)
}
) )
} }
} }
@ -145,7 +150,8 @@ private fun RestoreFromBackupContent(
state: RemoteRestoreViewModel.ScreenState, state: RemoteRestoreViewModel.ScreenState,
onRestoreBackupClick: () -> Unit = {}, onRestoreBackupClick: () -> Unit = {},
onCancelClick: () -> Unit = {}, onCancelClick: () -> Unit = {},
onErrorDialogDismiss: () -> Unit = {} onErrorDialogDismiss: () -> Unit = {},
onUpdateSignal: () -> Unit = {}
) { ) {
when (state.loadState) { when (state.loadState) {
RemoteRestoreViewModel.ScreenState.LoadState.LOADING -> { RemoteRestoreViewModel.ScreenState.LoadState.LOADING -> {
@ -159,7 +165,8 @@ private fun RestoreFromBackupContent(
state = state, state = state,
onRestoreBackupClick = onRestoreBackupClick, onRestoreBackupClick = onRestoreBackupClick,
onCancelClick = onCancelClick, onCancelClick = onCancelClick,
onErrorDialogDismiss = onErrorDialogDismiss onErrorDialogDismiss = onErrorDialogDismiss,
onUpdateSignal = onUpdateSignal
) )
} }
@ -178,7 +185,8 @@ private fun BackupAvailableContent(
state: RemoteRestoreViewModel.ScreenState, state: RemoteRestoreViewModel.ScreenState,
onRestoreBackupClick: () -> Unit, onRestoreBackupClick: () -> Unit,
onCancelClick: () -> Unit, onCancelClick: () -> Unit,
onErrorDialogDismiss: () -> Unit onErrorDialogDismiss: () -> Unit,
onUpdateSignal: () -> Unit
) { ) {
val subtitle = if (state.backupSize.bytes > 0) { val subtitle = if (state.backupSize.bytes > 0) {
stringResource( stringResource(
@ -244,7 +252,13 @@ private fun BackupAvailableContent(
RemoteRestoreViewModel.ImportState.None -> Unit RemoteRestoreViewModel.ImportState.None -> Unit
RemoteRestoreViewModel.ImportState.InProgress -> RestoreProgressDialog(state.restoreProgress) RemoteRestoreViewModel.ImportState.InProgress -> RestoreProgressDialog(state.restoreProgress)
is RemoteRestoreViewModel.ImportState.Restored -> Unit 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() 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()
}
}

View file

@ -1411,6 +1411,14 @@
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string> <string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed --> <!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string> <string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- Dialog title displayed when remote restore failed because of an outdated backup version. -->
<string name="RemoteRestoreActivity__couldnt_restore">Couldn\'t restore this backup</string>
<!-- Dialog message explaining that an update to Signal is required to restore the backup. -->
<string name="RemoteRestoreActivity__update_latest">This version of Signal can\'t restore your backup. Update to the latest version and try again.</string>
<!-- Text on button that when pressed will redirect them to update Signal -->
<string name="RemoteRestoreActivity__update_signal">Update Signal</string>
<!-- Text label button to dismiss the dialog -->
<string name="RemoteRestoreActivity__not_now">Not now</string>
<!-- GroupMentionSettingDialog --> <!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Notify me for Mentions</string> <string name="GroupMentionSettingDialog_notify_me_for_mentions">Notify me for Mentions</string>