Don't allow running app data check while backup is running
This commit is contained in:
parent
45d710270a
commit
15e8850e5e
6 changed files with 58 additions and 30 deletions
|
@ -16,6 +16,7 @@ import com.stevesoltys.seedvault.storage.StorageBackupService
|
||||||
import com.stevesoltys.seedvault.transport.ConfigurableBackupTransportService
|
import com.stevesoltys.seedvault.transport.ConfigurableBackupTransportService
|
||||||
import com.stevesoltys.seedvault.worker.AppBackupPruneWorker
|
import com.stevesoltys.seedvault.worker.AppBackupPruneWorker
|
||||||
import com.stevesoltys.seedvault.worker.AppBackupWorker.Companion.UNIQUE_WORK_NAME
|
import com.stevesoltys.seedvault.worker.AppBackupWorker.Companion.UNIQUE_WORK_NAME
|
||||||
|
import com.stevesoltys.seedvault.worker.AppCheckerWorker
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
|
||||||
|
@ -32,18 +33,28 @@ class BackupStateManager(
|
||||||
flow = ConfigurableBackupTransportService.isRunning,
|
flow = ConfigurableBackupTransportService.isRunning,
|
||||||
flow2 = StorageBackupService.isRunning,
|
flow2 = StorageBackupService.isRunning,
|
||||||
flow3 = workManager.getWorkInfosForUniqueWorkFlow(UNIQUE_WORK_NAME),
|
flow3 = workManager.getWorkInfosForUniqueWorkFlow(UNIQUE_WORK_NAME),
|
||||||
flow4 = workManager.getWorkInfosForUniqueWorkFlow(AppBackupPruneWorker.UNIQUE_WORK_NAME),
|
) { appBackupRunning, filesBackupRunning, workInfo1 ->
|
||||||
) { appBackupRunning, filesBackupRunning, workInfo1, workInfo2 ->
|
|
||||||
val workInfoState1 = workInfo1.getOrNull(0)?.state
|
val workInfoState1 = workInfo1.getOrNull(0)?.state
|
||||||
val workInfoState2 = workInfo2.getOrNull(0)?.state
|
|
||||||
Log.i(
|
Log.i(
|
||||||
TAG, "appBackupRunning: $appBackupRunning, " +
|
TAG, "appBackupRunning: $appBackupRunning, " +
|
||||||
"filesBackupRunning: $filesBackupRunning, " +
|
"filesBackupRunning: $filesBackupRunning, " +
|
||||||
"appBackupWorker: ${workInfoState1?.name}, " +
|
"appBackupWorker: ${workInfoState1?.name}"
|
||||||
"pruneBackupWorker: ${workInfoState2?.name}"
|
|
||||||
)
|
)
|
||||||
appBackupRunning || filesBackupRunning ||
|
appBackupRunning || filesBackupRunning || workInfoState1 == RUNNING
|
||||||
workInfoState1 == RUNNING || workInfoState2 == RUNNING
|
}
|
||||||
|
|
||||||
|
val isCheckOrPruneRunning: Flow<Boolean> = combine(
|
||||||
|
flow = workManager.getWorkInfosForUniqueWorkFlow(AppBackupPruneWorker.UNIQUE_WORK_NAME),
|
||||||
|
flow2 = workManager.getWorkInfosForUniqueWorkFlow(AppCheckerWorker.UNIQUE_WORK_NAME),
|
||||||
|
) { pruneInfo, checkInfo ->
|
||||||
|
val pruneInfoState = pruneInfo.getOrNull(0)?.state
|
||||||
|
val checkInfoState = checkInfo.getOrNull(0)?.state
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"pruneBackupWorker: ${pruneInfoState?.name}, " +
|
||||||
|
"appCheckerWorker: ${checkInfoState?.name}"
|
||||||
|
)
|
||||||
|
pruneInfoState == RUNNING || checkInfoState == RUNNING
|
||||||
}
|
}
|
||||||
|
|
||||||
val isAutoRestoreEnabled: Boolean
|
val isAutoRestoreEnabled: Boolean
|
||||||
|
|
|
@ -49,6 +49,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
private lateinit var backupLocation: Preference
|
private lateinit var backupLocation: Preference
|
||||||
private lateinit var backupStatus: Preference
|
private lateinit var backupStatus: Preference
|
||||||
private lateinit var backupScheduling: Preference
|
private lateinit var backupScheduling: Preference
|
||||||
|
private lateinit var backupAppCheck: Preference
|
||||||
private lateinit var backupStorage: TwoStatePreference
|
private lateinit var backupStorage: TwoStatePreference
|
||||||
private lateinit var backupRecoveryCode: Preference
|
private lateinit var backupRecoveryCode: Preference
|
||||||
|
|
||||||
|
@ -92,15 +93,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
backupLocation = findPreference("backup_location")!!
|
backupLocation = findPreference("backup_location")!!
|
||||||
backupLocation.setOnPreferenceClickListener {
|
backupLocation.setOnPreferenceClickListener {
|
||||||
if (viewModel.isBackupRunning.value) {
|
|
||||||
// don't allow changing backup destination while backup is running
|
|
||||||
// TODO we could show toast or snackbar here
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
viewModel.chooseBackupLocation()
|
viewModel.chooseBackupLocation()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
autoRestore = findPreference(PREF_KEY_AUTO_RESTORE)!!
|
autoRestore = findPreference(PREF_KEY_AUTO_RESTORE)!!
|
||||||
autoRestore.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
autoRestore.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
||||||
|
@ -117,6 +112,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
backupStatus = findPreference("backup_status")!!
|
backupStatus = findPreference("backup_status")!!
|
||||||
backupScheduling = findPreference("backup_scheduling")!!
|
backupScheduling = findPreference("backup_scheduling")!!
|
||||||
|
backupAppCheck = findPreference("backup_app_check")!!
|
||||||
|
|
||||||
backupStorage = findPreference("backup_storage")!!
|
backupStorage = findPreference("backup_storage")!!
|
||||||
backupStorage.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
backupStorage.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
|
||||||
|
@ -148,6 +144,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
viewModel.backupPossible.observe(viewLifecycleOwner) { possible ->
|
viewModel.backupPossible.observe(viewLifecycleOwner) { possible ->
|
||||||
toolbar.menu.findItem(R.id.action_backup)?.isEnabled = possible
|
toolbar.menu.findItem(R.id.action_backup)?.isEnabled = possible
|
||||||
toolbar.menu.findItem(R.id.action_restore)?.isEnabled = possible
|
toolbar.menu.findItem(R.id.action_restore)?.isEnabled = possible
|
||||||
|
backupLocation.isEnabled = possible
|
||||||
|
backupAppCheck.isEnabled = possible
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.lastBackupTime.observe(viewLifecycleOwner) { time ->
|
viewModel.lastBackupTime.observe(viewLifecycleOwner) { time ->
|
||||||
|
|
|
@ -83,7 +83,8 @@ internal class SettingsViewModel(
|
||||||
override val isRestoreOperation = false
|
override val isRestoreOperation = false
|
||||||
val isFirstStart get() = settingsManager.isFirstStart
|
val isFirstStart get() = settingsManager.isFirstStart
|
||||||
|
|
||||||
val isBackupRunning: StateFlow<Boolean>
|
private val isBackupRunning: StateFlow<Boolean>
|
||||||
|
private val isCheckOrPruneRunning: StateFlow<Boolean>
|
||||||
private val mBackupPossible = MutableLiveData(false)
|
private val mBackupPossible = MutableLiveData(false)
|
||||||
val backupPossible: LiveData<Boolean> = mBackupPossible
|
val backupPossible: LiveData<Boolean> = mBackupPossible
|
||||||
|
|
||||||
|
@ -144,12 +145,23 @@ internal class SettingsViewModel(
|
||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
initialValue = false,
|
initialValue = false,
|
||||||
)
|
)
|
||||||
|
isCheckOrPruneRunning = backupStateManager.isCheckOrPruneRunning.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = false,
|
||||||
|
)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
// update running state
|
// update running state
|
||||||
isBackupRunning.collect {
|
isBackupRunning.collect {
|
||||||
onBackupRunningStateChanged()
|
onBackupRunningStateChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scope.launch {
|
||||||
|
// update running state
|
||||||
|
isCheckOrPruneRunning.collect {
|
||||||
|
onBackupRunningStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
onStoragePropertiesChanged()
|
onStoragePropertiesChanged()
|
||||||
loadFilesSummary()
|
loadFilesSummary()
|
||||||
}
|
}
|
||||||
|
@ -172,11 +184,11 @@ internal class SettingsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onBackupRunningStateChanged() {
|
private fun onBackupRunningStateChanged() {
|
||||||
if (isBackupRunning.value) mBackupPossible.postValue(false)
|
val backupAllowed = !isBackupRunning.value && !isCheckOrPruneRunning.value
|
||||||
else viewModelScope.launch(Dispatchers.IO) {
|
if (backupAllowed) viewModelScope.launch(Dispatchers.IO) {
|
||||||
val canDo = !isBackupRunning.value && !backendManager.isOnUnavailableUsb()
|
val canDo = !backendManager.isOnUnavailableUsb()
|
||||||
mBackupPossible.postValue(canDo)
|
mBackupPossible.postValue(canDo)
|
||||||
}
|
} else mBackupPossible.postValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onStoragePropertiesChanged() {
|
private fun onStoragePropertiesChanged() {
|
||||||
|
|
|
@ -22,12 +22,14 @@ import androidx.work.OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import com.stevesoltys.seedvault.BackupStateManager
|
||||||
import com.stevesoltys.seedvault.R
|
import com.stevesoltys.seedvault.R
|
||||||
import com.stevesoltys.seedvault.backend.BackendManager
|
import com.stevesoltys.seedvault.backend.BackendManager
|
||||||
import com.stevesoltys.seedvault.repo.AppBackupManager
|
import com.stevesoltys.seedvault.repo.AppBackupManager
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.NOTIFICATION_ID_OBSERVER
|
import com.stevesoltys.seedvault.ui.notification.NOTIFICATION_ID_OBSERVER
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -100,6 +102,7 @@ class AppBackupWorker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val backupStateManager: BackupStateManager by inject()
|
||||||
private val backupRequester: BackupRequester by inject()
|
private val backupRequester: BackupRequester by inject()
|
||||||
private val settingsManager: SettingsManager by inject()
|
private val settingsManager: SettingsManager by inject()
|
||||||
private val apkBackupManager: ApkBackupManager by inject()
|
private val apkBackupManager: ApkBackupManager by inject()
|
||||||
|
@ -109,6 +112,10 @@ class AppBackupWorker(
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
Log.i(TAG, "Start worker $this ($id)")
|
Log.i(TAG, "Start worker $this ($id)")
|
||||||
|
if (backupStateManager.isCheckOrPruneRunning.first()) {
|
||||||
|
Log.i(TAG, "isCheckOrPruneRunning was true, so retrying later...")
|
||||||
|
return Result.retry()
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
setForeground(createForegroundInfo())
|
setForeground(createForegroundInfo())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
|
@ -17,10 +17,12 @@ import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
|
import androidx.work.OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import com.stevesoltys.seedvault.BackupStateManager
|
||||||
import com.stevesoltys.seedvault.repo.Checker
|
import com.stevesoltys.seedvault.repo.Checker
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.NOTIFICATION_ID_CHECKING
|
import com.stevesoltys.seedvault.ui.notification.NOTIFICATION_ID_CHECKING
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
@ -50,12 +52,16 @@ class AppCheckerWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val log = KotlinLogging.logger {}
|
private val log = KotlinLogging.logger {}
|
||||||
|
private val backupStateManager: BackupStateManager by inject()
|
||||||
private val checker: Checker by inject()
|
private val checker: Checker by inject()
|
||||||
private val nm: BackupNotificationManager by inject()
|
private val nm: BackupNotificationManager by inject()
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
// TODO don't let backup/restore happen while we check
|
|
||||||
log.info { "Start worker $this ($id)" }
|
log.info { "Start worker $this ($id)" }
|
||||||
|
if (backupStateManager.isBackupRunning.first()) {
|
||||||
|
Log.i(TAG, "isBackupRunning was true, so retrying later...")
|
||||||
|
return Result.retry()
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
setForeground(createForegroundInfo())
|
setForeground(createForegroundInfo())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -64,14 +70,8 @@ class AppCheckerWorker(
|
||||||
val percent = inputData.getInt(PERCENT, -1)
|
val percent = inputData.getInt(PERCENT, -1)
|
||||||
check(percent in 0..100) { "Percent $percent out of bounds." }
|
check(percent in 0..100) { "Percent $percent out of bounds." }
|
||||||
|
|
||||||
return try {
|
|
||||||
checker.check(percent)
|
checker.check(percent)
|
||||||
Result.success()
|
return Result.success()
|
||||||
} catch (e: Exception) {
|
|
||||||
// TODO maybe show error notification
|
|
||||||
log.error(e) { "Error while checking data: " }
|
|
||||||
Result.retry()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createForegroundInfo() = ForegroundInfo(
|
private fun createForegroundInfo() = ForegroundInfo(
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<androidx.preference.Preference
|
<androidx.preference.Preference
|
||||||
app:fragment="com.stevesoltys.seedvault.ui.check.AppCheckFragment"
|
app:fragment="com.stevesoltys.seedvault.ui.check.AppCheckFragment"
|
||||||
app:icon="@drawable/ic_cloud_search"
|
app:icon="@drawable/ic_cloud_search"
|
||||||
app:key="backup_scheduling"
|
app:key="backup_app_check"
|
||||||
app:summary="@string/settings_backup_app_check_summary"
|
app:summary="@string/settings_backup_app_check_summary"
|
||||||
app:title="@string/settings_backup_app_check_title" />
|
app:title="@string/settings_backup_app_check_title" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue