- README.md / README.txt 新增初次部署章节:前置条件(JDK 17 / SDK 34 / Gradle 8.9)、 获取代码与本地配置(local.properties 不入仓)、Debug/Release 构建、装到手机与相机权限、 连接配置(只连 Windows 客户端,默认 8765 / /pose)、第一次跑通、自检清单、常见问题 - 单测数量按实际(30 项)修正 - 三库同源契约:':=' 写法修回、去掉真实 MariaDB 版本串 - 全文去除易被 Gitea 判为「模棱两可 Unicode」的字符 - 本机 JDK 路径与内网示例地址改占位符 / RFC 5737 文档网段
95 lines
3.3 KiB
Kotlin
95 lines
3.3 KiB
Kotlin
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<Int, Int> {
|
||
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,推荐)"
|
||
}
|
||
}
|