diff --git a/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt b/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt index f5dddd70..39135ed3 100644 --- a/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt +++ b/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt @@ -49,7 +49,7 @@ class PluginTest : KoinComponent { @Before fun setup() = runBlocking { - every { mockedSettingsManager.getStorage() } returns settingsManager.getStorage() + every { mockedSettingsManager.getSafStorage() } returns settingsManager.getSafStorage() storage.rootBackupDir?.deleteContents(context) ?: error("Select a storage location in the app first!") } @@ -76,7 +76,7 @@ class PluginTest : KoinComponent { fun testInitializationAndRestoreSets() = runBlocking(Dispatchers.IO) { // no backups available initially assertEquals(0, storagePlugin.getAvailableBackups()?.toList()?.size) - val s = settingsManager.getStorage() ?: error("no storage") + val s = settingsManager.getSafStorage() ?: error("no storage") assertFalse(storagePlugin.hasBackup(s)) // prepare returned tokens requested when initializing device diff --git a/app/src/androidTest/java/com/stevesoltys/seedvault/e2e/impl/BackupRestoreTest.kt b/app/src/androidTest/java/com/stevesoltys/seedvault/e2e/impl/BackupRestoreTest.kt index 83e638b6..78c0ab7b 100644 --- a/app/src/androidTest/java/com/stevesoltys/seedvault/e2e/impl/BackupRestoreTest.kt +++ b/app/src/androidTest/java/com/stevesoltys/seedvault/e2e/impl/BackupRestoreTest.kt @@ -17,7 +17,7 @@ internal class BackupRestoreTest : SeedvaultLargeTest() { confirmCode() } - if (settingsManager.getStorage() == null) { + if (settingsManager.getSafStorage() == null) { chooseStorageLocation() } else { changeBackupLocation() diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt index 6f023d22..b7519cae 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt @@ -2,7 +2,7 @@ package com.stevesoltys.seedvault.plugins import android.app.backup.RestoreSet import androidx.annotation.WorkerThread -import com.stevesoltys.seedvault.settings.Storage +import com.stevesoltys.seedvault.plugins.saf.SafStorage import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -59,7 +59,7 @@ interface StoragePlugin { */ @WorkerThread @Throws(IOException::class) - suspend fun hasBackup(storage: Storage): Boolean + suspend fun hasBackup(safStorage: SafStorage): Boolean /** * Get the set of all backups currently available for restore. diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt index 56e49c2a..9f3dab51 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt @@ -9,7 +9,6 @@ import com.stevesoltys.seedvault.plugins.EncryptedMetadata import com.stevesoltys.seedvault.plugins.StoragePlugin import com.stevesoltys.seedvault.plugins.chunkFolderRegex import com.stevesoltys.seedvault.plugins.tokenRegex -import com.stevesoltys.seedvault.settings.Storage import java.io.FileNotFoundException import java.io.IOException import java.io.InputStream @@ -28,7 +27,7 @@ internal class DocumentsProviderStoragePlugin( */ private val context: Context get() = appContext.getStorageContext { - storage.storage?.isUsb == true + storage.safStorage?.isUsb == true } private val packageManager: PackageManager = appContext.packageManager @@ -79,10 +78,10 @@ internal class DocumentsProviderStoragePlugin( } @Throws(IOException::class) - override suspend fun hasBackup(storage: Storage): Boolean { + override suspend fun hasBackup(safStorage: SafStorage): Boolean { // potentially get system user context if needed here - val c = appContext.getStorageContext { storage.isUsb } - val parent = DocumentFile.fromTreeUri(c, storage.uri) ?: throw AssertionError() + val c = appContext.getStorageContext { safStorage.isUsb } + val parent = DocumentFile.fromTreeUri(c, safStorage.uri) ?: throw AssertionError() val rootDir = parent.findFileBlocking(c, DIRECTORY_ROOT) ?: return false val backupSets = getBackups(c, rootDir) return backupSets.isNotEmpty() diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsStorage.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsStorage.kt index 593f4943..e96b29e2 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsStorage.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsStorage.kt @@ -18,7 +18,6 @@ import androidx.annotation.VisibleForTesting import androidx.documentfile.provider.DocumentFile import com.stevesoltys.seedvault.getStorageContext import com.stevesoltys.seedvault.settings.SettingsManager -import com.stevesoltys.seedvault.settings.Storage import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine @@ -45,9 +44,9 @@ internal class DocumentsStorage( private val appContext: Context, private val settingsManager: SettingsManager, ) { - internal var storage: Storage? = null + internal var safStorage: SafStorage? = null get() { - if (field == null) field = settingsManager.getStorage() + if (field == null) field = settingsManager.getSafStorage() return field } @@ -56,14 +55,14 @@ internal class DocumentsStorage( */ private val context: Context get() = appContext.getStorageContext { - storage?.isUsb == true + safStorage?.isUsb == true } private val contentResolver: ContentResolver get() = context.contentResolver internal var rootBackupDir: DocumentFile? = null get() = runBlocking { if (field == null) { - val parent = storage?.getDocumentFile(context) + val parent = safStorage?.getDocumentFile(context) ?: return@runBlocking null field = try { parent.createOrGetDirectory(context, DIRECTORY_ROOT).apply { @@ -104,13 +103,13 @@ internal class DocumentsStorage( * Resets this storage abstraction, forcing it to re-fetch cached values on next access. */ fun reset(newToken: Long?) { - storage = null + safStorage = null currentToken = newToken rootBackupDir = null currentSetDir = null } - fun getAuthority(): String? = storage?.uri?.authority + fun getAuthority(): String? = safStorage?.uri?.authority @Throws(IOException::class) suspend fun getSetDir(token: Long = currentToken ?: error("no token")): DocumentFile? { diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/SafStorage.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/SafStorage.kt new file mode 100644 index 00000000..5b9cdfb8 --- /dev/null +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/SafStorage.kt @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2024 The Calyx Institute + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.stevesoltys.seedvault.plugins.saf + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.net.Uri +import androidx.annotation.WorkerThread +import androidx.documentfile.provider.DocumentFile + +data class SafStorage( + val uri: Uri, + val name: String, + val isUsb: Boolean, + val requiresNetwork: Boolean, +) { + fun getDocumentFile(context: Context) = DocumentFile.fromTreeUri(context, uri) + ?: throw AssertionError("Should only happen on API < 21.") + + /** + * Returns true if this is USB storage that is not available, false otherwise. + * + * Must be run off UI thread (ideally I/O). + */ + @WorkerThread + fun isUnavailableUsb(context: Context): Boolean { + return isUsb && !getDocumentFile(context).isDirectory + } + + /** + * Returns true if this is storage that requires network access, + * but it isn't available right now. + */ + fun isUnavailableNetwork(context: Context, allowMetered: Boolean): Boolean { + return requiresNetwork && !hasUnmeteredInternet(context, allowMetered) + } + + private fun hasUnmeteredInternet(context: Context, allowMetered: Boolean): Boolean { + val cm = context.getSystemService(ConnectivityManager::class.java) ?: return false + val isMetered = cm.isActiveNetworkMetered + val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false + return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + (allowMetered || !isMetered) + } +} diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/webdav/WebDavStoragePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/webdav/WebDavStoragePlugin.kt index a6e25022..075d7f87 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/plugins/webdav/WebDavStoragePlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/webdav/WebDavStoragePlugin.kt @@ -13,7 +13,7 @@ import com.stevesoltys.seedvault.plugins.chunkFolderRegex import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA import com.stevesoltys.seedvault.plugins.saf.FILE_NO_MEDIA import com.stevesoltys.seedvault.plugins.tokenRegex -import com.stevesoltys.seedvault.settings.Storage +import com.stevesoltys.seedvault.plugins.saf.SafStorage import okhttp3.HttpUrl.Companion.toHttpUrl import java.io.IOException import java.io.InputStream @@ -135,7 +135,7 @@ internal class WebDavStoragePlugin( } @Throws(IOException::class) - override suspend fun hasBackup(storage: Storage): Boolean { + override suspend fun hasBackup(safStorage: SafStorage): Boolean { // TODO this requires refactoring return true } 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 be3796a6..f1bcdc39 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt @@ -29,7 +29,7 @@ class SchedulingFragment : PreferenceFragmentCompat(), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val storage = settingsManager.getStorage() + val storage = settingsManager.getSafStorage() if (storage?.isUsb == true) { findPreference("scheduling_category_conditions")?.isEnabled = false } 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 e76bcf82..7d821147 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt @@ -22,6 +22,7 @@ import androidx.preference.TwoStatePreference import androidx.work.WorkInfo import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.permitDiskReads +import com.stevesoltys.seedvault.plugins.saf.SafStorage import com.stevesoltys.seedvault.restore.RestoreActivity import com.stevesoltys.seedvault.ui.toRelativeTime import org.koin.android.ext.android.inject @@ -48,7 +49,7 @@ class SettingsFragment : PreferenceFragmentCompat() { private var menuBackupNow: MenuItem? = null private var menuRestore: MenuItem? = null - private var storage: Storage? = null + private var safStorage: SafStorage? = null override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { permitDiskReads { @@ -164,7 +165,7 @@ class SettingsFragment : PreferenceFragmentCompat() { // we need to re-set the title when returning to this fragment activity?.setTitle(R.string.backup) - storage = settingsManager.getStorage() + safStorage = settingsManager.getSafStorage() setBackupEnabledState() setBackupLocationSummary() setAutoRestoreState() @@ -241,7 +242,7 @@ class SettingsFragment : PreferenceFragmentCompat() { activity?.contentResolver?.let { autoRestore.isChecked = Settings.Secure.getInt(it, BACKUP_AUTO_RESTORE, 1) == 1 } - val storage = this.storage + val storage = this.safStorage if (storage?.isUsb == true) { autoRestore.summary = getString(R.string.settings_auto_restore_summary) + "\n\n" + getString(R.string.settings_auto_restore_summary_usb, storage.name) @@ -252,7 +253,7 @@ class SettingsFragment : PreferenceFragmentCompat() { private fun setBackupLocationSummary() { // get name of storage location - backupLocation.summary = storage?.name ?: getString(R.string.settings_backup_location_none) + backupLocation.summary = safStorage?.name ?: getString(R.string.settings_backup_location_none) } private fun setAppBackupStatusSummary(lastBackupInMillis: Long?) { @@ -271,7 +272,7 @@ class SettingsFragment : PreferenceFragmentCompat() { * says that nothing is scheduled which can happen when backup destination is on flash drive. */ private fun setAppBackupSchedulingSummary(workInfo: WorkInfo?) { - if (storage?.isUsb == true) { + if (safStorage?.isUsb == true) { backupScheduling.summary = getString(R.string.settings_backup_status_next_backup_usb) return } diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsManager.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsManager.kt index 9584ffea..dfaf42da 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsManager.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsManager.kt @@ -12,6 +12,7 @@ import androidx.documentfile.provider.DocumentFile import androidx.preference.PreferenceManager import com.stevesoltys.seedvault.getStorageContext import com.stevesoltys.seedvault.permitDiskReads +import com.stevesoltys.seedvault.plugins.saf.SafStorage import com.stevesoltys.seedvault.transport.backup.BackupCoordinator import java.util.concurrent.ConcurrentSkipListSet @@ -88,24 +89,24 @@ class SettingsManager(private val context: Context) { token = newToken } - // FIXME Storage is currently plugin specific and not generic - fun setStorage(storage: Storage) { + // FIXME SafStorage is currently plugin specific and not generic + fun setSafStorage(safStorage: SafStorage) { prefs.edit() - .putString(PREF_KEY_STORAGE_URI, storage.uri.toString()) - .putString(PREF_KEY_STORAGE_NAME, storage.name) - .putBoolean(PREF_KEY_STORAGE_IS_USB, storage.isUsb) - .putBoolean(PREF_KEY_STORAGE_REQUIRES_NETWORK, storage.requiresNetwork) + .putString(PREF_KEY_STORAGE_URI, safStorage.uri.toString()) + .putString(PREF_KEY_STORAGE_NAME, safStorage.name) + .putBoolean(PREF_KEY_STORAGE_IS_USB, safStorage.isUsb) + .putBoolean(PREF_KEY_STORAGE_REQUIRES_NETWORK, safStorage.requiresNetwork) .apply() } - fun getStorage(): Storage? { + fun getSafStorage(): SafStorage? { val uriStr = prefs.getString(PREF_KEY_STORAGE_URI, null) ?: return null val uri = Uri.parse(uriStr) val name = prefs.getString(PREF_KEY_STORAGE_NAME, null) ?: throw IllegalStateException("no storage name") val isUsb = prefs.getBoolean(PREF_KEY_STORAGE_IS_USB, false) val requiresNetwork = prefs.getBoolean(PREF_KEY_STORAGE_REQUIRES_NETWORK, false) - return Storage(uri, name, isUsb, requiresNetwork) + return SafStorage(uri, name, isUsb, requiresNetwork) } fun setFlashDrive(usb: FlashDrive?) { @@ -144,7 +145,7 @@ class SettingsManager(private val context: Context) { */ @WorkerThread fun canDoBackupNow(): Boolean { - val storage = getStorage() ?: return false + val storage = getSafStorage() ?: return false val systemContext = context.getStorageContext { storage.isUsb } return !storage.isUnavailableUsb(systemContext) && !storage.isUnavailableNetwork(context, useMeteredNetwork) 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 99310110..2fd78d1f 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt @@ -131,7 +131,7 @@ internal class SettingsViewModel( } override fun onStorageLocationChanged() { - val storage = settingsManager.getStorage() ?: return + val storage = settingsManager.getSafStorage() ?: return Log.i(TAG, "onStorageLocationChanged (isUsb: ${storage.isUsb}") if (storage.isUsb) { @@ -156,7 +156,7 @@ internal class SettingsViewModel( } private fun onStoragePropertiesChanged() { - val storage = settingsManager.getStorage() ?: return + val storage = settingsManager.getSafStorage() ?: return Log.d(TAG, "onStoragePropertiesChanged") // register storage observer @@ -200,7 +200,7 @@ internal class SettingsViewModel( i.putExtra(EXTRA_START_APP_BACKUP, true) startForegroundService(app, i) } else { - val isUsb = settingsManager.getStorage()?.isUsb ?: false + val isUsb = settingsManager.getSafStorage()?.isUsb ?: false AppBackupWorker.scheduleNow(app, reschedule = !isUsb) } } @@ -280,14 +280,14 @@ internal class SettingsViewModel( } fun scheduleAppBackup(existingWorkPolicy: ExistingPeriodicWorkPolicy) { - val storage = settingsManager.getStorage() ?: error("no storage available") + val storage = settingsManager.getSafStorage() ?: error("no storage available") if (!storage.isUsb && backupManager.isBackupEnabled) { AppBackupWorker.schedule(app, settingsManager, existingWorkPolicy) } } fun scheduleFilesBackup() { - val storage = settingsManager.getStorage() ?: error("no storage available") + val storage = settingsManager.getSafStorage() ?: error("no storage available") if (!storage.isUsb && settingsManager.isStorageBackupEnabled()) { BackupJobService.scheduleJob( context = app, diff --git a/app/src/main/java/com/stevesoltys/seedvault/storage/SeedvaultSafStoragePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/storage/SeedvaultSafStoragePlugin.kt index 3e98116a..e78dd907 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/storage/SeedvaultSafStoragePlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/storage/SeedvaultSafStoragePlugin.kt @@ -18,7 +18,7 @@ internal class SeedvaultSafStoragePlugin( */ override val context: Context get() = appContext.getStorageContext { - storage.storage?.isUsb == true + storage.safStorage?.isUsb == true } override val root: DocumentFile get() = storage.rootBackupDir ?: error("No storage set") 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 5a9096d3..f7b0c828 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt @@ -43,7 +43,7 @@ internal class StorageBackupService : BackupService() { override fun onBackupFinished(intent: Intent, success: Boolean) { if (intent.getBooleanExtra(EXTRA_START_APP_BACKUP, false)) { - val isUsb = settingsManager.getStorage()?.isUsb ?: false + val isUsb = settingsManager.getSafStorage()?.isUsb ?: false AppBackupWorker.scheduleNow(applicationContext, reschedule = !isUsb) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt index 5846c4aa..8bf4d4ba 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt @@ -411,7 +411,7 @@ internal class BackupCoordinator( val longBackoff = DAYS.toMillis(30) // back off if there's no storage set - val storage = settingsManager.getStorage() ?: return longBackoff + val storage = settingsManager.getSafStorage() ?: return longBackoff return when { // back off if storage is removable and not available right now storage.isUnavailableUsb(context) -> longBackoff diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt index 68431258..1111a750 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt @@ -169,7 +169,7 @@ internal class RestoreCoordinator( // check if we even have a backup of that app if (metadataManager.getPackageMetadata(pmPackageName) != null) { // remind user to plug in storage device - val storageName = settingsManager.getStorage()?.name + val storageName = settingsManager.getSafStorage()?.name ?: context.getString(R.string.settings_backup_location_none) notificationManager.onRemovableStorageNotAvailableForRestore( pmPackageName, @@ -365,7 +365,7 @@ internal class RestoreCoordinator( // TODO this is plugin specific, needs to be factored out when supporting different plugins private fun isStorageRemovableAndNotAvailable(): Boolean { - val storage = settingsManager.getStorage() ?: return false + val storage = settingsManager.getSafStorage() ?: return false return storage.isUnavailableUsb(context) } 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 7ba5c695..76f08757 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 @@ -74,7 +74,7 @@ internal class BackupStorageViewModel( } private fun scheduleBackupWorkers() { - val storage = settingsManager.getStorage() ?: error("no storage available") + val storage = settingsManager.getSafStorage() ?: error("no storage available") if (!storage.isUsb) { if (backupManager.isBackupEnabled) { AppBackupWorker.schedule(app, settingsManager, CANCEL_AND_REENQUEUE) diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageViewModel.kt index 9562e13e..b3673d2a 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageViewModel.kt @@ -16,10 +16,10 @@ import androidx.lifecycle.viewModelScope import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.isMassStorage import com.stevesoltys.seedvault.permitDiskReads +import com.stevesoltys.seedvault.plugins.saf.SafStorage import com.stevesoltys.seedvault.settings.BackupManagerSettings import com.stevesoltys.seedvault.settings.FlashDrive import com.stevesoltys.seedvault.settings.SettingsManager -import com.stevesoltys.seedvault.settings.Storage import com.stevesoltys.seedvault.ui.LiveEvent import com.stevesoltys.seedvault.ui.MutableLiveEvent import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption @@ -47,7 +47,7 @@ internal abstract class StorageViewModel( internal var isSetupWizard: Boolean = false internal val hasStorageSet: Boolean - get() = settingsManager.getStorage() != null + get() = settingsManager.getSafStorage() != null abstract val isRestoreOperation: Boolean companion object { @@ -55,7 +55,7 @@ internal abstract class StorageViewModel( context: Context, settingsManager: SettingsManager, ): Boolean { - val storage = settingsManager.getStorage() ?: return false + val storage = settingsManager.getSafStorage() ?: return false if (storage.isUsb) return true return permitDiskReads { storage.getDocumentFile(context).isDirectory @@ -106,20 +106,20 @@ internal abstract class StorageViewModel( return saveStorage(storage) } - protected fun createStorage(uri: Uri): Storage { + protected fun createStorage(uri: Uri): SafStorage { val root = safOption ?: throw IllegalStateException("no storage root") val name = if (root.isInternal()) { "${root.title} (${app.getString(R.string.settings_backup_location_internal)})" } else { root.title } - return Storage(uri, name, root.isUsb, root.requiresNetwork) + return SafStorage(uri, name, root.isUsb, root.requiresNetwork) } - protected fun saveStorage(storage: Storage): Boolean { - settingsManager.setStorage(storage) + protected fun saveStorage(safStorage: SafStorage): Boolean { + settingsManager.setSafStorage(safStorage) - if (storage.isUsb) { + if (safStorage.isUsb) { Log.d(TAG, "Selected storage is a removable USB device.") val wasSaved = saveUsbDevice() // reset stored flash drive, if we did not update it @@ -129,9 +129,9 @@ internal abstract class StorageViewModel( } BackupManagerSettings.resetDefaults(app.contentResolver) - Log.d(TAG, "New storage location saved: ${storage.uri}") + Log.d(TAG, "New storage location saved: ${safStorage.uri}") - return storage.isUsb + return safStorage.isUsb } private fun saveUsbDevice(): Boolean { diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt index 598a6ff4..9d39cf2c 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt @@ -17,7 +17,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED import com.stevesoltys.seedvault.plugins.StoragePlugin import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA -import com.stevesoltys.seedvault.settings.Storage +import com.stevesoltys.seedvault.plugins.saf.SafStorage import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.worker.ApkBackup import io.mockk.Runs @@ -58,7 +58,7 @@ internal class BackupCoordinatorTest : BackupTest() { private val metadataOutputStream = mockk() private val fileDescriptor: ParcelFileDescriptor = mockk() private val packageMetadata: PackageMetadata = mockk() - private val storage = Storage( + private val safStorage = SafStorage( uri = Uri.EMPTY, name = getRandomString(), isUsb = false, @@ -290,7 +290,7 @@ internal class BackupCoordinatorTest : BackupTest() { ) } just Runs coEvery { full.cancelFullBackup(token, metadata.salt, false) } just Runs - every { settingsManager.getStorage() } returns storage + every { settingsManager.getSafStorage() } returns safStorage every { settingsManager.useMeteredNetwork } returns false every { metadataOutputStream.close() } just Runs @@ -340,7 +340,7 @@ internal class BackupCoordinatorTest : BackupTest() { ) } just Runs coEvery { full.cancelFullBackup(token, metadata.salt, false) } just Runs - every { settingsManager.getStorage() } returns storage + every { settingsManager.getSafStorage() } returns safStorage every { settingsManager.useMeteredNetwork } returns false every { metadataOutputStream.close() } just Runs diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt index 88ba4c15..f752ad72 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt @@ -16,7 +16,7 @@ import com.stevesoltys.seedvault.metadata.MetadataReader import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.plugins.EncryptedMetadata import com.stevesoltys.seedvault.plugins.StoragePlugin -import com.stevesoltys.seedvault.settings.Storage +import com.stevesoltys.seedvault.plugins.saf.SafStorage import com.stevesoltys.seedvault.transport.TransportTest import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import io.mockk.Runs @@ -57,7 +57,7 @@ internal class RestoreCoordinatorTest : TransportTest() { ) private val inputStream = mockk() - private val storage: Storage = mockk() + private val safStorage: SafStorage = mockk() private val packageInfo2 = PackageInfo().apply { packageName = "org.example2" } private val packageInfoArray = arrayOf(packageInfo) private val packageInfoArray2 = arrayOf(packageInfo, packageInfo2) @@ -164,10 +164,10 @@ internal class RestoreCoordinatorTest : TransportTest() { @Test fun `startRestore() optimized auto-restore with removed storage shows notification`() = runBlocking { - every { settingsManager.getStorage() } returns storage - every { storage.isUnavailableUsb(context) } returns true + every { settingsManager.getSafStorage() } returns safStorage + every { safStorage.isUnavailableUsb(context) } returns true every { metadataManager.getPackageMetadata(packageName) } returns PackageMetadata(42L) - every { storage.name } returns storageName + every { safStorage.name } returns storageName every { notificationManager.onRemovableStorageNotAvailableForRestore( packageName, @@ -188,8 +188,8 @@ internal class RestoreCoordinatorTest : TransportTest() { @Test fun `startRestore() optimized auto-restore with available storage shows no notification`() = runBlocking { - every { settingsManager.getStorage() } returns storage - every { storage.isUnavailableUsb(context) } returns false + every { settingsManager.getSafStorage() } returns safStorage + every { safStorage.isUnavailableUsb(context) } returns false restore.beforeStartRestore(metadata) assertEquals(TRANSPORT_OK, restore.startRestore(token, pmPackageInfoArray)) @@ -204,8 +204,8 @@ internal class RestoreCoordinatorTest : TransportTest() { @Test fun `startRestore() with removed storage shows no notification`() = runBlocking { - every { settingsManager.getStorage() } returns storage - every { storage.isUnavailableUsb(context) } returns true + every { settingsManager.getSafStorage() } returns safStorage + every { safStorage.isUnavailableUsb(context) } returns true every { metadataManager.getPackageMetadata(packageName) } returns null assertEquals(TRANSPORT_ERROR, restore.startRestore(token, pmPackageInfoArray))