package com.opendance.studio.data import android.content.Context import androidx.core.content.edit /** * 应用设置(SharedPreferences)。 * * 契约 websocket-keypoints.md §3.3 的 config 字段与这里的前四项一一对应。 */ class AppSettings(context: Context) { private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) /** Windows 客户端主机地址 */ var host: String get() = prefs.getString(KEY_HOST, DEFAULT_HOST) ?: DEFAULT_HOST set(value) = prefs.edit { putString(KEY_HOST, value.trim()) } var port: Int get() = prefs.getInt(KEY_PORT, DEFAULT_PORT) set(value) = prefs.edit { putInt(KEY_PORT, value.coerceIn(1, 65535)) } /** 目标帧率(契约 config.targetFps) */ var targetFps: Int get() = prefs.getInt(KEY_TARGET_FPS, DEFAULT_FPS) set(value) = prefs.edit { putInt(KEY_TARGET_FPS, value.coerceIn(5, 60)) } /** 是否镜像画面(契约 flags.bit0 / config.mirror) */ var mirror: Boolean get() = prefs.getBoolean(KEY_MIRROR, true) set(value) = prefs.edit { putBoolean(KEY_MIRROR, value) } /** 仅上半身模式(契约 flags.bit2) */ var upperBodyOnly: Boolean get() = prefs.getBoolean(KEY_UPPER_BODY, false) set(value) = prefs.edit { putBoolean(KEY_UPPER_BODY, value) } /** 分辨率档位,取值见 [Resolutions] */ var resolution: String get() = prefs.getString(KEY_RESOLUTION, Resolutions.MEDIUM) ?: Resolutions.MEDIUM set(value) = prefs.edit { putString(KEY_RESOLUTION, value) } /** 上次连接的设备名(hello 上报用) */ var deviceLabel: String get() = prefs.getString(KEY_DEVICE_LABEL, "") ?: "" set(value) = prefs.edit { putString(KEY_DEVICE_LABEL, value) } /** 自动重连 */ var autoReconnect: Boolean get() = prefs.getBoolean(KEY_AUTO_RECONNECT, true) set(value) = prefs.edit { putBoolean(KEY_AUTO_RECONNECT, value) } fun webSocketUrl(): String = "ws://$host:$port/pose" companion object { const val PREFS_NAME = "opendance_settings" const val DEFAULT_HOST = "192.0.2.100" const val DEFAULT_PORT = 8765 const val DEFAULT_FPS = 30 private const val KEY_HOST = "host" private const val KEY_PORT = "port" private const val KEY_TARGET_FPS = "target_fps" private const val KEY_MIRROR = "mirror" private const val KEY_UPPER_BODY = "upper_body_only" private const val KEY_RESOLUTION = "resolution" private const val KEY_DEVICE_LABEL = "device_label" private const val KEY_AUTO_RECONNECT = "auto_reconnect" } } /** 分辨率档位 —— 与契约 config.resolution 的取值格式一致 */ object Resolutions { const val LOW = "480x360" const val MEDIUM = "640x480" const val HIGH = "1280x720" val ALL = listOf(LOW, MEDIUM, HIGH) /** 返回 (宽, 高) */ fun parse(value: String): Pair { val parts = value.split("x") val w = parts.getOrNull(0)?.toIntOrNull() ?: 640 val h = parts.getOrNull(1)?.toIntOrNull() ?: 480 return w to h } fun label(value: String): String = when (value) { LOW -> "低(480×360,最省电)" HIGH -> "高(1280×720,最清晰)" else -> "中(640×480,推荐)" } }