Jelajahi Sumber

refactor: 迁移 Backup 设置网关

5151561 3 bulan lalu
induk
melakukan
341420263b

+ 16 - 30
app/src/main/java/io/legado/app/data/repository/FeatureSettingsRepositories.kt

@@ -8,7 +8,6 @@ import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppShellSettingsUpdate
 import io.legado.app.domain.gateway.AppShellSettingsUpdate
 import io.legado.app.domain.gateway.AppShellStringSetting
 import io.legado.app.domain.gateway.AppShellStringSetting
 import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
-import io.legado.app.domain.gateway.BackupSettingsUpdate
 import io.legado.app.domain.gateway.CoverSettingsGateway
 import io.legado.app.domain.gateway.CoverSettingsGateway
 import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.domain.gateway.LabSettingsGateway
 import io.legado.app.domain.gateway.LabSettingsGateway
@@ -255,35 +254,8 @@ class BackupSettingsRepository : BackupSettingsGateway {
         .map { it.toBackupSettings() }
         .map { it.toBackupSettings() }
         .distinctUntilChanged()
         .distinctUntilChanged()
 
 
-    override suspend fun update(update: BackupSettingsUpdate) = updateAll(listOf(update))
-
-    override suspend fun updateAll(updates: List<BackupSettingsUpdate>) {
-        val values = buildMap {
-            updates.forEach { update ->
-                when (update) {
-                    is BackupSettingsUpdate.WebDavUrl -> put(PreferKey.webDavUrl, update.value)
-                    is BackupSettingsUpdate.WebDavCredentials -> {
-                        put(PreferKey.webDavAccount, update.account)
-                        put(PreferKey.webDavPassword, update.password)
-                    }
-                    is BackupSettingsUpdate.WebDavDir -> put(PreferKey.webDavDir, update.value)
-                    is BackupSettingsUpdate.WebDavDeviceName ->
-                        put(PreferKey.webDavDeviceName, update.value)
-                    is BackupSettingsUpdate.SyncBookProgress ->
-                        put(PreferKey.syncBookProgress, update.value)
-                    is BackupSettingsUpdate.SyncBookProgressPlus ->
-                        put(PreferKey.syncBookProgressPlus, update.value)
-                    is BackupSettingsUpdate.AutoCheckNewBackup ->
-                        put(PreferKey.autoCheckNewBackup, update.value)
-                    is BackupSettingsUpdate.OnlyLatestBackup ->
-                        put(PreferKey.onlyLatestBackup, update.value)
-                    is BackupSettingsUpdate.BackupSyncMode ->
-                        put(PreferKey.backupSyncMode, update.value)
-                    is BackupSettingsUpdate.BackupPath -> put(PreferKey.backupPath, update.value)
-                }
-            }
-        }
-        AppConfigStore.putAll(values)
+    override suspend fun update(transform: (BackupSettings) -> BackupSettings) {
+        AppConfigStore.putAll(currentSettings.diffPrefMap(transform, BackupSettings::toPrefMap))
     }
     }
 }
 }
 
 
@@ -402,6 +374,20 @@ internal fun Preferences.toBackupSettings(): BackupSettings = BackupSettings(
     backupPath = compatDsString(PreferKey.backupPath),
     backupPath = compatDsString(PreferKey.backupPath),
 )
 )
 
 
