重构上传

This commit is contained in:
2026-10-29 14:39:14 +08:00
parent 5739f7f8e7
commit 0cf352cabb
19 changed files with 1398 additions and 190 deletions
@@ -1,65 +1,116 @@
package net.bluemangoo.indpjsk.data
import android.content.Context
import android.content.SharedPreferences
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.edit
import net.bluemangoo.indpjsk.App
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
object SettingsStore {
private fun prefs() =
App.appContext.getSharedPreferences("settings", Context.MODE_PRIVATE)
private const val PREFS = "settings"
private const val KEY_UPLOAD_CODE = "upload_code"
private const val KEY_USE_UPLOAD = "use_upload"
private const val KEY_PROXY = "upstream_proxy"
private const val KEY_USE_PROXY = "use_proxy"
private const val KEY_BYPASS = "bypass_packages"
private const val KEY_PROFILE = "selected_profile"
private inline fun <reified T> pref(
default: T? = null,
key: String? = null
) = PrefProperty(
clazz = T::class,
customDefault = default,
customKey = key,
prefs = { prefs() }
)
private fun prefs(context: Context) =
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
var uploadProfilesJson by pref<String>()
var upstreamProxy by pref<String>()
var useProxy by pref<Boolean>()
var bypassPackages by pref<Set<String>>()
var selectedProfileId by pref<String?>(default = null)
var continueRun by pref<Boolean>()
fun loadUploadCode(context: Context): String =
prefs(context).getString(KEY_UPLOAD_CODE, "").orEmpty()
fun saveUploadCode(context: Context, code: String) {
prefs(context).edit { putString(KEY_UPLOAD_CODE, code) }
}
fun loadUseUpload(context: Context): Boolean {
return loadUploadCode(context).isNotBlank() && prefs(context).getBoolean(KEY_USE_UPLOAD, true)
}
fun saveUseUpload(context: Context, enabled: Boolean) {
prefs(context).edit { putBoolean(KEY_USE_UPLOAD, enabled) }
}
fun loadProxy(context: Context): String =
prefs(context).getString(KEY_PROXY, "").orEmpty()
fun saveProxy(context: Context, proxy: String) {
prefs(context).edit { putString(KEY_PROXY, proxy.trim()) }
}
fun loadUseProxy(context: Context): Boolean {
return loadProxy(context).isNotBlank() && prefs(context).getBoolean(KEY_USE_PROXY, true)
}
fun saveUseProxy(context: Context, enabled: Boolean) {
prefs(context).edit { putBoolean(KEY_USE_PROXY, enabled) }
}
fun loadBypassPackages(context: Context): Set<String> =
prefs(context).getStringSet(KEY_BYPASS, emptySet())?.toSet() ?: emptySet()
fun saveBypassPackages(context: Context, packages: Set<String>) {
prefs(context).edit { putStringSet(KEY_BYPASS, packages) }
}
fun loadSelectedProfileId(context: Context): String? =
prefs(context).getString(KEY_PROFILE, null)
fun saveSelectedProfileId(context: Context, id: String) {
prefs(context).edit { putString(KEY_PROFILE, id) }
}
}
class PrefProperty<T>(
private val clazz: KClass<*>,
private val customDefault: T? = null,
private val customKey: String? = null,
private val prefs: () -> SharedPreferences
) : ReadWriteProperty<Any?, T> {
private var internalState: MutableState<T>? = null
private var resolvedKey: String? = null
private fun getKey(property: KProperty<*>): String =
resolvedKey ?: (customKey ?: property.name).also { resolvedKey = it }
@Suppress("UNCHECKED_CAST")
private fun getDefault(): T {
if (customDefault != null) return customDefault
return when {
clazz == String::class -> "" as T
clazz == Boolean::class -> false as T
clazz == Int::class -> 0 as T
clazz == Long::class -> 0L as T
clazz == Float::class -> 0f as T
clazz == Double::class -> 0.0 as T
Set::class.java.isAssignableFrom(clazz.java) -> emptySet<String>() as T
else -> throw IllegalArgumentException("Unsupported type: ${clazz.simpleName}")
}
}
@Suppress("UNCHECKED_CAST")
private fun getOrCreateState(property: KProperty<*>): MutableState<T> {
return internalState ?: run {
val key = getKey(property)
val sp = prefs()
val fallback = getDefault()
val initialVal: T = when {
clazz == String::class -> sp.getString(key, fallback as String) as T
clazz == Boolean::class -> sp.getBoolean(key, fallback as Boolean) as T
clazz == Int::class -> sp.getInt(key, fallback as Int) as T
clazz == Long::class -> sp.getLong(key, fallback as Long) as T
clazz == Float::class -> sp.getFloat(key, fallback as Float) as T
Set::class.java.isAssignableFrom(clazz.java) -> {
(sp.getStringSet(key, fallback as? Set<String>)?.toSet() ?: emptySet<String>()) as T
}
else -> fallback
}
mutableStateOf(initialVal).also { internalState = it }
}
}
@Suppress("UNCHECKED_CAST")
private fun saveToSp(value: T) {
val key = checkNotNull(resolvedKey) { "Property has not been initialized yet" }
prefs().edit {
when (value) {
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Set<*> -> putStringSet(key, HashSet(value as Set<String>))
}
}
}
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return getOrCreateState(property).value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
getOrCreateState(property).value = value
saveToSp(value)
}
operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): PrefProperty<T> {
resolvedKey = customKey ?: property.name
getOrCreateState(property)
return this
}
}
@@ -4,12 +4,6 @@ import android.content.Context
import org.json.JSONObject
import androidx.core.content.edit
/**
* 每个 profile 下每个步骤「要不要抓」的持久化。
*
* 就一个 JSON 字符串塞进 SharedPreferences,键是 "profileId/stepId",值 Boolean,
* 没存过的默认 true(全抓)。
*/
object StepSelectionStore {
private const val PREFS = "step_selection"