Alert user to file system errors during backup restore.

This commit is contained in:
Nicholas Tinsley 2024-06-13 16:13:43 -04:00
parent 73142cea39
commit 71979b34db
4 changed files with 48 additions and 5 deletions

View file

@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.service.LocalBackupListener
import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.BackupUtil.BackupInfo
import java.io.IOException
/**
@ -28,8 +29,13 @@ import java.io.IOException
object RestoreRepository {
private val TAG = Log.tag(RestoreRepository.javaClass)
suspend fun getLocalBackupFromUri(context: Context, uri: Uri): BackupUtil.BackupInfo? = withContext(Dispatchers.IO) {
BackupUtil.getBackupInfoFromSingleUri(context, uri)
suspend fun getLocalBackupFromUri(context: Context, uri: Uri): BackupInfoResult = withContext(Dispatchers.IO) {
try {
return@withContext BackupInfoResult(backupInfo = BackupUtil.getBackupInfoFromSingleUri(context, uri), failureCause = null, failure = false)
} catch (ex: BackupUtil.BackupFileException) {
Log.w(TAG, "Encountered error while trying to read backup!", ex)
return@withContext BackupInfoResult(backupInfo = null, failureCause = ex, failure = true)
}
}
suspend fun restoreBackupAsynchronously(context: Context, backupFileUri: Uri, passphrase: String): BackupImportResult = withContext(Dispatchers.IO) {
@ -88,4 +94,6 @@ object RestoreRepository {
FAILURE_FOREIGN_KEY,
FAILURE_UNKNOWN
}
data class BackupInfoResult(val backupInfo: BackupInfo?, val failureCause: BackupUtil.BackupFileException?, val failure: Boolean)
}

View file

@ -10,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
@ -29,6 +30,7 @@ import org.thoughtcrime.securesms.registration.fragments.RestoreBackupFragment.P
import org.thoughtcrime.securesms.restore.RestoreActivity
import org.thoughtcrime.securesms.restore.RestoreRepository
import org.thoughtcrime.securesms.restore.RestoreViewModel
import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.ViewModelFactory
@ -74,6 +76,13 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc
return
}
restoreLocalBackupViewModel.backupReadError.observe(viewLifecycleOwner) { fileState ->
fileState?.let {
restoreLocalBackupViewModel.clearBackupFileStateError()
handleBackupFileStateError(it)
}
}
restoreLocalBackupViewModel.uiState.observe(viewLifecycleOwner) { fragmentState ->
fragmentState.backupInfo?.let {
presentBackupFileInfo(backupSize = it.size, backupTimestamp = it.timestamp)
@ -126,6 +135,18 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc
restoreLocalBackupViewModel.onBackupProgressUpdate(event)
}
private fun handleBackupFileStateError(fileState: BackupUtil.BackupFileState) {
@StringRes
val errorResId: Int = when (fileState) {
BackupUtil.BackupFileState.READABLE -> throw AssertionError("Unexpected error state.")
BackupUtil.BackupFileState.NOT_FOUND -> R.string.RestoreBackupFragment__backup_not_found
BackupUtil.BackupFileState.NOT_READABLE -> R.string.RestoreBackupFragment__backup_has_a_bad_extension
BackupUtil.BackupFileState.UNSUPPORTED_FILE_EXTENSION -> R.string.RestoreBackupFragment__backup_could_not_be_read
}
Toast.makeText(requireContext(), errorResId, Toast.LENGTH_LONG).show()
}
private fun handleBackupImportError(importResult: RestoreRepository.BackupImportResult) {
when (importResult) {
RestoreRepository.BackupImportResult.FAILURE_VERSION_DOWNGRADE -> Toast.makeText(requireContext(), R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show()

View file

@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.restore.restorelocalbackup
import android.net.Uri
import org.thoughtcrime.securesms.restore.RestoreRepository
import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.BackupUtil.BackupInfo
/**
@ -15,6 +16,7 @@ import org.thoughtcrime.securesms.util.BackupUtil.BackupInfo
data class RestoreLocalBackupState(
val uri: Uri,
val backupInfo: BackupInfo? = null,
val backupFileStateError: BackupUtil.BackupFileState? = null,
val backupPassphrase: String = "",
val restoreInProgress: Boolean = false,
val backupVerifyingInProgress: Boolean = false,

View file

@ -25,21 +25,29 @@ class RestoreLocalBackupViewModel(fileBackupUri: Uri) : ViewModel() {
private val store = MutableStateFlow(RestoreLocalBackupState(fileBackupUri))
val uiState = store.asLiveData()
val backupReadError = store.map { it.backupFileStateError }.asLiveData()
val backupComplete = store.map { Pair(it.backupRestoreComplete, it.backupImportResult) }.asLiveData()
fun prepareRestore(context: Context) {
val backupFileUri = store.value.uri
viewModelScope.launch {
val backupInfo = RestoreRepository.getLocalBackupFromUri(context, backupFileUri)
val result: RestoreRepository.BackupInfoResult = RestoreRepository.getLocalBackupFromUri(context, backupFileUri)
if (backupInfo == null) {
if (result.failure && result.failureCause != null) {
store.update {
it.copy(
backupFileStateError = result.failureCause.state
)
}
} else if (result.backupInfo == null) {
abort()
return@launch
}
store.update {
it.copy(
backupInfo = backupInfo
backupInfo = result.backupInfo
)
}
}
@ -101,6 +109,10 @@ class RestoreLocalBackupViewModel(fileBackupUri: Uri) : ViewModel() {
}
}
fun clearBackupFileStateError() {
store.update { it.copy(backupFileStateError = null) }
}
companion object {
private val TAG = Log.tag(RestoreLocalBackupViewModel::class.java)
}