Browse Source
设备默认图 - default.png / default_icon.png 是 assets/images/devices 里仅有的两张**不透明带底图** (892×1152 竖版海报 + 浅蓝渐变底),而设备页展示区是 1.14:1 的横宽框, BoxFit.contain 后成了中间一条窄的蓝色卡片、左右大片留白,深色模式下尤其突兀。 改成去背透明(948×828 / 176×154),主体自己铺满框、周围就是页面背景,代码零适配。 - device_images.dart 记下这条约定,免得以后又丢一张竖版带底图进来。 AI 页按设备铺编排(连着香薰却显示全量功能) - _load() 的一次性迁移会改 enabledIds 却**不更新基线 _lastAppliedKey**, 尤其 _migrateDefaultLayout 会把 defaultEnabled 里缺的全部 append 回来 —— 香薰编排铺成 3 项,重启后被追加成 11 项,基线还停在 3 项, 于是 applyDeviceDefaults 判定「用户动过编排」,从此再也不铺,且怎么重连都恢复不了。 现在:当前编排若是自动铺的就整个跳过这些迁移;已经坏掉的装机由 _rebaseDeviceDefaultsBaseline 一次性把基线抬到现状(判据收紧到迁移留下的特征形态)。 - AgentDefaults.bind 补跑一次当前状态:GetX 的 ever 拿不到绑定那一刻已有的值, 而设备是被动回连的,完全可能赶在 bind 之前就绪。 - 断开设备后回退到 DeviceHub.lastKnownVendor:设备图和「设备管理」在断连后都显示 最近那台设备,AI 页却停在上一台铺的编排里,于是出现「图是耳机、功能是香薰」。 为此给 DeviceHub 加 lastKnownVendor,并把它与 lastKnownName 抽成同一个来源 —— 各算各的就还会自相矛盾。 - test/agent_module_defaults_test.dart 6 个用例守这条规则。 车载香薰唤醒 AI 后回复从手机扬声器出来 - 下行本来就设计成走经典蓝牙 A2DP,坏在共用的音频会话上:defaultToSpeaker 把输出 钉在内置扬声器,选项里只有 allowBluetooth(HFP)而没有 allowBluetoothA2dp (playAndRecord 下 A2DP 输出默认是关的)。而这条链路采音在设备上走 BLE opus, 手机压根不录音,用 playAndRecord 本身就是错配。 - EmaiAudioSession 分档:香薰走新的 playbackOnly(.playback 类别,输出跟随系统路由), EMAI 页「按住说话」仍是 playAndRecord(去掉 defaultToSpeaker 会退回听筒,更糟), 只补上 allowBluetoothA2dp。原生 PcmSpeakerPlayer 明确不碰 AVAudioSession,改这一处即可。 固件升级页 - 后台没给产品配过固件时,页面显示的是绿色的「已是最新」——这是假话,会让人以为 手上固件是最新的而不再去查。改成三态:找不到产品配置 / 服务端暂无固件版本 / 有新版本·已是最新。 新文案 otaNoServerVersion 已补齐 41 个语言文件(i18n_audit total_missing=0)。 - 产品找到但版本为空时打一条点名 warn,不再和「拉取失败」混在一起。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>main
53 changed files with 401 additions and 49 deletions
|
Before Width: | Height: | Size: 846 KiB After Width: | Height: | Size: 572 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 32 KiB |
@ -0,0 +1,81 @@ |
|||||
|
import 'package:eaimar/data/models/agent_module.dart'; |
||||
|
import 'package:eaimar/data/services/agent_module_service.dart'; |
||||
|
import 'package:flutter_test/flutter_test.dart'; |
||||
|
|
||||
|
void main() { |
||||
|
// 车载香薰连上时铺的那三个 |
||||
|
const carAroma = ['smartcarChat', 'fieldRecording', 'meeting']; |
||||
|
const defaults = ['call', 'simultaneous', 'meeting', 'fieldRecording']; |
||||
|
|
||||
|
group('isLayoutMigrationArtifact', () { |
||||
|
// 这正是 2026-09-14 线上那台机的状态:香薰编排铺过 3 项, |
||||
|
// 重启后被 _migrateDefaultLayout 把默认全量追加到尾部, |
||||
|
// 于是基线对不上、从此再也不按设备铺编排。 |
||||
|
test('设备编排被追加了默认项 → 认得出来', () { |
||||
|
expect( |
||||
|
AgentModuleService.isLayoutMigrationArtifact( |
||||
|
enabled: [...carAroma, 'call', 'simultaneous'], |
||||
|
lastApplied: carAroma, |
||||
|
defaults: defaults, |
||||
|
), |
||||
|
isTrue, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
test('用户把设备编排里的项删掉/换过顺序 → 不是迁移造成的,不能碰', () { |
||||
|
expect( |
||||
|
AgentModuleService.isLayoutMigrationArtifact( |
||||
|
enabled: ['fieldRecording', 'smartcarChat', 'meeting', 'call'], |
||||
|
lastApplied: carAroma, |
||||
|
defaults: defaults, |
||||
|
), |
||||
|
isFalse, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
test('多出来的项不在默认全量里(用户从广场加的)→ 不认', () { |
||||
|
expect( |
||||
|
AgentModuleService.isLayoutMigrationArtifact( |
||||
|
enabled: [...carAroma, 'pictureTranslation'], |
||||
|
lastApplied: carAroma, |
||||
|
defaults: defaults, |
||||
|
), |
||||
|
isFalse, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
test('编排与基线完全一致 → 没被追加过', () { |
||||
|
expect( |
||||
|
AgentModuleService.isLayoutMigrationArtifact( |
||||
|
enabled: carAroma, |
||||
|
lastApplied: carAroma, |
||||
|
defaults: defaults, |
||||
|
), |
||||
|
isFalse, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
test('从没自动铺过(基线为空)→ 不认', () { |
||||
|
expect( |
||||
|
AgentModuleService.isLayoutMigrationArtifact( |
||||
|
enabled: [...carAroma, 'call'], |
||||
|
lastApplied: const [], |
||||
|
defaults: defaults, |
||||
|
), |
||||
|
isFalse, |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// 设备编排的内容本身也守一下:香薰就该是这三个,别被顺手改掉。 |
||||
|
test('车载香薰的默认编排是 Smartcar / 现场录音 / 语音纪要', () { |
||||
|
expect( |
||||
|
AgentModules.defaultsForCategory('carAroma'), |
||||
|
[ |
||||
|
AgentModules.smartcarChat, |
||||
|
AgentModules.fieldRecording, |
||||
|
AgentModules.meeting, |
||||
|
], |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
Loading…
Reference in new issue