+internal fun BackupSettings.toPrefMap(): Map<String, Any?> = mapOf(
+    PreferKey.webDavUrl to webDavUrl,
+    PreferKey.webDavAccount to webDavAccount,
+    PreferKey.webDavPassword to webDavPassword,
+    PreferKey.webDavDir to webDavDir,
+    PreferKey.webDavDeviceName to webDavDeviceName,
+    PreferKey.syncBookProgress to syncBookProgress,
+    PreferKey.syncBookProgressPlus to syncBookProgressPlus,
+    PreferKey.autoCheckNewBackup to autoCheckNewBackup,
+    PreferKey.onlyLatestBackup to onlyLatestBackup,
+    PreferKey.backupSyncMode to backupSyncMode,
+    PreferKey.backupPath to backupPath,
+)
+
 internal fun Preferences.toAppShellSettings(): AppShellSettings = AppShellSettings(
 internal fun Preferences.toAppShellSettings(): AppShellSettings = AppShellSettings(
     themeMode = compatDsString(PreferKey.themeMode) ?: "0",
     themeMode = compatDsString(PreferKey.themeMode) ?: "0",
     fontScale = compatDsInt(PreferKey.fontScale) ?: 10,
     fontScale = compatDsInt(PreferKey.fontScale) ?: 10,

+ 1 - 15
app/src/main/java/io/legado/app/domain/gateway/BackupSettingsGateway.kt

@@ -6,19 +6,5 @@ import kotlinx.coroutines.flow.Flow
 interface BackupSettingsGateway {
 interface BackupSettingsGateway {
     val currentSettings: BackupSettings
     val currentSettings: BackupSettings
     val settings: Flow<BackupSettings>
     val settings: Flow<BackupSettings>
-    suspend fun update(update: BackupSettingsUpdate)
-    suspend fun updateAll(updates: List<BackupSettingsUpdate>)
-}
-
-sealed interface BackupSettingsUpdate {
-    data class WebDavUrl(val value: String) : BackupSettingsUpdate
-    data class WebDavCredentials(val account: String, val password: String) : BackupSettingsUpdate
-    data class WebDavDir(val value: String) : BackupSettingsUpdate
-    data class WebDavDeviceName(val value: String) : BackupSettingsUpdate
-    data class SyncBookProgress(val value: Boolean) : BackupSettingsUpdate
-    data class SyncBookProgressPlus(val value: Boolean) : BackupSettingsUpdate
-    data class AutoCheckNewBackup(val value: Boolean) : BackupSettingsUpdate
-    data class OnlyLatestBackup(val value: Boolean) : BackupSettingsUpdate
-    data class BackupSyncMode(val value: String) : BackupSettingsUpdate
-    data class BackupPath(val value: String?) : BackupSettingsUpdate
+    suspend fun update(transform: (BackupSettings) -> BackupSettings)
 }
 }

+ 19 - 18
app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt

@@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
-import io.legado.app.domain.gateway.BackupSettingsUpdate
+import io.legado.app.domain.model.settings.BackupSettings
 import io.legado.app.domain.usecase.BackupRestoreUseCase
 import io.legado.app.domain.usecase.BackupRestoreUseCase
 import io.legado.app.domain.usecase.WebDavBackupUseCase
 import io.legado.app.domain.usecase.WebDavBackupUseCase
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.isContentScheme
@@ -48,19 +48,19 @@ class BackupConfigViewModel(
 
 
     fun onIntent(intent: BackupConfigIntent) {
     fun onIntent(intent: BackupConfigIntent) {
         when (intent) {
         when (intent) {
-            is BackupConfigIntent.SetWebDavUrl -> update(BackupSettingsUpdate.WebDavUrl(intent.value))
-            is BackupConfigIntent.SetWebDavDir -> update(BackupSettingsUpdate.WebDavDir(intent.value))
+            is BackupConfigIntent.SetWebDavUrl -> update { it.copy(webDavUrl = intent.value) }
+            is BackupConfigIntent.SetWebDavDir -> update { it.copy(webDavDir = intent.value) }
             is BackupConfigIntent.SetWebDavDeviceName ->
             is BackupConfigIntent.SetWebDavDeviceName ->
-                update(BackupSettingsUpdate.WebDavDeviceName(intent.value))
+                update { it.copy(webDavDeviceName = intent.value) }
             is BackupConfigIntent.SetSyncBookProgress -> setSyncBookProgress(intent.value)
             is BackupConfigIntent.SetSyncBookProgress -> setSyncBookProgress(intent.value)
             is BackupConfigIntent.SetSyncBookProgressPlus ->
             is BackupConfigIntent.SetSyncBookProgressPlus ->
-                update(BackupSettingsUpdate.SyncBookProgressPlus(intent.value))
+                update { it.copy(syncBookProgressPlus = intent.value) }
             is BackupConfigIntent.SetAutoCheckNewBackup ->
             is BackupConfigIntent.SetAutoCheckNewBackup ->
-                update(BackupSettingsUpdate.AutoCheckNewBackup(intent.value))
+                update { it.copy(autoCheckNewBackup = intent.value) }
             is BackupConfigIntent.SetOnlyLatestBackup ->
             is BackupConfigIntent.SetOnlyLatestBackup ->
-                update(BackupSettingsUpdate.OnlyLatestBackup(intent.value))
+                update { it.copy(onlyLatestBackup = intent.value) }
             is BackupConfigIntent.SetBackupSyncMode ->
             is BackupConfigIntent.SetBackupSyncMode ->
-                update(BackupSettingsUpdate.BackupSyncMode(intent.value))
+                update { it.copy(backupSyncMode = intent.value) }
             is BackupConfigIntent.OpenSheet -> _uiState.update { it.copy(activeSheet = intent.sheet) }
             is BackupConfigIntent.OpenSheet -> _uiState.update { it.copy(activeSheet = intent.sheet) }
             BackupConfigIntent.DismissSheet -> _uiState.update { it.copy(activeSheet = null) }
             BackupConfigIntent.DismissSheet -> _uiState.update { it.copy(activeSheet = null) }
             BackupConfigIntent.OpenWebDavAuth -> openWebDavAuth()
             BackupConfigIntent.OpenWebDavAuth -> openWebDavAuth()
@@ -107,17 +107,18 @@ class BackupConfigViewModel(
         }
         }
     }
     }
 
 
-    private fun update(update: BackupSettingsUpdate) {
-        viewModelScope.launch { settingsGateway.update(update) }
+    private fun update(transform: (BackupSettings) -> BackupSettings) {
+        viewModelScope.launch { settingsGateway.update(transform) }
     }
     }
 
 
     private fun setSyncBookProgress(value: Boolean) {
     private fun setSyncBookProgress(value: Boolean) {
         viewModelScope.launch {
         viewModelScope.launch {
-            val updates = buildList {
-                add(BackupSettingsUpdate.SyncBookProgress(value))
-                if (!value) add(BackupSettingsUpdate.SyncBookProgressPlus(false))
+            settingsGateway.update {
+                it.copy(
+                    syncBookProgress = value,
+                    syncBookProgressPlus = it.syncBookProgressPlus && value,
+                )
             }
             }
-            settingsGateway.updateAll(updates)
         }
         }
     }
     }
 
 
@@ -143,9 +144,9 @@ class BackupConfigViewModel(
     private fun saveWebDavAuth() {
     private fun saveWebDavAuth() {
         val dialog = _uiState.value.activeDialog as? BackupConfigDialog.WebDavAuth ?: return
         val dialog = _uiState.value.activeDialog as? BackupConfigDialog.WebDavAuth ?: return
         viewModelScope.launch {
         viewModelScope.launch {
-            settingsGateway.update(
-                BackupSettingsUpdate.WebDavCredentials(dialog.account, dialog.password)
-            )
+            settingsGateway.update {
+                it.copy(webDavAccount = dialog.account, webDavPassword = dialog.password)
+            }
             testWebDav()
             testWebDav()
         }
         }
     }
     }
@@ -253,7 +254,7 @@ class BackupConfigViewModel(
 
 
     private fun saveBackupPath(path: String, runBackup: Boolean) {
     private fun saveBackupPath(path: String, runBackup: Boolean) {
         viewModelScope.launch {
         viewModelScope.launch {
-            settingsGateway.update(BackupSettingsUpdate.BackupPath(path))
+            settingsGateway.update { it.copy(backupPath = path) }
             if (runBackup && path.isNotEmpty()) requestBackup("both", path)
             if (runBackup && path.isNotEmpty()) requestBackup("both", path)
         }
         }
     }
     }

+ 1 - 2
app/src/main/java/io/legado/app/ui/main/home/HomeViewModel.kt

@@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.data.repository.BookRepository
 import io.legado.app.data.repository.BookRepository
 import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
-import io.legado.app.domain.gateway.BackupSettingsUpdate
 import io.legado.app.domain.model.HomeDashboardSection
 import io.legado.app.domain.model.HomeDashboardSection
 import io.legado.app.domain.model.HomeReadingBook
 import io.legado.app.domain.model.HomeReadingBook
 import io.legado.app.domain.model.WebDavBackup
 import io.legado.app.domain.model.WebDavBackup
@@ -247,7 +246,7 @@ class HomeViewModel(
             try {
             try {
                 runCatching {
                 runCatching {
                     if (savePath) {
                     if (savePath) {
-                        backupSettingsGateway.update(BackupSettingsUpdate.BackupPath(path))
+                        backupSettingsGateway.update { it.copy(backupPath = path) }
                     }
                     }
                     if (destination != HomeBackupDestination.Local) {
                     if (destination != HomeBackupDestination.Local) {
                         webDavBackupUseCase.refreshConfig()
                         webDavBackupUseCase.refreshConfig()

+ 6 - 6
app/src/main/java/io/legado/app/ui/welcome/WebDavFragment.kt

@@ -15,7 +15,6 @@ import io.legado.app.constant.AppLog
 import io.legado.app.databinding.FragmentWebdavAuthBinding
 import io.legado.app.databinding.FragmentWebdavAuthBinding
 import io.legado.app.domain.usecase.WebDavBackupUseCase
 import io.legado.app.domain.usecase.WebDavBackupUseCase
 import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
-import io.legado.app.domain.gateway.BackupSettingsUpdate
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
@@ -132,12 +131,13 @@ class WebDavFragment : BaseFragment(R.layout.fragment_webdav_auth) {
     }
     }
 
 
     suspend fun saveWebDavConfig(config: WebDavConfig, onSaved: (() -> Unit)? = null) {
     suspend fun saveWebDavConfig(config: WebDavConfig, onSaved: (() -> Unit)? = null) {
-        backupSettingsGateway.updateAll(
-            listOf(
-                BackupSettingsUpdate.WebDavUrl(config.url),
-                BackupSettingsUpdate.WebDavCredentials(config.account, config.password),
+        backupSettingsGateway.update {
+            it.copy(
+                webDavUrl = config.url,
+                webDavAccount = config.account,
+                webDavPassword = config.password,
             )
             )
-        )
+        }
         onSaved?.invoke()
         onSaved?.invoke()
     }
     }
 
 

+ 77 - 0
app/src/test/java/io/legado/app/data/repository/BackupSettingsMappingTest.kt

@@ -0,0 +1,77 @@
+package io.legado.app.data.repository
+
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.mutablePreferencesOf
+import io.legado.app.constant.PreferKey
+import io.legado.app.domain.model.settings.BackupSettings
+import io.legado.app.help.config.setPrefValue
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BackupSettingsMappingTest {
+
+    @Test
+    fun `备份设置各字段写读映射往返恒等且可识别布尔键交换`() {
+        val base = BackupSettings(
+            webDavUrl = "https://backup.example.test/dav",
+            webDavAccount = "backup-account",
+            webDavPassword = "backup-password",
+            webDavDir = "backup-directory",
+            webDavDeviceName = "backup-device",
+            backupSyncMode = "local",
+            backupPath = "content://backup/path",
+        )
+        val samples = listOf(
+            base,
+            base.copy(syncBookProgress = false),
+            base.copy(syncBookProgressPlus = true),
+            base.copy(autoCheckNewBackup = false),
+            base.copy(onlyLatestBackup = false),
+        )
+
+        samples.forEach { expected ->
+            assertEquals(expected, expected.toPrefMap().toPreferences().toBackupSettings())
+        }
+    }
+
+    @Test
+    fun `copy 同时变更多字段只产生对应差量并支持删除 nullable 键`() {
+        val current = BackupSettings(
+            webDavAccount = "old-account",
+            webDavPassword = "old-password",
+            syncBookProgress = true,
+            syncBookProgressPlus = true,
+            backupPath = "content://old/path",
+        )
+
+        val diff = current.diffPrefMap(
+            transform = {
+                it.copy(
+                    webDavAccount = "new-account",
+                    webDavPassword = "new-password",
+                    syncBookProgress = false,
+                    syncBookProgressPlus = false,
+                    backupPath = null,
+                )
+            },
+            toPrefMap = BackupSettings::toPrefMap,
+        )
+
+        assertEquals(
+            mapOf(
+                PreferKey.webDavAccount to "new-account",
+                PreferKey.webDavPassword to "new-password",
+                PreferKey.syncBookProgress to false,
+                PreferKey.syncBookProgressPlus to false,
+                PreferKey.backupPath to null,
+            ),
+            diff,
+        )
+    }
+}
+
+private fun Map<String, Any?>.toPreferences(): Preferences {
+    val preferences = mutablePreferencesOf()
+    forEach { (key, value) -> preferences.setPrefValue(key, value) }
+    return preferences
+}