|
|
@@ -18,7 +18,18 @@ import io.legado.app.utils.putPrefInt
|
|
|
import io.legado.app.utils.putPrefLong
|
|
|
import io.legado.app.utils.putPrefString
|
|
|
import io.legado.app.utils.putPrefStringSync
|
|
|
+import io.legado.app.help.config.DsSync
|
|
|
+import androidx.datastore.preferences.core.*
|
|
|
+import io.legado.app.data.repository.dataStore
|
|
|
+import kotlinx.coroutines.CoroutineScope
|
|
|
+import kotlinx.coroutines.Dispatchers
|
|
|
+import kotlinx.coroutines.Job
|
|
|
+import kotlinx.coroutines.flow.catch
|
|
|
+import kotlinx.coroutines.flow.distinctUntilChanged
|
|
|
+import kotlinx.coroutines.flow.map
|
|
|
+import kotlinx.coroutines.launch
|
|
|
import splitties.init.appCtx
|
|
|
+import java.io.IOException
|
|
|
import kotlin.properties.ReadWriteProperty
|
|
|
import kotlin.reflect.KProperty
|
|
|
|
|
|
@@ -41,6 +52,7 @@ fun <T> prefDelegate(
|
|
|
return object : PrefDelegate<T>, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver {
|
|
|
private var _value: MutableState<T> = mutableStateOf(readInitialValue())
|
|
|
override val state: State<T> get() = _value
|
|
|
+ private var dsObserverJob: Job? = null
|
|
|
|
|
|
init {
|
|
|
if (lifecycleOwner != null) {
|
|
|
@@ -48,6 +60,40 @@ fun <T> prefDelegate(
|
|
|
} else {
|
|
|
appCtx.defaultSharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
|
|
}
|
|
|
+ // 观察 DataStore,以 DS 为读取权威源
|
|
|
+ dsObserverJob = CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ appCtx.dataStore.data
|
|
|
+ .catch { if (it is IOException) emit(emptyPreferences()) else throw it }
|
|
|
+ .map { prefs ->
|
|
|
+ // 安全读取 String 值,用于类型回退(类型不匹配时返回 null)
|
|
|
+ val strVal = runCatching { prefs[stringPreferencesKey(key)] }.getOrNull()
|
|
|
+ @Suppress("UNCHECKED_CAST")
|
|
|
+ when {
|
|
|
+ defaultValue is String || defaultValue == null ->
|
|
|
+ strVal as T?
|
|
|
+ defaultValue is Int ->
|
|
|
+ (prefs[intPreferencesKey(key)]
|
|
|
+ ?: strVal?.toIntOrNull()) as T?
|
|
|
+ defaultValue is Boolean ->
|
|
|
+ (prefs[booleanPreferencesKey(key)]
|
|
|
+ ?: strVal?.toBooleanStrictOrNull()) as T?
|
|
|
+ defaultValue is Long ->
|
|
|
+ (prefs[longPreferencesKey(key)]
|
|
|
+ ?: strVal?.toLongOrNull()) as T?
|
|
|
+ defaultValue is Float ->
|
|
|
+ (prefs[floatPreferencesKey(key)]
|
|
|
+ ?: strVal?.toFloatOrNull()) as T?
|
|
|
+ else -> null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .distinctUntilChanged()
|
|
|
+ .collect { dsValue ->
|
|
|
+ if (dsValue != null && _value.value != dsValue) {
|
|
|
+ _value.value = dsValue
|
|
|
+ onValueChange?.invoke(dsValue)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
override fun onCreate(owner: LifecycleOwner) {
|
|
|
@@ -60,6 +106,7 @@ fun <T> prefDelegate(
|
|
|
|
|
|
override fun dispose() {
|
|
|
appCtx.defaultSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
|
|
+ dsObserverJob?.cancel()
|
|
|
}
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
@@ -92,6 +139,16 @@ fun <T> prefDelegate(
|
|
|
is Long -> appCtx.putPrefLong(key, value)
|
|
|
is Float -> appCtx.putPrefFloat(key, value)
|
|
|
}
|
|
|
+ // 同步写入 DataStore,保持 SP/DS 一致
|
|
|
+ CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ when (value) {
|
|
|
+ is String? -> DsSync.putString(key, value)
|
|
|
+ is Int -> DsSync.putInt(key, value)
|
|
|
+ is Boolean -> DsSync.putBoolean(key, value)
|
|
|
+ is Long -> DsSync.putLong(key, value)
|
|
|
+ is Float -> DsSync.putFloat(key, value)
|
|
|
+ }
|
|
|
+ }
|
|
|
_value.value = value
|
|
|
onValueChange?.invoke(value)
|
|
|
}
|