Ask on first run if user wants restore

This commit is contained in:
Torsten Grote 2024-08-02 09:54:50 -03:00
parent cff5d20342
commit 22b3ace3aa
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
7 changed files with 79 additions and 28 deletions

View file

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2024 The Calyx Institute
* SPDX-License-Identifier: Apache-2.0
*/
package com.stevesoltys.seedvault.settings
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.restore.RestoreActivity
class FirstRunFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
isCancelable = false // is what really works, specifying it for the dialog only doesn't
return MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.first_start_text)
.setPositiveButton(R.string.setup_button) { dialog, _ ->
parentFragmentManager.beginTransaction()
.replace(R.id.fragment, SettingsFragment(), null)
.commit()
dialog.dismiss()
}
.setNeutralButton(R.string.restore_backup_button) { dialog, _ ->
val i = Intent(requireContext(), RestoreActivity::class.java)
startActivity(i)
dialog.dismiss()
requireActivity().finish()
}
.setCancelable(false)
.create()
}
}

View file

@ -6,17 +6,14 @@
package com.stevesoltys.seedvault.settings
import android.os.Bundle
import androidx.annotation.CallSuper
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.ui.RequireProvisioningActivity
import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import com.stevesoltys.seedvault.ui.recoverycode.ARG_FOR_NEW_CODE
import com.stevesoltys.seedvault.ui.storage.StorageCheckFragment
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
internal const val ACTION_APP_STATUS_LIST = "com.stevesoltys.seedvault.APP_STATUS_LIST"
@ -25,7 +22,6 @@ private const val PREF_BACKUP_RECOVERY_CODE = "backup_recovery_code"
class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmentCallback {
private val viewModel: SettingsViewModel by viewModel()
private val notificationManager: BackupNotificationManager by inject()
override fun getViewModel(): RequireProvisioningViewModel = viewModel
@ -37,8 +33,13 @@ class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmen
setSupportActionBar(requireViewById(R.id.toolbar))
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
// always start with settings fragment as a base (when fresh start)
if (savedInstanceState == null) showFragment(SettingsFragment())
if (savedInstanceState == null && viewModel.isFirstStart) {
// let user choose whether to restore on first start
FirstRunFragment().show(supportFragmentManager, null)
} else if (savedInstanceState == null) {
// always start with settings fragment as a base (when fresh start)
showFragment(SettingsFragment())
}
// add app status fragment on the stack, if started via intent
if (intent?.action == ACTION_APP_STATUS_LIST) {
showFragment(AppStatusFragment(), true)
@ -58,24 +59,6 @@ class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmen
}
}
@CallSuper
override fun onResume() {
super.onResume()
// Activity results from the parent will get delivered before and might tell us to finish.
// Don't start any new activities when that happens.
// Note: onStart() can get called *before* results get delivered, so we use onResume() here
if (isFinishing) return
// check that backup is provisioned
if (!viewModel.recoveryCodeIsSet()) {
showRecoveryCodeActivity()
} else if (!viewModel.validLocationIsSet()) {
showStorageActivity()
// remove potential error notifications
notificationManager.onBackupErrorSeen()
}
}
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference,

View file

@ -30,6 +30,7 @@ import com.stevesoltys.seedvault.permitDiskReads
import com.stevesoltys.seedvault.plugins.StoragePluginManager
import com.stevesoltys.seedvault.plugins.StorageProperties
import com.stevesoltys.seedvault.restore.RestoreActivity
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import com.stevesoltys.seedvault.ui.toRelativeTime
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
@ -42,6 +43,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private val viewModel: SettingsViewModel by sharedViewModel()
private val storagePluginManager: StoragePluginManager by inject()
private val backupManager: IBackupManager by inject()
private val notificationManager: BackupNotificationManager by inject()
private lateinit var backup: TwoStatePreference
private lateinit var autoRestore: TwoStatePreference
@ -184,6 +186,24 @@ class SettingsFragment : PreferenceFragmentCompat() {
setAppBackupSchedulingSummary(viewModel.appBackupWorkInfo.value)
}
override fun onResume() {
super.onResume()
// Activity results from the parent will get delivered before and might tell us to finish.
// Don't start any new activities when that happens.
// Note: onStart() can get called *before* results get delivered, so we use onResume() here
if (requireActivity().isFinishing) return
// check that backup is provisioned
val activity = requireActivity() as SettingsActivity
if (!viewModel.recoveryCodeIsSet()) {
activity.showRecoveryCodeActivity()
} else if (!viewModel.validLocationIsSet()) {
activity.showStorageActivity()
// remove potential error notifications
notificationManager.onBackupErrorSeen()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.settings_menu, menu)

View file

@ -247,6 +247,14 @@ class SettingsManager(private val context: Context) {
.putBoolean(PREF_KEY_D2D_BACKUPS, enabled)
.apply()
}
/**
* This assumes that if there's no storage plugin set, it is the first start.
* We enforce a storage plugin and don't allow unsetting one,
* so this should be a safe assumption.
*/
val isFirstStart get() = prefs.getString(PREF_KEY_STORAGE_PLUGIN, null) == null
}
data class FlashDrive(

View file

@ -85,6 +85,7 @@ internal class SettingsViewModel(
private val workManager = WorkManager.getInstance(app)
override val isRestoreOperation = false
val isFirstStart get() = settingsManager.isFirstStart
val isBackupRunning: StateFlow<Boolean>
private val mBackupPossible = MutableLiveData(false)

View file

@ -56,12 +56,12 @@ abstract class RequireProvisioningActivity : BackupActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getViewModel().chooseBackupLocation.observeEvent(this, { show ->
getViewModel().chooseBackupLocation.observeEvent(this) { show ->
if (show) showStorageActivity()
})
}
}
protected fun showStorageActivity() {
fun showStorageActivity() {
val intent = Intent(this, StorageActivity::class.java).apply {
putExtra(INTENT_EXTRA_IS_RESTORE, getViewModel().isRestoreOperation)
putExtra(INTENT_EXTRA_IS_SETUP_WIZARD, isSetupWizard)
@ -69,7 +69,7 @@ abstract class RequireProvisioningActivity : BackupActivity() {
requestLocation.launch(intent)
}
protected fun showRecoveryCodeActivity() {
fun showRecoveryCodeActivity() {
val intent = Intent(this, RecoveryCodeActivity::class.java).apply {
putExtra(INTENT_EXTRA_IS_RESTORE, getViewModel().isRestoreOperation)
putExtra(INTENT_EXTRA_IS_SETUP_WIZARD, isSetupWizard)

View file

@ -9,7 +9,9 @@
<string name="data_management_label">Seedvault Backup</string>
<string name="current_destination_string">Backup status and settings</string>
<string name="first_start_text">Do you want to restore your device from an existing backup or start a new backup?</string>
<string name="restore_backup_button">Restore backup</string>
<string name="setup_button">Start new</string>
<!-- Settings -->
<string name="settings_category_apps">App backup</string>