From f67c1d5544d1452e4c2a83ac7945190fb680c10f Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 14 Oct 2024 10:32:50 -0300 Subject: [PATCH 01/20] Replace deprecated sharedViewModel by activityViewModel --- .../com/stevesoltys/seedvault/restore/AppSelectionFragment.kt | 4 ++-- .../stevesoltys/seedvault/restore/FilesSelectionFragment.kt | 4 ++-- .../stevesoltys/seedvault/restore/RecycleBackupFragment.kt | 4 ++-- .../com/stevesoltys/seedvault/restore/RestoreFilesFragment.kt | 4 ++-- .../stevesoltys/seedvault/restore/RestoreProgressFragment.kt | 4 ++-- .../com/stevesoltys/seedvault/restore/RestoreSetFragment.kt | 4 ++-- .../seedvault/restore/install/InstallProgressFragment.kt | 4 ++-- .../com/stevesoltys/seedvault/settings/AppStatusFragment.kt | 4 ++-- .../stevesoltys/seedvault/settings/ExpertSettingsFragment.kt | 4 ++-- .../com/stevesoltys/seedvault/settings/SchedulingFragment.kt | 4 ++-- .../com/stevesoltys/seedvault/settings/SettingsFragment.kt | 4 ++-- .../seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt | 4 ++-- .../seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/AppSelectionFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/AppSelectionFragment.kt index d19cbacd..fcf2be16 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/AppSelectionFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/AppSelectionFragment.kt @@ -13,11 +13,11 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.checkbox.MaterialCheckBox import com.stevesoltys.seedvault.R -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class AppSelectionFragment : Fragment() { - private val viewModel: RestoreViewModel by sharedViewModel() + private val viewModel: RestoreViewModel by activityViewModel() private val layoutManager = LinearLayoutManager(context) private val adapter = AppSelectionAdapter(lifecycleScope, this::loadIcon) { item -> diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/FilesSelectionFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/FilesSelectionFragment.kt index 656be0a9..5fd3923b 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/FilesSelectionFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/FilesSelectionFragment.kt @@ -14,11 +14,11 @@ import android.widget.Button import com.stevesoltys.seedvault.R import org.calyxos.backup.storage.ui.restore.FileSelectionFragment import org.calyxos.backup.storage.ui.restore.FilesItem -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel internal class FilesSelectionFragment : FileSelectionFragment() { - override val viewModel: RestoreViewModel by sharedViewModel() + override val viewModel: RestoreViewModel by activityViewModel() private lateinit var button: Button override fun onCreateView( diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RecycleBackupFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RecycleBackupFragment.kt index cf054368..8c85f144 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RecycleBackupFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RecycleBackupFragment.kt @@ -13,11 +13,11 @@ import android.widget.Button import android.widget.TextView import androidx.fragment.app.Fragment import com.stevesoltys.seedvault.R -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class RecycleBackupFragment : Fragment() { - private val viewModel: RestoreViewModel by sharedViewModel() + private val viewModel: RestoreViewModel by activityViewModel() override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreFilesFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreFilesFragment.kt index e3e9187b..a35cae7f 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreFilesFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreFilesFragment.kt @@ -17,10 +17,10 @@ import androidx.fragment.app.Fragment import com.stevesoltys.seedvault.R import org.calyxos.backup.storage.api.SnapshotItem import org.calyxos.backup.storage.ui.restore.SnapshotFragment -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel internal class RestoreFilesFragment : SnapshotFragment() { - override val viewModel: RestoreViewModel by sharedViewModel() + override val viewModel: RestoreViewModel by activityViewModel() override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt index 1f32eb76..32f7b3ff 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt @@ -22,11 +22,11 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.ui.AppBackupState.FAILED_NOT_INSTALLED -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class RestoreProgressFragment : Fragment() { - private val viewModel: RestoreViewModel by sharedViewModel() + private val viewModel: RestoreViewModel by activityViewModel() private val layoutManager = LinearLayoutManager(context) private val adapter = RestoreProgressAdapter(lifecycleScope, this::loadIcon) diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetFragment.kt index c445125f..f7701302 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetFragment.kt @@ -18,11 +18,11 @@ import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.transport.restore.RestorableBackup -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class RestoreSetFragment : Fragment() { - private val viewModel: RestoreViewModel by sharedViewModel() + private val viewModel: RestoreViewModel by activityViewModel() private lateinit var listView: RecyclerView private lateinit var progressBar: ProgressBar diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt index 014f198c..829c4b0b 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt @@ -26,11 +26,11 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.restore.RestoreViewModel -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class InstallProgressFragment : Fragment(), InstallItemListener { - private val viewModel: RestoreViewModel by sharedViewModel() + private val viewModel: RestoreViewModel by activityViewModel() private val layoutManager = LinearLayoutManager(context) private val adapter = InstallProgressAdapter(lifecycleScope, this::loadIcon, this) diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/AppStatusFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/AppStatusFragment.kt index dc91cb96..8b8126c8 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/AppStatusFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/AppStatusFragment.kt @@ -18,7 +18,7 @@ import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.stevesoltys.seedvault.R -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel internal interface AppStatusToggleListener { fun onAppStatusToggled(status: AppStatus) @@ -26,7 +26,7 @@ internal interface AppStatusToggleListener { class AppStatusFragment : Fragment(), AppStatusToggleListener { - private val viewModel: SettingsViewModel by sharedViewModel() + private val viewModel: SettingsViewModel by activityViewModel() private val layoutManager = LinearLayoutManager(context) private val adapter = AppStatusAdapter(this) diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt index 8a3f1de7..879a5acf 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt @@ -20,11 +20,11 @@ import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.transport.backup.PackageService import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class ExpertSettingsFragment : PreferenceFragmentCompat() { - private val viewModel: SettingsViewModel by sharedViewModel() + private val viewModel: SettingsViewModel by activityViewModel() private val packageService: PackageService by inject() private val backupManager: IBackupManager by inject() diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt index e30556e6..3ebe7e41 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt @@ -21,12 +21,12 @@ import com.stevesoltys.seedvault.backend.BackendManager import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.settings.preference.M3ListPreference import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class SchedulingFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { - private val viewModel: SettingsViewModel by sharedViewModel() + private val viewModel: SettingsViewModel by activityViewModel() private val settingsManager: SettingsManager by inject() private val backendManager: BackendManager by inject() diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt index c33a47d3..26d2bc52 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt @@ -31,14 +31,14 @@ import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.toRelativeTime import org.calyxos.seedvault.core.backends.BackendProperties import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel import java.util.concurrent.TimeUnit private val TAG = SettingsFragment::class.java.name class SettingsFragment : PreferenceFragmentCompat() { - private val viewModel: SettingsViewModel by sharedViewModel() + private val viewModel: SettingsViewModel by activityViewModel() private val backendManager: BackendManager by inject() private val backupStateManager: BackupStateManager by inject() private val backupManager: IBackupManager by inject() diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt index 77ec609d..72f8a62b 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt @@ -39,14 +39,14 @@ import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.google.android.material.textfield.TextInputLayout import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.isDebugBuild -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel import java.util.Locale internal const val ARG_FOR_NEW_CODE = "forStoringNewCode" class RecoveryCodeInputFragment : Fragment() { - private val viewModel: RecoveryCodeViewModel by sharedViewModel() + private val viewModel: RecoveryCodeViewModel by activityViewModel() private lateinit var introText: TextView private lateinit var doneButton: Button diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt index 9c8263ce..c618a164 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt @@ -18,11 +18,11 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.isDebugBuild -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class RecoveryCodeOutputFragment : Fragment() { - private val viewModel: RecoveryCodeViewModel by sharedViewModel() + private val viewModel: RecoveryCodeViewModel by activityViewModel() private lateinit var wordList: RecyclerView private lateinit var confirmCodeButton: Button From 060bb425da7ce15c1c5eab5b642450d37ef7cb01 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 15 Oct 2024 15:52:40 -0300 Subject: [PATCH 02/20] UI for checking app backups --- .../java/com/stevesoltys/seedvault/App.kt | 1 + .../com/stevesoltys/seedvault/repo/Checker.kt | 39 +++++++ .../stevesoltys/seedvault/repo/RepoModule.kt | 1 + .../seedvault/settings/AppCheckFragment.kt | 87 +++++++++++++++ .../seedvault/settings/SettingsViewModel.kt | 11 ++ app/src/main/res/drawable/ic_cloud_search.xml | 10 ++ .../main/res/layout/fragment_app_check.xml | 104 ++++++++++++++++++ app/src/main/res/values/strings.xml | 8 ++ app/src/main/res/xml/settings.xml | 7 ++ 9 files changed, 268 insertions(+) create mode 100644 app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt create mode 100644 app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt create mode 100644 app/src/main/res/drawable/ic_cloud_search.xml create mode 100644 app/src/main/res/layout/fragment_app_check.xml diff --git a/app/src/main/java/com/stevesoltys/seedvault/App.kt b/app/src/main/java/com/stevesoltys/seedvault/App.kt index eac57f83..02b31b0c 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/App.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/App.kt @@ -90,6 +90,7 @@ open class App : Application() { storageBackup = get(), backupManager = get(), backupStateManager = get(), + checker = get(), ) } viewModel { diff --git a/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt b/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt new file mode 100644 index 00000000..ffcdf627 --- /dev/null +++ b/app/src/main/java/com/stevesoltys/seedvault/repo/Checker.kt @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024 The Calyx Institute + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.stevesoltys.seedvault.repo + +import androidx.annotation.WorkerThread +import com.stevesoltys.seedvault.backend.BackendManager +import com.stevesoltys.seedvault.crypto.Crypto +import org.calyxos.seedvault.core.backends.AppBackupFileType +import org.calyxos.seedvault.core.backends.TopLevelFolder + +@WorkerThread +internal class Checker( + private val crypto: Crypto, + private val backendManager: BackendManager, + private val snapshotManager: SnapshotManager, +) { + + 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) + } + val snapshots = snapshotManager.onSnapshotsLoaded(handles) + + // get total disk space used by snapshots + val sizeMap = mutableMapOf() + snapshots.forEach { snapshot -> + // add sizes to a map first, so we don't double count + snapshot.blobsMap.forEach { (chunkId, blob) -> sizeMap[chunkId] = blob.length } + } + return sizeMap.values.sumOf { it.toLong() } + } + +} diff --git a/app/src/main/java/com/stevesoltys/seedvault/repo/RepoModule.kt b/app/src/main/java/com/stevesoltys/seedvault/repo/RepoModule.kt index 8aa72064..bf1dca9f 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/repo/RepoModule.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/repo/RepoModule.kt @@ -21,4 +21,5 @@ val repoModule = module { } factory { SnapshotCreatorFactory(androidContext(), get(), get(), get()) } factory { Pruner(get(), get(), get()) } + single { Checker(get(), get(), get()) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt new file mode 100644 index 00000000..076e8a3f --- /dev/null +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/AppCheckFragment.kt @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2024 The Calyx Institute + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.stevesoltys.seedvault.settings + +import android.os.Bundle +import android.text.format.Formatter.formatShortFileSize +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.Button +import android.widget.ScrollView +import android.widget.TextView +import androidx.fragment.app.Fragment +import com.google.android.material.slider.LabelFormatter.LABEL_VISIBLE +import com.google.android.material.slider.Slider +import com.stevesoltys.seedvault.R +import org.koin.androidx.viewmodel.ext.android.activityViewModel + +private const val WARN_PERCENT = 25 +private const val WARN_BYTES = 1024 * 1024 * 1024 // 1 GB + +class AppCheckFragment : Fragment() { + + private val viewModel: SettingsViewModel by activityViewModel() + private lateinit var sliderLabel: TextView + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + val v = inflater.inflate(R.layout.fragment_app_check, container, false) as ScrollView + + val slider = v.requireViewById(R.id.slider) + sliderLabel = v.requireViewById(R.id.sliderLabel) + + // label not scrolling will be fixed in material-components 1.12.0 (next update) + slider.setLabelFormatter { value -> + viewModel.backupSize.value?.let { + formatShortFileSize(context, (it * value / 100).toLong()) + } ?: "${value.toInt()}%" + } + slider.addOnChangeListener { _, value, _ -> + onSliderChanged(value) + } + + viewModel.backupSize.observe(viewLifecycleOwner) { + 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) + } + + v.requireViewById