diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6bc9213ab..c3bd194cc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -156,6 +156,11 @@ + + ( - repeatInterval = 24, - repeatIntervalTimeUnit = TimeUnit.HOURS, - flexTimeInterval = 2, - flexTimeIntervalUnit = TimeUnit.HOURS, - ).setConstraints(backupConstraints) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.HOURS) - .build() - val workManager = WorkManager.getInstance(appContext) - workManager.enqueueUniquePeriodicWork(UNIQUE_WORK_NAME, UPDATE, backupWorkRequest) - } - - fun unschedule(appContext: Context) { - val workManager = WorkManager.getInstance(appContext) - workManager.cancelUniqueWork(UNIQUE_WORK_NAME) - } - } - - override fun doWork(): Result { - // TODO once we make this the default, we should do storage backup here as well - // or have two workers and ensure they never run at the same time - return if (requestBackup(applicationContext)) Result.success() - else Result.retry() - } -} diff --git a/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt b/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt index 4800fcefb..ff5208b03 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt @@ -20,8 +20,8 @@ import com.stevesoltys.seedvault.settings.FlashDrive import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.storage.StorageBackupService import com.stevesoltys.seedvault.storage.StorageBackupService.Companion.EXTRA_START_APP_BACKUP -import com.stevesoltys.seedvault.transport.requestBackup import com.stevesoltys.seedvault.ui.storage.AUTHORITY_STORAGE +import com.stevesoltys.seedvault.worker.AppBackupWorker import org.koin.core.context.GlobalContext.get import java.util.concurrent.TimeUnit.HOURS @@ -63,9 +63,7 @@ class UsbIntentReceiver : UsbMonitor() { i.putExtra(EXTRA_START_APP_BACKUP, true) startForegroundService(context, i) } else { - Thread { - requestBackup(context) - }.start() + AppBackupWorker.scheduleNow(context) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreViewModel.kt index e03e55e80..6e6b0c5b6 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreViewModel.kt @@ -39,7 +39,7 @@ import com.stevesoltys.seedvault.restore.install.isInstalled import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.storage.StorageRestoreService import com.stevesoltys.seedvault.transport.TRANSPORT_ID -import com.stevesoltys.seedvault.transport.backup.NUM_PACKAGES_PER_TRANSACTION +import com.stevesoltys.seedvault.worker.NUM_PACKAGES_PER_TRANSACTION import com.stevesoltys.seedvault.transport.restore.RestoreCoordinator import com.stevesoltys.seedvault.ui.AppBackupState import com.stevesoltys.seedvault.ui.AppBackupState.FAILED diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt index e220462c3..afb056879 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt @@ -25,7 +25,7 @@ import androidx.lifecycle.Transformations.switchMap import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import androidx.recyclerview.widget.DiffUtil.calculateDiff -import com.stevesoltys.seedvault.BackupWorker +import androidx.work.ExistingPeriodicWorkPolicy import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.crypto.KeyManager import com.stevesoltys.seedvault.metadata.MetadataManager @@ -33,9 +33,9 @@ import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.storage.StorageBackupJobService import com.stevesoltys.seedvault.storage.StorageBackupService import com.stevesoltys.seedvault.storage.StorageBackupService.Companion.EXTRA_START_APP_BACKUP -import com.stevesoltys.seedvault.transport.requestBackup import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager +import com.stevesoltys.seedvault.worker.AppBackupWorker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -174,7 +174,7 @@ internal class SettingsViewModel( i.putExtra(EXTRA_START_APP_BACKUP, true) startForegroundService(app, i) } else { - requestBackup(app) + AppBackupWorker.scheduleNow(app) } } } @@ -266,9 +266,9 @@ internal class SettingsViewModel( fun onD2dChanged(enabled: Boolean) { backupManager.setFrameworkSchedulingEnabledForUser(UserHandle.myUserId(), !enabled) if (enabled) { - BackupWorker.schedule(app) + AppBackupWorker.schedule(app) } else { - BackupWorker.unschedule(app) + AppBackupWorker.unschedule(app) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt b/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt index 1c54beb27..e42da2d3f 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt @@ -1,7 +1,7 @@ package com.stevesoltys.seedvault.storage import android.content.Intent -import com.stevesoltys.seedvault.transport.requestBackup +import com.stevesoltys.seedvault.worker.AppBackupWorker import org.calyxos.backup.storage.api.BackupObserver import org.calyxos.backup.storage.api.RestoreObserver import org.calyxos.backup.storage.api.StorageBackup @@ -40,7 +40,7 @@ internal class StorageBackupService : BackupService() { override fun onBackupFinished(intent: Intent, success: Boolean) { if (intent.getBooleanExtra(EXTRA_START_APP_BACKUP, false)) { - requestBackup(applicationContext) + AppBackupWorker.scheduleNow(applicationContext) } } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt index 1b9fe3b6c..9d81d3e5e 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt @@ -2,18 +2,13 @@ package com.stevesoltys.seedvault.transport import android.app.Service import android.app.backup.IBackupManager -import android.content.Context import android.content.Intent import android.os.IBinder import android.util.Log -import androidx.annotation.WorkerThread import com.stevesoltys.seedvault.crypto.KeyManager -import com.stevesoltys.seedvault.transport.backup.BackupRequester -import com.stevesoltys.seedvault.transport.backup.PackageService import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import org.koin.core.context.GlobalContext.get private val TAG = ConfigurableBackupTransportService::class.java.simpleName @@ -56,23 +51,3 @@ class ConfigurableBackupTransportService : Service(), KoinComponent { } } - -/** - * Requests the system to initiate a backup. - * - * @return true iff backups was requested successfully (backup itself can still fail). - */ -@WorkerThread -fun requestBackup(context: Context): Boolean { - val backupManager: IBackupManager = get().get() - return if (backupManager.isBackupEnabled) { - val packageService: PackageService = get().get() - - Log.d(TAG, "Backup is enabled, request backup...") - val backupRequester = BackupRequester(context, backupManager, packageService) - return backupRequester.requestBackup() - } else { - Log.i(TAG, "Backup is not enabled") - true // this counts as success - } -} diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/BackupNotificationManager.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/BackupNotificationManager.kt index 0a8bc09da..ec450d780 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/BackupNotificationManager.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/BackupNotificationManager.kt @@ -30,17 +30,15 @@ import com.stevesoltys.seedvault.settings.SettingsActivity import kotlin.math.min private const val CHANNEL_ID_OBSERVER = "NotificationBackupObserver" -private const val CHANNEL_ID_APK = "NotificationApkBackup" private const val CHANNEL_ID_SUCCESS = "NotificationBackupSuccess" private const val CHANNEL_ID_ERROR = "NotificationError" private const val CHANNEL_ID_RESTORE_ERROR = "NotificationRestoreError" -private const val NOTIFICATION_ID_OBSERVER = 1 -internal const val NOTIFICATION_ID_APK = 2 -private const val NOTIFICATION_ID_SUCCESS = 3 -private const val NOTIFICATION_ID_ERROR = 4 -private const val NOTIFICATION_ID_RESTORE_ERROR = 5 -private const val NOTIFICATION_ID_BACKGROUND = 6 -private const val NOTIFICATION_ID_NO_MAIN_KEY_ERROR = 7 +internal const val NOTIFICATION_ID_OBSERVER = 1 +private const val NOTIFICATION_ID_SUCCESS = 2 +private const val NOTIFICATION_ID_ERROR = 3 +private const val NOTIFICATION_ID_RESTORE_ERROR = 4 +private const val NOTIFICATION_ID_BACKGROUND = 5 +private const val NOTIFICATION_ID_NO_MAIN_KEY_ERROR = 6 private val TAG = BackupNotificationManager::class.java.simpleName @@ -48,7 +46,6 @@ internal class BackupNotificationManager(private val context: Context) { private val nm = context.getSystemService(NotificationManager::class.java)!!.apply { createNotificationChannel(getObserverChannel()) - createNotificationChannel(getApkChannel()) createNotificationChannel(getSuccessChannel()) createNotificationChannel(getErrorChannel()) createNotificationChannel(getRestoreErrorChannel()) @@ -61,13 +58,6 @@ internal class BackupNotificationManager(private val context: Context) { } } - private fun getApkChannel(): NotificationChannel { - val title = context.getString(R.string.notification_apk_channel_title) - return NotificationChannel(CHANNEL_ID_APK, title, IMPORTANCE_LOW).apply { - enableVibration(false) - } - } - private fun getSuccessChannel(): NotificationChannel { val title = context.getString(R.string.notification_success_channel_title) return NotificationChannel(CHANNEL_ID_SUCCESS, title, IMPORTANCE_LOW).apply { @@ -91,8 +81,7 @@ internal class BackupNotificationManager(private val context: Context) { fun onApkBackup(packageName: String, name: CharSequence, transferred: Int, expected: Int) { Log.i(TAG, "$transferred/$expected - $name ($packageName)") val text = context.getString(R.string.notification_apk_text, name) - val notification = getApkBackupNotification(text, transferred, expected) - nm.notify(NOTIFICATION_ID_APK, notification) + updateBackupNotification(text, transferred, expected) } /** @@ -100,32 +89,15 @@ internal class BackupNotificationManager(private val context: Context) { */ fun onAppsNotBackedUp() { Log.i(TAG, "onAppsNotBackedUp") - val notification = - getApkBackupNotification(context.getString(R.string.notification_apk_not_backed_up)) - nm.notify(NOTIFICATION_ID_APK, notification) + val text = context.getString(R.string.notification_apk_not_backed_up) + updateBackupNotification(text) } - fun getApkBackupNotification( - text: String?, - expected: Int = 0, - transferred: Int = 0, - ): Notification = Builder(context, CHANNEL_ID_APK).apply { - setSmallIcon(R.drawable.ic_cloud_upload) - setContentTitle(context.getString(R.string.notification_title)) - setContentText(text) - setOngoing(true) - setShowWhen(false) - setWhen(System.currentTimeMillis()) - setProgress(expected, transferred, false) - priority = PRIORITY_DEFAULT - foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE - }.build() - /** * Call after [onApkBackup] or [onAppsNotBackedUp] were called. */ fun onApkBackupDone() { - nm.cancel(NOTIFICATION_ID_APK) + nm.cancel(NOTIFICATION_ID_OBSERVER) } /** @@ -133,7 +105,7 @@ internal class BackupNotificationManager(private val context: Context) { */ fun onBackupStarted(expectedPackages: Int) { updateBackupNotification( - appName = "", // This passes quickly, no need to show something here + text = "", // This passes quickly, no need to show something here transferred = 0, expected = expectedPackages ) @@ -145,32 +117,33 @@ internal class BackupNotificationManager(private val context: Context) { * this type is is expected to get called after [onApkBackup]. */ fun onBackupUpdate(app: CharSequence, transferred: Int, total: Int) { - updateBackupNotification( - appName = app, - transferred = min(transferred, total), - expected = total - ) + updateBackupNotification(app, min(transferred, total), total) } private fun updateBackupNotification( - appName: CharSequence, - transferred: Int, - expected: Int, + text: CharSequence, + transferred: Int = 0, + expected: Int = 0, ) { - val notification = Builder(context, CHANNEL_ID_OBSERVER).apply { + val notification = getBackupNotification(text, transferred, expected) + nm.notify(NOTIFICATION_ID_OBSERVER, notification) + } + + fun getBackupNotification(text: CharSequence, progress: Int = 0, total: Int = 0): Notification { + return Builder(context, CHANNEL_ID_OBSERVER).apply { setSmallIcon(R.drawable.ic_cloud_upload) setContentTitle(context.getString(R.string.notification_title)) - setContentText(appName) + setContentText(text) setOngoing(true) setShowWhen(false) setWhen(System.currentTimeMillis()) - setProgress(expected, transferred, false) + setProgress(progress, total, false) priority = PRIORITY_DEFAULT foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE }.build() - nm.notify(NOTIFICATION_ID_OBSERVER, notification) } + // TODO where was this used? private fun updateBackgroundBackupNotification(infoText: CharSequence) { Log.i(TAG, "$infoText") val notification = Builder(context, CHANNEL_ID_OBSERVER).apply { diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt index ff2982075..5f5eaea4f 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt @@ -10,7 +10,7 @@ import android.util.Log.isLoggable import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.metadata.MetadataManager -import com.stevesoltys.seedvault.transport.backup.BackupRequester +import com.stevesoltys.seedvault.worker.BackupRequester import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/BackupStorageViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/BackupStorageViewModel.kt index 4595468b0..33fe51b80 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/BackupStorageViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/BackupStorageViewModel.kt @@ -12,8 +12,7 @@ import androidx.lifecycle.viewModelScope import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.transport.TRANSPORT_ID -import com.stevesoltys.seedvault.transport.backup.BackupCoordinator -import com.stevesoltys.seedvault.transport.requestBackup +import com.stevesoltys.seedvault.worker.AppBackupWorker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.calyxos.backup.storage.api.StorageBackup @@ -24,7 +23,6 @@ private val TAG = BackupStorageViewModel::class.java.simpleName internal class BackupStorageViewModel( private val app: Application, private val backupManager: IBackupManager, - private val backupCoordinator: BackupCoordinator, private val storageBackup: StorageBackup, settingsManager: SettingsManager, ) : StorageViewModel(app, settingsManager) { @@ -73,7 +71,7 @@ internal class BackupStorageViewModel( // notify the UI that the location has been set mLocationChecked.postEvent(LocationResult()) if (requestBackup) { - requestBackup(app) + AppBackupWorker.scheduleNow(app) } } else { // notify the UI that the location was invalid diff --git a/app/src/main/java/com/stevesoltys/seedvault/worker/AppBackupWorker.kt b/app/src/main/java/com/stevesoltys/seedvault/worker/AppBackupWorker.kt new file mode 100644 index 000000000..f6cc1b459 --- /dev/null +++ b/app/src/main/java/com/stevesoltys/seedvault/worker/AppBackupWorker.kt @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: 2024 The Calyx Institute + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.stevesoltys.seedvault.worker + +import android.content.Context +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.util.Log +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE +import androidx.work.ExistingPeriodicWorkPolicy.UPDATE +import androidx.work.ExistingWorkPolicy.REPLACE +import androidx.work.ForegroundInfo +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.WorkerParameters +import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager +import com.stevesoltys.seedvault.ui.notification.NOTIFICATION_ID_OBSERVER +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.util.concurrent.TimeUnit + +class AppBackupWorker( + appContext: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(appContext, workerParams), KoinComponent { + + companion object { + private val TAG = AppBackupWorker::class.simpleName + private const val UNIQUE_WORK_NAME = "com.stevesoltys.seedvault.APP_BACKUP" + private const val TAG_NOW = "com.stevesoltys.seedvault.TAG_NOW" + + fun schedule(context: Context, existingWorkPolicy: ExistingPeriodicWorkPolicy = UPDATE) { + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.UNMETERED) + .setRequiresCharging(true) + .build() + val workRequest = PeriodicWorkRequestBuilder( + repeatInterval = 24, + repeatIntervalTimeUnit = TimeUnit.HOURS, + flexTimeInterval = 2, + flexTimeIntervalUnit = TimeUnit.HOURS, + ).setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) + .build() + val workManager = WorkManager.getInstance(context) + Log.i(TAG, "Scheduling app backup: $workRequest") + workManager.enqueueUniquePeriodicWork(UNIQUE_WORK_NAME, existingWorkPolicy, workRequest) + } + + fun scheduleNow(context: Context) { + val workRequest = OneTimeWorkRequestBuilder() + .setExpedited(RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .addTag(TAG_NOW) + .build() + val workManager = WorkManager.getInstance(context) + Log.i(TAG, "Asking to do app backup now...") + workManager.enqueueUniqueWork(UNIQUE_WORK_NAME, REPLACE, workRequest) + } + + fun unschedule(context: Context) { + Log.i(TAG, "Unscheduling app backup...") + val workManager = WorkManager.getInstance(context) + workManager.cancelUniqueWork(UNIQUE_WORK_NAME) + } + } + + private val backupRequester: BackupRequester by inject() + private val apkBackupManager: ApkBackupManager by inject() + private val nm: BackupNotificationManager by inject() + + override suspend fun doWork(): Result { + try { + setForeground(createForegroundInfo()) + } catch (e: Exception) { + Log.e(TAG, "Error while running setForeground: ", e) + } + var result: Result = Result.success() + try { + Log.i(TAG, "Starting APK backup...") + apkBackupManager.backup() + } catch (e: Exception) { + Log.e(TAG, "Error backing up APKs: ", e) + result = Result.retry() + } finally { + Log.i(TAG, "Requesting app data backup...") + val requestSuccess = try { + if (backupRequester.isBackupEnabled) { + Log.d(TAG, "Backup is enabled, request backup...") + backupRequester.requestBackup() + } else true + } finally { + // schedule next backup, because the old one gets lost + // when scheduling a OneTimeWorkRequest with the same unique name via scheduleNow() + if (tags.contains(TAG_NOW)) { + // needs to use CANCEL_AND_REENQUEUE otherwise it doesn't get scheduled + schedule(applicationContext, CANCEL_AND_REENQUEUE) + } + } + if (!requestSuccess) result = Result.retry() + } + return result + } + + private fun createForegroundInfo() = ForegroundInfo( + NOTIFICATION_ID_OBSERVER, + nm.getBackupNotification(""), + FOREGROUND_SERVICE_TYPE_DATA_SYNC, + ) +} diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupRequester.kt b/app/src/main/java/com/stevesoltys/seedvault/worker/BackupRequester.kt similarity index 95% rename from app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupRequester.kt rename to app/src/main/java/com/stevesoltys/seedvault/worker/BackupRequester.kt index 209db7622..9eac74069 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupRequester.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/worker/BackupRequester.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.stevesoltys.seedvault.transport.backup +package com.stevesoltys.seedvault.worker import android.app.backup.BackupManager import android.app.backup.IBackupManager @@ -12,6 +12,7 @@ import android.os.RemoteException import android.util.Log import androidx.annotation.WorkerThread import com.stevesoltys.seedvault.BackupMonitor +import com.stevesoltys.seedvault.transport.backup.PackageService import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.notification.NotificationBackupObserver import org.koin.core.component.KoinComponent @@ -34,6 +35,8 @@ internal class BackupRequester( val packageService: PackageService, ) : KoinComponent { + val isBackupEnabled: Boolean get() = backupManager.isBackupEnabled + private val packages = packageService.eligiblePackages private val observer = NotificationBackupObserver( context = context, diff --git a/app/src/main/java/com/stevesoltys/seedvault/worker/WorkerModule.kt b/app/src/main/java/com/stevesoltys/seedvault/worker/WorkerModule.kt index ed18a6352..dce45be2d 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/worker/WorkerModule.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/worker/WorkerModule.kt @@ -9,6 +9,13 @@ import org.koin.android.ext.koin.androidContext import org.koin.dsl.module val workerModule = module { + factory { + BackupRequester( + context = androidContext(), + backupManager = get(), + packageService = get(), + ) + } single { ApkBackup( pm = androidContext().packageManager,