Use WorkInfo for determining if a backup is already running

Backup and restore is not possible when a backup is running. We used to check notifications for this, but now can use WorkManager's WorkInfo which should be more reliable.

Also, we used to prevent the "Backup now" action when app backup was disabled. But the user may want to do a storage backup. This is now possible.
This commit is contained in:
Torsten Grote 2024-02-22 13:09:48 -03:00
parent 0c1898c198
commit 8a870d8942
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
4 changed files with 26 additions and 33 deletions

View file

@ -141,10 +141,17 @@ class SettingsFragment : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel.lastBackupTime.observe(viewLifecycleOwner) { time -> viewModel.lastBackupTime.observe(viewLifecycleOwner) { time ->
setAppBackupStatusSummary(time, viewModel.nextScheduleTimeMillis.value) setAppBackupStatusSummary(
lastBackupInMillis = time,
nextScheduleTimeMillis = viewModel.appBackupWorkInfo.value?.nextScheduleTimeMillis,
)
} }
viewModel.nextScheduleTimeMillis.observe(viewLifecycleOwner) { time -> viewModel.appBackupWorkInfo.observe(viewLifecycleOwner) { workInfo ->
setAppBackupStatusSummary(viewModel.lastBackupTime.value, time) viewModel.onWorkerStateChanged()
setAppBackupStatusSummary(
lastBackupInMillis = viewModel.lastBackupTime.value,
nextScheduleTimeMillis = workInfo?.nextScheduleTimeMillis,
)
} }
val backupFiles: Preference = findPreference("backup_files")!! val backupFiles: Preference = findPreference("backup_files")!!
@ -165,7 +172,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
setAutoRestoreState() setAutoRestoreState()
setAppBackupStatusSummary( setAppBackupStatusSummary(
lastBackupInMillis = viewModel.lastBackupTime.value, lastBackupInMillis = viewModel.lastBackupTime.value,
nextScheduleTimeMillis = viewModel.nextScheduleTimeMillis.value, nextScheduleTimeMillis = viewModel.appBackupWorkInfo.value?.nextScheduleTimeMillis,
) )
} }
@ -273,11 +280,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
if (sb.isNotEmpty()) sb.append("\n") if (sb.isNotEmpty()) sb.append("\n")
// set time of next backup // set time of next backup
when (nextScheduleTimeMillis) { when (nextScheduleTimeMillis) {
-1L -> {
val text = getString(R.string.settings_backup_last_backup_never)
sb.append(getString(R.string.settings_backup_status_next_backup, text))
}
Long.MAX_VALUE -> { Long.MAX_VALUE -> {
val text = if (backupManager.isBackupEnabled && storage?.isUsb != true) { val text = if (backupManager.isBackupEnabled && storage?.isUsb != true) {
getString(R.string.notification_title) getString(R.string.notification_title)

View file

@ -26,6 +26,7 @@ import androidx.lifecycle.map
import androidx.lifecycle.switchMap import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.DiffUtil.calculateDiff import androidx.recyclerview.widget.DiffUtil.calculateDiff
import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.crypto.KeyManager import com.stevesoltys.seedvault.crypto.KeyManager
@ -72,10 +73,9 @@ internal class SettingsViewModel(
val backupPossible: LiveData<Boolean> = mBackupPossible val backupPossible: LiveData<Boolean> = mBackupPossible
internal val lastBackupTime = metadataManager.lastBackupTime internal val lastBackupTime = metadataManager.lastBackupTime
val nextScheduleTimeMillis = internal val appBackupWorkInfo =
workManager.getWorkInfosForUniqueWorkLiveData(UNIQUE_WORK_NAME).map { workManager.getWorkInfosForUniqueWorkLiveData(UNIQUE_WORK_NAME).map {
if (it.size > 0) it[0].nextScheduleTimeMillis it.getOrNull(0)
else -1L
} }
private val mAppStatusList = lastBackupTime.switchMap { private val mAppStatusList = lastBackupTime.switchMap {
@ -140,6 +140,14 @@ internal class SettingsViewModel(
onStoragePropertiesChanged() onStoragePropertiesChanged()
} }
fun onWorkerStateChanged() {
viewModelScope.launch(Dispatchers.IO) {
val canDo = settingsManager.canDoBackupNow() &&
appBackupWorkInfo.value?.state != WorkInfo.State.RUNNING
mBackupPossible.postValue(canDo)
}
}
private fun onStoragePropertiesChanged() { private fun onStoragePropertiesChanged() {
val storage = settingsManager.getStorage() ?: return val storage = settingsManager.getStorage() ?: return
@ -165,11 +173,8 @@ internal class SettingsViewModel(
connectivityManager?.registerNetworkCallback(request, networkCallback) connectivityManager?.registerNetworkCallback(request, networkCallback)
networkCallback.registered = true networkCallback.registered = true
} }
// update whether we can do backups right now or not
viewModelScope.launch(Dispatchers.IO) { onWorkerStateChanged()
val canDo = settingsManager.canDoBackupNow()
mBackupPossible.postValue(canDo)
}
} }
override fun onCleared() { override fun onCleared() {
@ -181,12 +186,7 @@ internal class SettingsViewModel(
} }
internal fun backupNow() { internal fun backupNow() {
// maybe replace the check below with one that checks if our transport service is running viewModelScope.launch(Dispatchers.IO) {
if (notificationManager.hasActiveBackupNotifications()) {
Toast.makeText(app, R.string.notification_backup_already_running, LENGTH_LONG).show()
} else if (!backupManager.isBackupEnabled) {
Toast.makeText(app, R.string.notification_backup_disabled, LENGTH_LONG).show()
} else viewModelScope.launch(Dispatchers.IO) {
if (settingsManager.isStorageBackupEnabled()) { if (settingsManager.isStorageBackupEnabled()) {
val i = Intent(app, StorageBackupService::class.java) val i = Intent(app, StorageBackupService::class.java)
// this starts an app backup afterwards // this starts an app backup afterwards

View file

@ -23,6 +23,7 @@ force running with:
adb shell cmd jobscheduler run -f com.stevesoltys.seedvault 0 adb shell cmd jobscheduler run -f com.stevesoltys.seedvault 0
*/ */
internal class StorageBackupJobService : BackupJobService(StorageBackupService::class.java) internal class StorageBackupJobService : BackupJobService(StorageBackupService::class.java)
internal class StorageBackupService : BackupService() { internal class StorageBackupService : BackupService() {

View file

@ -192,16 +192,6 @@ internal class BackupNotificationManager(private val context: Context) {
nm.notify(NOTIFICATION_ID_SUCCESS, notification) nm.notify(NOTIFICATION_ID_SUCCESS, notification)
} }
fun hasActiveBackupNotifications(): Boolean {
nm.activeNotifications.forEach {
if (it.packageName == context.packageName) {
if (it.id == NOTIFICATION_ID_BACKGROUND) return true
if (it.id == NOTIFICATION_ID_OBSERVER) return it.isOngoing
}
}
return false
}
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun onBackupError() { fun onBackupError() {
val intent = Intent(context, SettingsActivity::class.java) val intent = Intent(context, SettingsActivity::class.java)