diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt index a919d0a279..60b8d6407e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt @@ -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) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt index ff77c7aeff..2e46bbf11a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupFragment.kt @@ -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() diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt index fbf2305fe0..f62af42146 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupState.kt @@ -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, diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt index 9b33c4f8e4..ee6cc891a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/restorelocalbackup/RestoreLocalBackupViewModel.kt @@ -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) }