From 06191a1fc39ad9a3cfe0a1379e5a3c65f60584ad Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 28 Oct 2024 15:32:39 -0300 Subject: [PATCH] Catch error when loading snapshots for app checking --- .../com/stevesoltys/seedvault/repo/Checker.kt | 28 +++++++++++------ .../seedvault/settings/AppCheckFragment.kt | 8 +++-- .../ui/check/AppCheckResultActivity.kt | 31 +++++++++++++------ app/src/main/res/values/strings.xml | 8 ++--- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt b/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt index e336f5b7..8832fe19 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt @@ -49,20 +49,25 @@ internal class Checker( private set @WorkerThread - suspend fun getBackupSize(): Long { + suspend fun getBackupSize(): Long? { // get all snapshots val folder = TopLevelFolder(crypto.repoId) val handles = mutableListOf() - backendManager.backend.list(folder, AppBackupFileType.Snapshot::class) { fileInfo -> - handles.add(fileInfo.fileHandle as AppBackupFileType.Snapshot) + try { + backendManager.backend.list(folder, AppBackupFileType.Snapshot::class) { fileInfo -> + handles.add(fileInfo.fileHandle as AppBackupFileType.Snapshot) + } + val snapshots = snapshotManager.onSnapshotsLoaded(handles) + this.snapshots = snapshots // remember loaded snapshots + this.handleSize = handles.size // remember number of snapshot handles we had + } catch (e: Exception) { + log.error(e) { "Error loading snapshots: " } + // we swallow this exception, because an error will be shown in the next step + return null } - val snapshots = snapshotManager.onSnapshotsLoaded(handles) - this.snapshots = snapshots // remember loaded snapshots - this.handleSize = handles.size // remember number of snapshot handles we had - // get total disk space used by snapshots val sizeMap = mutableMapOf() - snapshots.forEach { snapshot -> + snapshots?.forEach { snapshot -> // add sizes to a map first, so we don't double count snapshot.blobsMap.forEach { (chunkId, blob) -> sizeMap[chunkId] = blob.length } } @@ -73,7 +78,12 @@ internal class Checker( suspend fun check(percent: Int) { check(percent in 0..100) { "Percent $percent out of bounds." } - if (snapshots == null) getBackupSize() // just get size again to be sure we get snapshots + if (snapshots == null) try { + getBackupSize() // just get size again to be sure we get snapshots + } catch (e: Exception) { + nm.onCheckFinishedWithError(0, 0) + checkerResult = CheckerResult.GeneralError(e) + } val snapshots = snapshots ?: error("Snapshots still null") val handleSize = handleSize ?: error("Handle size still null") check(handleSize >= snapshots.size) { diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt index 6512b1cd..cea46c86 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt @@ -50,9 +50,11 @@ class AppCheckFragment : Fragment() { } viewModel.backupSize.observe(viewLifecycleOwner) { - slider.labelBehavior = LABEL_VISIBLE - slider.invalidate() - onSliderChanged(slider.value) + if (it != null) { + slider.labelBehavior = LABEL_VISIBLE + slider.invalidate() + onSliderChanged(slider.value) + } // we can stop observing as the loaded size won't change again viewModel.backupSize.removeObservers(viewLifecycleOwner) } diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/check/AppCheckResultActivity.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/check/AppCheckResultActivity.kt index 922ba469..a1ac94cc 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/check/AppCheckResultActivity.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/check/AppCheckResultActivity.kt @@ -56,9 +56,14 @@ class AppCheckResultActivity : BackupActivity() { is CheckerResult.Success -> onSuccess(result) is CheckerResult.Error -> onError(result) is CheckerResult.GeneralError, null -> { - // TODO - if (result == null) log.error { "No more result" } - else log.info((result as CheckerResult.GeneralError).e) { "Error: " } + if (result == null) { + val str = getString(R.string.backup_app_check_error_no_result) + val e = NullPointerException(str) + val r = CheckerResult.GeneralError(e) + onGeneralError(r) + } else { + onGeneralError(result as CheckerResult.GeneralError) + } } } checker.clear() @@ -98,14 +103,8 @@ class AppCheckResultActivity : BackupActivity() { R.string.backup_app_check_error_only_broken_snapshots, result.existingSnapshots, ) - } else if (result.existingSnapshots > result.snapshots.size) { - getString( - R.string.backup_app_check_error_some_snapshots, - result.existingSnapshots, - result.snapshots.size, - ) } else { - getString(R.string.backup_app_check_error_read_all_snapshots, result.snapshots.size) + getString(R.string.backup_app_check_error_has_snapshots, result.existingSnapshots) } requireViewById(R.id.introView).text = intro @@ -121,4 +120,16 @@ class AppCheckResultActivity : BackupActivity() { ) } + private fun onGeneralError(result: CheckerResult.GeneralError) { + setContentView(R.layout.activity_check_result) + requireViewById(R.id.imageView).setImageResource(R.drawable.ic_cloud_error) + requireViewById(R.id.titleView).setText(R.string.backup_app_check_error_title) + + requireViewById(R.id.introView).text = + getString(R.string.backup_app_check_error_no_snapshots) + + requireViewById(R.id.disclaimerView).text = + "${result.e.localizedMessage}\n\n${result.e}" + } + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e11e0eb9..5e66f82a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -196,12 +196,12 @@ Details %1$d snapshots were found and %2$d%% of their data (%3$s) successfully verified: - Note: We can not verify whether apps include all of their data in the backup. + Note: we cannot verify whether individual apps include all of their user data in the backup. @string/notification_checking_error_title We could not find any backup. Please run a successful backup first and then try checking again. We found %1$d backup snapshots. However, all of them were corrupted. Please run a successful backup and then try checking again. - We found %1$d backup snapshots. However, we could only read the following %2$d snapshots. - We found %1$d backup snapshots. However, some had errors and can not be fully restored. + We found %1$d backup snapshots, some of them are corrupt or have errors. Below are the backups that could be restored. + We lost the detailed results. Did a long time pass since running the check? Try running again. @@ -235,7 +235,7 @@ Has user data for %1$d apps Contains %1$d apps (%2$s) Contains %1$d apps - Can not be (fully) restored. + This backup has some errors. You may be able to restore it partly. Don\'t restore Skip restoring apps No backups found