ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Operit 语音服务设置页 UI 重排实战:TTS/STT 分页、档案管理与可展开配置的实现与数据契约

Operit 语音服务设置页 UI 重排实战:TTS/STT 分页、档案管理与可展开配置的实现与数据契约 AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载导读本文基于 Operit 仓库中的speech_services_settings_ui_20260822设计文档系统讲解语音服务设置页从单页双长表单重构为TTS/STT 分页 紧凑档案管理 可展开低频配置的完整过程。你将掌握页面重排的行为约束不破坏已发布配置数据与 Provider 契约、TTS/STT 页面结构与各 Compose 组件实现、档案生命周期与自动保存机制以及SpeechServiceProfilesPreferences与旧SpeechServicesPreferences双 DataStore 投影的底层原理可直接对照 SpeechServicesSettingsScreen.kt 与 SpeechServiceProfilesPreferences.kt 逐行验证。一、重构背景单页长表单的问题重构前的设置页把 TTS、STT、配置档案、供应商参数、清理规则和说明内容连续堆叠在同一条LazyColumn长列表中。TTS 供应商切换后低频 JSON 参数如 Headers、响应管道和常用播放参数语速、音调没有清晰层级用户需要长距离滚动才能完成一次配置见 speech_services_settings_ui_20260822/index.md 的原本状况。本次重排的目标是在不改变已发布配置数据、路由、自动保存和 Provider 参数契约的前提下将页面压缩为 TTS/STT 分页、单行配置档案管理、紧凑播放参数和可展开的低频设置。作用域严格限定为SpeechServicesSettingsScreen.kt的 Compose 页面结构SpeechServiceProfilesPreferences.kt及 Provider 层数据模型与运行时契约一律不动。二、已发布行为约束重排的铁律原文档 01_ui_relayout.md 明确了四条不可逾越的约束这是本次重构与普通 UI 改版最大的区别约束含义源码对应不改变档案存储SpeechServiceProfilesPreferences的 JSON、档案 ID、当前档案选择和删除约束保持原样SpeechServiceProfilesPreferences.kt 中TtsProfile/SttProfile数据结构、tts_profiles/stt_profiles等 key 均未变动不改变旧投影旧SpeechServicesPreferences投影保持原样现有 Provider 继续读取相同字段saveTtsSettings/saveSttSettings签名与写入 key 未变见 SpeechServicesPreferences.kt不增加第二套保存机制页面继续使用现有自动保存流程不引入手动保存按钮自动保存由LaunchedEffect 防抖实现见下文第五节不删除已存在字段所有已存在的 Provider 配置字段全部保留TTS 侧TtsHttpConfig的urlTemplate、apiKey、headers、httpMethod、requestBody、contentType、localeTag、voiceId、modelName、responsePipeline十个字段以及VitsTtsPackageConfig、SttHttpConfig均原样保留这套约束的根本原因在于仓库已经发布了基于旧speech_services_preferencesDataStore 的版本迁移到档案存储后旧 DataStore 仍作为运行时投影被现有 Provider 和旧版本读取接口消费见 speech_service_profiles.md。任何字段或保存路径的改动都会破坏已发布安装的兼容性。三、新页面结构五大组成模块原文档给出了重排后的页面结构蓝图源码实现可逐条对应1. 页面内 TabTTS / STT由 SpeechServicesSettingsScreen.kt 中的SpeechServicesModeTabs实现使用 Material3 的TabRow承载两个TabselectedTabIndex为 0 表示 TTS、1 表示 STT。页面主体通过if (selectedTabIndex 0) item(key tts-settings)L458与if (selectedTabIndex 1) item(key stt-settings)L2101条件渲染这正是完成标准中不再同时渲染 TTS 和 STT 两个完整长表单的实现方式——同一时刻LazyColumn中只有一个完整配置区。2. 当前 Tab 的配置档案紧凑管理栏SpeechProfileManagementBarL2523-L2564根据当前 Tab 决定展示 TTS 还是 STT 档案内部委托给SpeechProfileSelectorL2566-L2681。该组件提供当前档案展示整行可点击的Surface显示当前档案标签与档案名点击展开DropdownMenu列出全部档案新建Add 图标弹出SpeechProfileNameDialogL2683-L2714输入名称确认后调用createTtsProfile/createSttProfile重命名Edit 图标弹出同名对话框确认后调用updateTtsProfile(activeTtsProfile.copy(name name))切换在下拉菜单点击任意档案条目触发onSelect调用selectTtsProfile/selectSttProfile删除非当前档案下拉菜单中非当前档案条目右侧显示 Delete 图标点击后弹出SpeechProfileDeleteDialogL2716-L2736确认当前档案不显示删除按钮if (profile.id ! activeProfileId)。3. 当前 Provider 与播放参数集中展示TTS 引擎选择使用ExposedDropdownMenuBox下拉框枚举值来自 VoiceServiceFactory.kt 的VoiceServiceTypeSIMPLE_TTS、HTTP_TTS、OPENAI_WS_TTS、SILICONFLOW_TTS、MINIMAX_TTS、MIMO_TTS、DOUBAO_TTS、OPENAI_TTS、VITS_TTS共九种。选择 Doubao 时页面自动预填默认端点 URL、默认音色与application/jsonContent-Type见 L528-L532。语速与音调合并为同一播放参数区域的两个Slider取值范围均为0.5f..2.0f、steps 5实时显示当前数值——重排后这两个高频参数始终可见不再被长表单淹没。4. 清理正则与 HTTP 请求参数作为可展开区域TTS 清理规则ttsCleanerExpanded状态 AnimatedVisibility标题行显示当前正则数量(N)展开后可逐条编辑、删除、新增Add按钮并提供模板下拉菜单内置五个常用正则模板\*[^*]\*星号包裹、\*\*[^*]\*\*双星号包裹、\([^)]\)英文括号、[^]中文括号、[^]XML 标签。默认清理列表见 SpeechServicesPreferences.kt中英文括号两个正则。HTTP 高级参数ttsHttpAdvancedExpanded状态仅当引擎为HTTP_TTS时展示包含 HeadersJSON 文本域实时校验、HTTP 方法GET/POST 下拉、Content-Type、POST 请求体模板支持{text}占位符以及响应管道responsePipeline多行 JSON逐字校验并给出错误提示。5. 测试入口与说明TTS Tab 底部保留测试 TTS按钮L2297-L2310通过onNavigateToTextToSpeech跳转测试页STT Tab 保留对应引擎参数与说明。两种 Tab 共用底部说明区根据selectedTabIndex切换显示 TTS/STT 描述文本。四、数据契约档案存储与旧投影的双轨结构页面重排不改数据层但理解数据契约才能理解 UI 行为。档案存储使用独立的speech_service_profilesDataStore版本号 currentVersion 1含 schema 迁移由 SpeechServiceProfilesPreferences.kt 独占读写TtsProfileid、name、serviceType、httpConfig完整TtsHttpConfig、vitsConfig、cleanerRegexs、speechRate、pitch、createdAt、updatedAtSttProfileid、name、serviceType、httpConfigSttHttpConfig、createdAt、updatedAtDataStore 顶层 keytts_profiles、stt_profiles、current_tts_profile_id、current_stt_profile_id。旧speech_services_preferencesDataStore 则保留为当前档案投影projectTtsProfile/projectSttProfileL346-L362在创建、更新、切换档案后调用旧存储的saveTtsSettings/saveSttSettings把当前档案的字段原样写回旧 keytts_service_type、tts_http_config、tts_cleaner_regexs、tts_speech_rate、tts_pitch、stt_service_type、stt_http_config。现有 Provider 与旧版本读取接口继续从旧 DataStore 消费而新代码一律通过档案存储读写——这就是切换和保存会更新旧偏好投影并重置对应服务实例的落地方式。首次访问新档案存储时migratePreferencesFromVersionZero会把旧 TTS/STT 配置分别封装为固定 IDlegacy-tts-profile/legacy-stt-profile的档案并写回同时设置当前档案 ID新安装用户因旧 key 缺失而直接获得默认档案系统 TTSSIMPLE_TTS语速/音调默认1.0f与本地 Sherpa STTSHERPA_NCNN见 SpeechServicesPreferences.kt。五、自动保存流程防抖 变更检测 校验门控页面无保存按钮依赖自动保存流程其实现位于 SpeechServicesSettingsScreen.kt变更检测hasPendingChanges逐字段对比输入 state 与当前档案值覆盖 TTS 全部字段服务类型、URL、API Key、Headers、方法、Body、Content-Type、语言、音色、模型、响应管道、VITS 路径/说话人/选项、清理正则、语速、音调与 STT 全部字段服务类型、端点、API Key、模型防抖LaunchedEffect监听所有输入字段延迟500ms后再次检查hasPendingChanges避免每次按键都触发磁盘写入JSON 校验门控当引擎为HTTP_TTS且 Headers/响应管道 JSON 非法、或引擎为VITS_TTS且 options JSON 非法时直接returnLaunchedEffect阻断保存配合输入框的isError红色提示写入与重置依次调用updateTtsProfile/updateSttProfile随后调用VoiceServiceFactory.resetInstance()与SpeechServiceFactory.resetInstance()使已创建的 Provider 实例在下次使用时按新配置重建异常反馈捕获异常后通过AppLogger.e记录并将错误信息写入ttsProfileError/sttProfileError由SnackbarHostState.showSnackbar弹出提示。值得注意Tab 切换不参与自动保存selectedTabIndex不在LaunchedEffect的监听列表内且所有输入 state 均为remember(数据源)初始化、常驻 Composable 作用域因此切换 Tab 不会丢失任一侧的编辑状态这是完成标准的第一条。六、档案生命周期创建、更新、切换、删除的源码约束SpeechServiceProfilesPreferences.kt 的四个核心方法与页面操作一一对应创建createTtsProfile/createSttProfile以当前档案为模板copy出新档案分配新UUID.randomUUID()写入列表并立即设为当前档案随后投影到旧存储更新updateTtsProfile/updateSttProfile名称经requireProfileName去空白并校验非空cleanerRegexs过滤空串speechRate/pitch经requirePositive校验必须 0保留原createdAt只更新updatedAt仅当被更新档案是当前档案时才触发投影切换selectTtsProfile/selectSttProfile只写当前档案 ID 并投影不改动任何档案内容删除deleteTtsProfile/deleteSttProfile先check(preferences[CURRENT_TTS_PROFILE_ID] ! id)当前档案删除直接抛异常UI 层通过错误捕获转为 Snackbar 提示随后从列表过滤目标 ID。底层数据通过ttsProfilesFlow/sttProfilesFlow等 Flow 对外暴露UI 使用collectAsState(initial emptyList())订阅当前档案缺失时页面显示CircularProgressIndicator不会用隐式默认档案掩盖数据损坏见架构文档 speech_service_profiles.md 的生命周期约束。七、Provider 如何消费档案工厂调用链档案不仅是设置页的数据源也是运行时语音服务的配置来源。VoiceServiceFactory.kt 的createVoiceService通过runBlocking { profiles.getCurrentTtsProfile() }读取当前档案再按serviceType分派到对应 ProviderSIMPLE_TTS用SimpleVoiceProvider携带localeTag/voiceId其余引擎依次映射到 HTTP、OpenAI Realtime、硅基流动、MiniMax、MiMo、豆包、OpenAI、VITS 等实现。STT 侧由 SpeechServiceFactory.kt 以同样方式消费。这也解释了为什么设置页保存后必须resetInstance()单例工厂持有已构建的 Provider 实例只有重置才能让下一次朗读/识别使用新档案。仓库中SpeechServiceProfilesPreferences的消费方还包括聊天朗读ChatViewModel.kt、工具箱 TTS 页面TextToSpeechScreen.kt、悬浮窗全屏页FloatingFullscreenScreen.kt以及内置软件设置工具StandardSoftwareSettingsModifyTools.kt说明档案存储是全局语音配置的唯一入口。八、完成标准与验收要点原文档的四条完成标准均可对照实现逐项验收切换 Tab 不丢失编辑状态输入 state 使用remember(数据源)初始化并常驻selectedTabIndex不触发自动保存切回 Tab 时字段原样保留切换、创建、重命名、删除档案行为不变四类操作全部走SpeechServiceProfilesPreferences原方法UI 仅改变入口形态下拉菜单 对话框底层check约束与投影逻辑未动每种已存在 TTS Provider 仍可访问原有字段九种VoiceServiceType全部保留TtsHttpConfig十字段、VitsTtsPackageConfig、SttHttpConfig逐一在编辑区可用页面不再同时渲染 TTS 和 STT 两个完整长表单通过selectedTabIndex条件渲染同一时刻仅渲染一个配置卡片tts-settings或stt-settingsitem。相关源码路径索引设置页实现app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/SpeechServicesSettingsScreen.kt共 2736 行含 Tab、档案管理栏、TTS/STT 卡片、对话框与自动保存档案存储app/src/main/java/com/ai/assistance/operit/data/preferences/SpeechServiceProfilesPreferences.kt旧投影存储app/src/main/java/com/ai/assistance/operit/data/preferences/SpeechServicesPreferences.kt语音工厂app/src/main/java/com/ai/assistance/operit/api/voice/VoiceServiceFactory.kt、app/src/main/java/com/ai/assistance/operit/api/speech/SpeechServiceFactory.kt架构文档docs/doc-src/architecture/speech_service_profiles.md、docs/TODO/speech_service_profiles_20260810/01_migration_contract.md本主题设计文档docs/TODO/speech_services_settings_ui_20260822/index.md、docs/TODO/speech_services_settings_ui_20260822/01_ui_relayout.md赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐Xberg 页面级抽取与页面标记配置实战extract_pages / insert_page_markers 契约解析与实现原理Xberg 页面级抽取与页面标记配置实战extract_pages / insert_page_markers 契约解析与实现原理 本篇技术指南围绕 Xber后端AI 应用NLPLiveKit Agents 集成 Telnyx 语音服务livekit-plugins-telnyx 的 STT 与 TTS 实战指南LiveKit Agents 集成 Telnyx 语音服务livekit plugins telnyx 的 STT 与 TTS 实战指南 livekit plAI Agent人工智能语音AI 应用多模态AIRI 语音输入输出配置实战TTS 语音合成与 ASR/STT 语音识别完整指南AIRI 语音输入输出配置实战TTS 语音合成与 ASR/STT 语音识别完整指南 AIRI 的语音能力分为语音合成TTS将 AI 回复朗读出来与语音识AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染上一篇swagger-codegen 生成的 Eiffel 客户端 API 参考FAKECLASSNAMETAGS123_API 的 test_classname 端点全解析下一篇如何让Mac上的重要窗口永远保持在最上层Topit窗口置顶工具带来全新解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表