90 KiB
90 KiB
agent.md
当前阶段
- 2026-06-05:排查并优化蓝牙通讯录/通话记录权限与同步 UI 状态。
已验证结论
- 清空蓝牙本地 SQLite 表不是通讯录同步失败的直接原因;正常清空后 PBAP 同步会重新写入。
- 日志中偶现的
ConcurrentModificationException指向联系人保存阶段共享列表被并发访问,属于同步完成后保存/回调链路稳定性问题。 - 2026-06-06 新复测日志
2026_0606_1105_50_063.log显示 UI 修复生效:- 未出现
Dex checksum does not match - 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败 - 同步中
ContactsFragment保持isPhoneBookSynchronized=false isPhoneBookPulling=true - 最终
11:08:28.044收到ContactListViewModel: onContactsPhoneBook = 1973
- 未出现
2026_0606_1105_50_063.log同时确认双进程重复同步:11:06:32.967com.yaxon.carcontrolPID 3859 触发phoneBookPullRequest11:06:32.977com.yaxon.bluetoothPID 4968 触发phoneBookPullRequest- 两个进程都收到
onContactsPullCompleted、读取1973条、执行deleteContacts
- 2026-06-06 复测日志
2026_0606_1136_51_087.log确认移除YX_CarControl联系人/PBAP 初始化后双进程重复同步已消失:- PID 3793 为
com.yaxon.carcontrol,只出现配对弹窗和自动同步开关日志,未出现BtContactsControlManager - PID 5101 为
com.yaxon.bluetooth,唯一执行BluetoothPbapClient: phoneBookPullRequest - 联系人同步成功:
11:38:25.186 contacts read completed size = 1973,11:38:41.378 onContactsPhoneBook = 1973 - 通话记录同步成功:
11:38:02.223 mCallRecordBeanList size = 51,11:38:02.953 notifyPhoneRecordList list == 51 - 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
- PID 3793 为
- 2026-06-06 用户现场验证
2026_0606_1136_51_087.log对应 UI 表现符合预期:- 点击下载通讯录后联系人页显示“正在加载通讯录”
- 切到通话页面时通话列表可正常显示
- 再切回联系人页面后通讯录正常显示
- 说明联系人同步中的空列表中间态已被 loading 承接,未再误显示 0 个联系人
- 2026-06-06 现场重新清理 logcat 后复测,导出日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_2026_0606_1348_32_838.log:com.yaxon.carcontrolPID 3957 只出现配对弹窗/自动同步开关日志,未出现BtContactsControlManagercom.yaxon.bluetoothPID 5030 唯一执行BluetoothPbapClient: phoneBookPullRequest- 通讯录流程成功:
12:40:35.640 phoneBookPullRequest,12:41:22.565 onContactsPullCompleted,12:41:51.884 contacts read completed size = 1973 - 通话记录流程成功:
12:41:27.963 mCallRecordBeanList size = 51,12:41:28.665 notifyPhoneRecordList list == 51 - 用户切回联系人页后读取并显示成功:
12:44:48.892 onContactsPhoneBook = 1973,isPhoneBookSynchronized = true isPhoneBookPulling=false - 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
- 2026-06-06 用户再次点击“同步通讯录”后复测,导出日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_resync_2026_0606_1352_02_395.log:- 再次点击会重新触发一次 PBAP 通讯录同步:
12:47:06.124 BluetoothPbapClient: phoneBookPullRequest - 仍然只有
com.yaxon.bluetoothPID 5030 执行同步,BtContactsControlManager = 0 - 第二次同步成功:
12:47:50.259 onContactsPullCompleted,13:51:27.509 contacts read completed size = 1973,13:51:45.832 onContactsPhoneBook = 1973 - 第二次通话记录也成功刷新:
12:47:55.391 mCallRecordBeanList size = 51,12:47:55.982 notifyPhoneRecordList list == 51 - 日志中出现设备时间从
06-05 12:47跳到06-06 13:51的校时现象,因此第二次同步耗时不能直接按日志墙钟计算 - 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
- 再次点击会重新触发一次 PBAP 通讯录同步:
- 2026-06-06 清空 logcat 后再次点击“同步通讯录”复测,导出日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_resync_clean_2026_0606_1355_54_369.log,补抓尾部verify_resync_clean_tail_2026_0606_1356_44_383.log:- 本轮无时间跳变,可计算耗时:
13:54:40.708 phoneBookPullRequest到13:56:06.301 onContactsPhoneBook = 1973,约 85.6 秒 - PBAP 拉取阶段:
13:54:40.708到13:55:24.857 onContactsPullCompleted,约 44.1 秒 - 联系人读取阶段:
13:55:24.926 cursor.getCount() = 1973到13:55:51.351 contacts read completed size = 1973,约 26.4 秒 - 写库/页面回调阶段:
13:55:51.352 deleteContacts到13:56:06.301 onContactsPhoneBook = 1973,约 14.9 秒 BtContactsControlManager = 0,BluetoothPairedRequestView = 0,说明手动同步未经过YX_CarControl配对弹窗- 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
- 本轮无时间跳变,可计算耗时:
- 2026-06-06 通话页 loading UI 修复后复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1405_50_910.log:- 新逻辑已生效:切回通话页时出现
CallLogsFragment: onHiddenChanged hidden=false后紧跟setIsBtConnect=true - 通话记录同步完成:
14:07:31.863 mCallRecordBeanList size = 51,14:07:32.670 notifyPhoneRecordList list == 51 - 通讯录同步完成:
14:07:56.555 contacts read completed size = 1973,14:08:14.822 onContactsPhoneBook = 1973 - 切回通话页时即使
isPhoneBookPulling=true,日志仍显示CallRecordUpdated = true并刷新notifyPhoneRecordList list == 51 - 本轮未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败 - 本轮出现
Dex checksum does not match,推测为系统 APK 覆盖推送后旧 dex/oat 缓存未刷新,需重启设备或清理对应编译缓存后再做最终确认
- 新逻辑已生效:切回通话页时出现
- 2026-06-06 重新加载/重启后复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1413_21_443.log,补抓尾部tail_after_2026_0606_1413_21_443_2026_0606_1416_43_579.log:- 未再出现
Dex checksum does not match - 通话页切回刷新逻辑生效:
CallLogsFragment: onHiddenChanged hidden=false后出现setIsBtConnect=true - 通话记录同步完成:
14:14:39.747 mCallRecordBeanList size = 51,14:14:40.416 notifyPhoneRecordList list == 51 - 通讯录同步完成:
14:15:06.632 contacts read completed size = 1973,补抓尾部14:15:22.731 onContactsPhoneBook = 1973 - PBAP 断开完成:
14:15:22.821 prevState = 2 state = 3,14:15:22.886 prevState = 3 state = 0 - 本轮未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
- 未再出现
2026_0605_1739_21_177.log中“先显示 0 个联系人,随后显示联系人列表”的原因不是同步失败,而是 UI 状态提前:17:40:48.576收到BtContactsManager: onContactsPullCompleted17:40:48.623系统联系人 Provider 查到1973条17:41:19.867SDK 读联系人完成:contacts read completed size = 197317:41:30.114页面判断isPhoneBookSynchronized = true isPhoneBookPulling=true17:41:36.979ViewModel 才收到onContactsPhoneBook = 1973
isPhoneBookSynchronized()只查 DB 是否存在 1 条联系人;保存大量联系人时第一条写入后就可能返回 true,导致联系人页提前显示空列表区域。
Bug 与修复记录
- 2026-06-09 蓝牙音乐封面继续扩展到
YX_SystemUI/YX_Launcher:- 用户确认先备份,已备份
DisplayShareViewModel.java、MediaControlView.java、WidgetsMedia.java到.codex_backup\music_cover_systemui_launcher_20260609_194529 - 最新日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1939_16_256.log显示YX_Music已能在封面文件稳定后加载成功,剩余问题集中在 SystemUI/Launcher 空albumUrl或无效文件覆盖已有封面 YX_SystemUI的MediaControlView增加蓝牙曲目 key 与封面显示状态:同一首歌收到空albumUrl或无效文件时保留当前封面;新歌收到空/无效封面时显示默认图,避免继续显示上一首封面YX_Launcher的WidgetsMedia按相同规则处理手写 widget 封面逻辑,并避免BitmapFactory.decodeFile()直接解码空路径YX_Launcher的DisplayShareViewModel.btMusicAlbum数据绑定入口增加 lastAlbum 缓存:同一首歌后续空路径/0 字节/解码失败时返回当前封面,新歌无有效封面时返回默认图- 已执行
.\gradlew :YX_SystemUI:assembleDebug和.\gradlew :YX_Launcher:assembleDebug,均编译通过 - 已推送
YX_SystemUI.apk到/system/app/YX_SystemUI/YX_SystemUI.apk,推送YX_Launcher.apk到/system/app/YX_Launcher/YX_Launcher.apk,并执行设备重启;重启后sys.boot_completed=1,两个包路径正常
- 用户确认先备份,已备份
- 2026-06-08 蓝牙音乐封面短暂显示后消失问题:
- 最新日志显示 SDK 可收到
albumUrl = /sdcard/ID3/id3cover.JPEG,但YX_Music侧多次出现Failed to create image decoder和BluetoothMusicViewModel: 获取bitmap为null YX_BluetoothSDK只转发系统蓝牙MediaMetadata.METADATA_KEY_ALBUM_ART_URI,未发现生成/sdcard/ID3/id3cover.JPEG的逻辑YX_Music已调整封面加载兜底:先解码新图片,成功后再替换并回收旧图;解码失败时如果当前已有文件封面则保留当前封面BluetoothMusicFragment已调整空albumUrl处理:当前没有文件封面时才显示默认图,避免元数据短暂空值覆盖已有封面
- 最新日志显示 SDK 可收到
- 2026-06-08 最新日志
2026_0608_1939_20_273.log确认/sdcard/ID3/id3cover.JPEG文件存在且可读,但长度始终为 0:BluetoothMusicViewModel: 获取bitmap为null, filePath = /sdcard/ID3/id3cover.JPEG, exists = true, length = 0, canRead = true- 本轮日志中
length 0 = 53,未发现非 0 字节封面文件 YX_BluetoothSDK已做最小颗粒过滤:BtMusicManager和BtInfoPushManager下发封面前校验本地文件路径,0 字节/不可读/不存在的封面路径不再下发- 同时增加
ALBUM_ART_URI、ART_URI、DISPLAY_ICON_URI、ALBUM_ART、ART字段诊断日志,暂不做 bitmap 落盘,避免扩大改动
- 2026-06-08 最新日志
2026_0608_1958_40_593.log确认切歌后封面路径晚于歌曲信息到达:- 点击下一首后,歌名通常先到,
albumUrl为空;约 2 到 4 秒后才出现固定路径/sdcard/ID3/id3cover.JPEG - 由于系统复用同一个固定封面文件,过早解码可能读到上一首封面内容,导致“先显示上一首,过会儿显示正确”
BluetoothMusicFragment已增加切歌封面调度:切歌或曲目 key 变化时先显示默认封面;固定 ID3 封面路径延迟 1 秒解码;使用版本号取消旧封面延迟任务
- 点击下一首后,歌名通常先到,
- 2026-06-09 继续分析日志
2026_0608_2015_49_223.log,确认 1 秒延迟仍不足以规避固定封面文件重写竞态:skip invalid album file = 111,说明YX_BluetoothSDK的 0 字节过滤已生效BluetoothMusicViewModel: 获取bitmap为null = 8,说明YX_Music延迟解码时仍可能撞上/sdcard/ID3/id3cover.JPEG被底层清零或半写入- 典型时序:
20:18:11.761底层开始get image,20:18:11.803SDK 检测length = 0并过滤,20:18:11.808YX_Music解码同一路径失败,20:18:12.294底层才提示avrcp image download succeed - 根因是
/sdcard/ID3/id3cover.JPEG为底层复用固定路径,单次下发时校验和页面实际解码之间仍存在文件重写竞态 - 本轮按用户要求先只修改
YX_Music:BluetoothMusicFragment对固定 ID3 封面路径增加文件刷新检测、连续两次长度/修改时间稳定后再解码、最多 20 次 300ms 重试;切歌/上一首/下一首时重置检测状态并先显示默认封面 BluetoothMusicViewModel.loadFileImageToView()改为返回解码是否成功,解码失败时保留已有有效文件封面;无有效文件封面时才回退默认图
BtContactsManager已增加联系人列表快照防御:- PBAP 联系人读取完成后使用
contactsSnapshot - 保存 DB 和回调 UI 不再直接传共享的
mContactsList saveContactsList()增加null判断和防御拷贝
- PBAP 联系人读取完成后使用
ContactsFragment已调整联系人页显示时序:- LiveData 收到非空联系人时,先填充
mPhoneBookList,再显示联系人列表 - 已同步但本地联系人列表为空时,先显示 loading,不提前显示空列表
- 点击一键同步且 PBAP 正在同步时,联系人页显示 loading,避免重复同步 Toast
- 重新同步期间如果读取到旧库联系人,但本轮 PBAP 未完成(
isPhoneBookPulling=true && getContactLoadWonType=false),忽略这批旧数据并保持 loading,避免提前显示旧通讯录列表
- LiveData 收到非空联系人时,先填充
YX_CarControl已移除配对弹窗中对BtContactsControlManager.getInstance().init(context)的调用:YX_CarControl只保留自动同步开关设置- 联系人/PBAP 同步统一由
YX_BlueTooth进程负责 - 避免
YX_CarControl和YX_BlueTooth两个进程同时注册 PBAP 完成广播并重复删写联系人表
CallLogsFragment已修复通话页切回后仍显示 loading 的 UI 问题:onHiddenChanged(false)时重新执行setIsBtConnect(...),确保切回通话页会按最新通话记录同步状态刷新 UIreadCallRecordUpdated()中已有通话记录列表时优先显示列表,不再被isPhoneBookPulling=true覆盖成 loading- 补充兜底:
notifyPhoneRecordList()收到非空通话记录时直接关闭 loading、显示通话列表,避免通话记录已到但外层状态未及时收敛导致页面仍转圈
- 2026-06-06 复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1528_11_585.log确认联系人页重新同步期间仍会显示旧列表:15:31:00.768第二次phoneBookPullRequest15:31:04.208到15:31:20.734多次切回联系人页,均为isPhoneBookSynchronized = true isPhoneBookPulling=true- 本轮联系人实际到达为
15:32:25.450 onContactsPhoneBook = 1973 - 根因是切回联系人页时
syncPhoneBookSynchronized(true)只看本地库已有联系人,未优先判断“本轮同步尚未完成”,导致旧内存列表重新显示
- 已追加联系人同步中隐藏旧列表修复:
BtContactsManager.phoneBookPullRequest()开始新一轮同步时重置isContactsPullCompeted=falseContactsFragment.syncPhoneBookSynchronized()优先判断isPhoneBookPulling && !getContactLoadWonType(),该状态强制显示 loading 并隐藏联系人列表showContactsLoadingPanel()统一隐藏蓝牙连接面板并调用updateLoadPage(true)
- 2026-06-06 复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1556_40_545.log确认通话记录“显示空”不是同步失败:- 无
Dex checksum does not match 15:57:41.356通话记录同步到mCallRecordBeanList size = 5115:57:41.377保存通话记录saveCallRecordList size = 5115:58:24.959切回通话页后notifyPhoneRecordList list == 51- 根因是
CallLogsFragment.notifyPhoneRecordList()先填充字段mCallRecordList,再把同一个 list 传给CallRecordAdapter.setData();Adapter 内部先clear()再addAll(),导致自清空,日志显示 51 但 Adapter 实际为空
- 无
- 已修复通话记录 Adapter 自清空问题:
CallLogsFragment.notifyPhoneRecordList()改为传入new ArrayList<>(mCallRecordList),避免 Adapter 清空同一个数据源引用
验证记录
- 2026-06-08:蓝牙音乐封面兜底修复后,
.\gradlew :YX_Music:assembleDebug编译通过。第一次沙箱内编译因读取本机 Android SDKandroid.jar权限失败,使用非沙箱权限重跑通过。 - 2026-06-08:
YX_BluetoothSDK封面路径有效性过滤后,.\gradlew :YX_BluetoothSDK:assembleDebug编译通过;随后.\gradlew :YX_Music:assembleDebug、.\gradlew :YX_BlueTooth:assembleDebug集成编译通过。 - 2026-06-08:
BluetoothMusicFragment切歌封面延迟加载修复后,.\gradlew :YX_Music:assembleDebug编译通过。 - 2026-06-09:
YX_Music固定 ID3 封面文件稳定性检测和重试修复后,.\gradlew :YX_Music:assembleDebug编译通过;尚待推送设备后复测切歌封面是否仍出现默认图/上一首封面/解码失败。 - 2026-06-09:
YX_Music.apk已推送到设备/system/app/YX_Music/YX_Music.apk,设备旧包已备份到/sdcard/YX_Music.apk.bak_20260609_112853;尚未重启设备,需重启后复测以避免旧 dex/oat 缓存影响。 - 2026-06-09:重新执行
.\gradlew :YX_Music:assembleDebug编译通过,并再次推送/system/app/YX_Music/YX_Music.apk;推送前设备当前包备份到/sdcard/YX_Music.apk.bak_20260609_113508,随后已重启设备。 - 2026-06-09:重启后启动验证日志已保存到
C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_music_yxmusic_only_20260609_1142.log:sys.boot_completed = 1pm path com.yaxon.music指向/system/app/YX_Music/YX_Music.apk- 通过
am start -n com.yaxon.music/com.yaxon.musicplay.view.UsbMainActivity可启动应用 - 启动阶段未发现
Dex checksum、FATAL EXCEPTION、获取bitmap为null、Failed to create image decoder - 当前日志显示
isMusicPlaying false,尚未覆盖蓝牙播放中切歌封面场景;需现场播放蓝牙音乐并切歌后再抓日志确认
- 2026-06-09:分析
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1409_00_592.log,最后一段同专辑与我无关复用固定封面路径;14:18:50-14:18:51底层写/sdcard/ID3/id3cover.JPEG时出现length=0 -> 16146 -> 24220 -> 43843,YX_Music已等待文件稳定,SystemUI/Launcher仍会直接读固定路径。为复现确认,YX_Music仅追加诊断日志:BluetoothMusicFragment记录 ID3 文件稳定确认、准备加载、加载失败重试、重置检查状态BluetoothMusicViewModel记录默认封面设置、文件封面解码成功的文件长度/修改时间/bitmap 宽高/byteCount
- 2026-06-09:
YX_Music蓝牙封面诊断日志追加后,.\gradlew :YX_Music:assembleDebug编译通过;已备份设备旧包到/sdcard/YX_Music.apk.bak_20260609_143548,新包已推送到/system/app/YX_Music/YX_Music.apk并重启设备;重启后确认sys.boot_completed=1,pm path com.yaxon.music指向系统路径。 - 2026-06-09:分析
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1451_00_890.log,确认YX_Music未解码失败,但固定 ID3 封面稳定判断过早:loadFileImageToView success = 1125,获取bitmap为null = 0,loadFileImageToView e = 0- 同一
lastModified下出现多个成功加载长度,例如1780988430000: 8072 -> 16146 -> 27885 - 典型时序:
15:00:30.103先成功加载length=8072,15:00:30.293底层才avrcp image download succeed,15:00:30.403再成功加载length=27885 - 根因是元数据回调密集,连续两次长度/修改时间相同可在数毫秒内发生,当前稳定判断会把半写入文件误判为稳定
- 2026-06-09:继续仅修改
YX_Music,延后SystemUI/Launcher:BluetoothMusicFragment增加ID3_ALBUM_STABLE_DURATION_MS=500,固定 ID3 封面必须长度/修改时间连续稳定满 500ms 才允许解码- 合并重复的 ID3 延迟加载请求,避免密集元数据回调堆叠多个相同 runnable
- 已显示同一长度/修改时间的 ID3 文件时跳过重复解码,减少反复 setImageBitmap
.\gradlew :YX_Music:assembleDebug编译通过;设备旧包备份到/sdcard/YX_Music.apk.bak_20260609_160339,新包已推送到/system/app/YX_Music/YX_Music.apk并重启;重启后确认sys.boot_completed=1,pm path com.yaxon.music正常
.\gradlew :YX_BlueTooth:assembleDebug已编译通过。- 最新
ContactsFragmentUI 时序修改已编译通过,但在用户中断前尚未执行adb push到设备验证。 - 2026-06-06:
YX_BlueToothAPK 已重新 clean 编译并推送到/system/app/YX_BlueTooth。 - 2026-06-06:
CallLogsFragmentloading UI 修复后,.\gradlew :YX_BlueTooth:assembleDebug编译通过,并已推送到/system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小21232834。 - 2026-06-06:
CallLogsFragment.notifyPhoneRecordList()非空列表强制关闭 loading 兜底后,.\gradlew :YX_BlueTooth:assembleDebug编译通过,并已推送到/system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小21232896。 - 2026-06-06:
ContactsFragment重新同步期间过滤旧库联系人兜底后,.\gradlew :YX_BlueTooth:assembleDebug编译通过,并已推送到/system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小21233011。 - 2026-06-06:联系人页切回时隐藏旧列表修复后,
.\gradlew :YX_BlueTooth:assembleDebug编译通过,并已推送到/system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小21233092。 - 2026-06-06:通话记录列表 Adapter 自清空修复后,
.\gradlew :YX_BlueTooth:assembleDebug编译通过,并已推送到/system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小21233566。 - 2026-06-06 复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1605_11_651.log:- 用户确认通话列表已有显示
- 通话记录同步成功:
16:05:58.835 mCallRecordBeanList size = 51,16:05:58.843 saveCallRecordList size = 51,16:05:59.636 notifyPhoneRecordList list == 51 - 通讯录同步成功:
16:06:24.788 contacts read completed size = 1973,16:06:40.194 onContactsPhoneBook = 1973 BtContactsControlManager = 0,未出现ConcurrentModificationException、onContactsPullFailed、通讯录同步失败- 仍出现
Dex checksum does not match,最终验收建议重启设备后再抓一轮无 dex 缓存异常日志
- 2026-06-06 最新复测日志
C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1612_17_622.log作为当前最终有效验证:Dex checksum does not match = 0com.yaxon.carcontrol只出现BluetoothPairedRequestView配对弹窗/自动同步开关日志,BtContactsControlManager = 0com.yaxon.bluetooth唯一触发16:13:20.668 phoneBookPullRequest- 通话记录同步和显示成功:
16:14:11.907 mCallRecordBeanList size = 51,16:14:11.921 saveCallRecordList size = 51,16:14:12.673 notifyPhoneRecordList list == 51;同步完成后切回通话页16:15:36.739 notifyPhoneRecordList list == 51 - 通讯录同步成功:
16:14:35.249 contacts read completed size = 1973,16:14:51.422 onContactsPhoneBook = 1973 - 未出现
ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
待办事项
- 清空蓝牙联系人/通话记录表后重新验证:
- 联系人权限允许后点击一键同步
- 同步过程中切换联系人页/通话记录页
- 确认不再短暂显示 0 个联系人列表
- 确认同步完成后显示实际联系人数量
- 确认无
ConcurrentModificationException和“通讯录同步失败” Toast
- 编译并推送
YX_CarControl.apk后重启设备,重新确认只剩com.yaxon.bluetooth一个进程执行BtContactsManager联系人同步。
更新记录
- 2026-06-23:调整语音
playDislike(不喜欢这首歌)处理逻辑:- 用户确认本次不做文件备份。
YX_Voice/src/main/java/com/yaxon/voice/protocol/VoiceProtocol.java中CMD_PLAY_DISLIKE从原先复用下一首逻辑改为音频源取消收藏逻辑。- 视频源仍保持下一个视频:
sendVideoController(CMD_VIDEO_NEXT)。 - 收音机源改为复用
sendMusicUnFavorites(false),走现有收音机取消收藏按键链路。 - USB/蓝牙音乐源改为复用
sendMusicUnFavorites(true),向音乐模块发送CMD_PLAY_UNFAVORITES。 - 验证:
.\gradlew :YX_Voice:assembleDebug编译通过。
- 2026-06-22:按 TXZ 反馈调整“可见即可说”验证方向:
- TXZ 确认
shadow.register属于网页、小程序、自绘等非常规页面的主动插词注册方式;普通原生页面不需要主动同步给 TXZ,语音侧会通过无障碍扫描id/text/contentDescription/clickable/scrollable。 - 本次选
YX_CarControl的语音设置页fragment_voice.xml做普通页面扫描验证:为语音唤醒、免唤醒、免唤醒时长、系统语音包、唤醒词入口补充contentDescription、clickable/focusable和滚动容器 id。 CustomTriStateSwitch按文档要求在onInitializeAccessibilityNodeInfo()中暴露checkable/checked/clickable/enabled,便于 TXZ 判断自定义开关状态。VoiceFragment根据开关当前状态动态更新contentDescription:关闭态暴露“打开/开启”,开启态暴露“关闭/关掉”,避免状态型控件同时暴露正反插词导致动作反向。- 暂停
YX_CarControl启动后自动注册测试插词,不再自动发送shadow.register,但保留registerVisibleSpeakKeywords(...)/unregisterVisibleSpeakKeywords(...)预留接口供后续非常规页面使用。 - 验证:
.\gradlew :YX_CarControl:assembleDebug编译通过。
- TXZ 确认
- 2026-06-22 更新:语音设置页可能与既有语义命令重叠,不适合作为无障碍扫描效果验证页,已新增维保页独立测试点:
fragment_maintenance.xml为保养里程选项和保养重置按钮补充独特测试插词:可见测试关闭保养里程、可见测试五千公里、可见测试一万公里、可见测试三万公里、可见测试保养重置。MaintenanceFragment在对应点击入口增加visible speak test click: ...日志,便于判断语音命中后是否真实触发控件点击。- 验证:
.\gradlew :YX_CarControl:assembleDebug编译通过;新包已推送到/system/app/YX_CarControl/YX_CarControl.apk并重启设备,sys.boot_completed=1,pm path com.yaxon.carcontrol指向系统路径。
- 2026-06-22 更新:维保页 RadioButton/contentDescription 测试现场未响应,进一步简化为系统页可见标准按钮测试:
- 在
fragment_system.xml顶部新增标准Button,可见文本为可见测试按钮,依赖 TXZ 扫描android:text,避免 RadioButton、隐藏标签、既有语义干扰。 SystemFragment绑定按钮点击后打印visible speak simple test button clicked并 Toast可见测试按钮已点击。- 验证:
.\gradlew :YX_CarControl:assembleDebug编译通过;新包已推送到/system/app/YX_CarControl/YX_CarControl.apk并重启设备,sys.boot_completed=1,pm path com.yaxon.carcontrol指向系统路径。
- 在
- 2026-06-13:抽取“可见即可说”业务侧公共工具:
- 用户确认本次不做文件备份。
- 新增
YX_Lib_CommonBase/src/main/java/com/yaxon/common/base/voice/VisibleSpeakHelper.java,统一封装业务模块向YX_Voice发送cmd.visibleSpeakRegister/cmd.visibleSpeakUnregister的 Bundle 组装和 IPC 发送逻辑。 VisibleSpeakHelper同时提供parseExecute(Bundle),业务模块收到cmd.visibleSpeakExecute后可直接解析出packageName、className、pageName、keyword。YX_Lib_CommonBase/build.gradle增加implementation project(path: ':YX_IPC_SDK'),用于公共工具内部调用YxIPC。YX_CarControl的VoiceSettingManager保留测试插词注册时机和命中日志,但注册/注销发送改为复用VisibleSpeakHelper,避免后续模块重复手写 Bundle。- 验证:
.\gradlew :YX_CarControl:assembleDebug编译通过;.\gradlew :YX_Voice:assembleDebug编译通过。
- 2026-06-13 更新:为避免
YX_Lib_CommonBase扩大 IPC 依赖面,已移除YX_Lib_CommonBase/build.gradle中的implementation project(path: ':YX_IPC_SDK'):VisibleSpeakHelper改为只负责buildRegisterBundle(...)、buildUnregisterBundle(...)、parseExecute(...)等参数构造/解析,不再直接 import 或调用YxIPC。- 业务模块仍由自身已有的
YX_IPC_SDK依赖执行YxIPC.getInstance().sendRemoteNoCall(...);当前YX_CarControl已按此方式调整。 - 验证:
.\gradlew :YX_CarControl:assembleDebug、.\gradlew :YX_Voice:assembleDebug编译通过;单独.\gradlew :YX_Lib_CommonBase:assembleDebug在沙箱内因本机 Android SDKandroid.jar读取权限失败,非沙箱授权请求被审批服务异常拒绝,但YX_CarControl/YX_Voice编译已覆盖该公共库改动。
- 2026-06-13:预留“可见即可说”自动注册框架接口:
- 用户确认本次不做文件备份。
- 阅读《同行者可见即可说自动注册框架对接方案.docx》后,按文档预留
shadow.register、shadow.unregister、shadow.execute三段链路。 YX_Lib_CommonBase的VoiceNewConstant增加可见即可说 IPC 命令和参数 key:包名、类名、页面名、插词列表。YX_Voice的VoiceNewProtocolConst增加 TXZ 侧 method/字段常量:key_type、action、keywordList、cmd等。YX_Voice的BaseProtocol增加可见即可说注册/注销/执行分发;VoiceEventHelper负责把应用注册转成 TXZ 请求,并把命中的插词回传给目标应用;VoiceProtocol接收 TXZshadow.execute后回传应用。YX_CarControl的VoiceSettingManager增加registerVisibleSpeakKeywords(...)/unregisterVisibleSpeakKeywords(...)预留方法,并在语音 IPC 和 TXZ bind 都就绪后注册一组测试插词可见即可说测试#打开可见即可说测试,命中后当前只记录handleVisibleSpeakExecute日志,不执行真实业务动作。- 验证:
.\gradlew :YX_Voice:assembleDebug编译通过;.\gradlew :YX_CarControl:assembleDebug编译通过。 - 待现场验证:推送
YX_Voice.apk/YX_CarControl.apk后观察日志中是否出现registerVisibleSpeakKeywords、sendCommandTxz → method= shadow.register;说出测试插词后确认是否出现shadow.execute和handleVisibleSpeakExecute。 - 已知风险:TXZ 侧
shadow.register参数结构目前按文档文字和流程图推断为keywordList使用#分隔字符串;若同行者实际要求 array/list 或字段名不同,需要按现场日志调整。
- 2026-06-12:修复语音前台 TTS 播报转发命令:
- 用户确认本次不做文件备份。
- 核对链路:
YX_CarControl的VoiceSettingManager.sendForegroundTtsPlay(...)发送cmd.syncForegroundTtsText到YX_Voice。 YX_Voice的BaseProtocol已能将cmd.syncForegroundTtsText分发到VoiceEventHelper.playTtsOnUi(...)。- 修复点:
VoiceEventHelper.playTtsOnUi(...)转发 TXZ 时由syncBackgroundTtsText改为syncForegroundTtsText,后台playTtsInBackground(...)保持syncBackgroundTtsText不变。 - 验证:
.\gradlew :YX_Voice:assembleDebug编译通过。 - 待现场验证:触发仪表脱手报警“请手握方向盘”或其它前台 TTS 场景,确认日志出现
sendForegroundTtsPlay、playTtsOnUi且 TXZ 前台播报生效。
- 2026-06-09:新增语音前台 TTS 文本同步接口
syncForegroundTtsText:VoiceNewConstant增加 IPC 入站命令CMD_SYNC_FOREGROUND_TTS_TEXT = "cmd.syncForegroundTtsText"VoiceNewProtocolConst增加 TXZ 协议命令CMD_SYNC_FOREGROUND_TTS_TEXT = "syncForegroundTtsText"BaseProtocol新增前台 TTS 分支,收到cmd.syncForegroundTtsText后调用VoiceEventHelper.playTtsOnUi(...)VoiceEventHelper.playTtsOnUi(...)改为向 TXZ 发送syncForegroundTtsText,后台syncBackgroundTtsText保持不变VoiceSettingManager新增sendForegroundTtsPlay(String tts),供YX_CarControl侧发送前台 TTS.\gradlew :YX_Voice:assembleDebug编译通过.\gradlew :YX_CarControl:assembleDebug编译通过
- 2026-06-09:继续仅修改
YX_Music蓝牙音乐封面加载策略:- 切歌后
albumUrl为空时,不再立刻判定无封面;进入固定 ID3 封面/sdcard/ID3/id3cover.JPEG等待链路。 - 同一首歌已经显示文件封面后,后续空
albumUrl不清空当前封面。 - ID3 封面重试从原约 6 秒扩展为前 20 次 300ms、后续 1s、最多 50 次,总窗口约 36 秒,用于覆盖封面二十几秒后才下发的场景。
.\gradlew :YX_Music:assembleDebug编译通过;YX_Music.apk已推送到/system/app/YX_Music/YX_Music.apk并重启设备,sys.boot_completed=1,pm path com.yaxon.music指向系统路径。
- 切歌后
- 2026-06-16:修复蓝牙联系人页权限恢复后“正在同步通讯录”卡住:
- 用户确认本次不做文件备份。
- 复现日志
2026_0616_1437_51_345.log显示本轮phoneBookPullRequest = 0、onContactsPullCompleted = 0,不是 PBAP 真同步;权限置 0 后ContactListViewModel仍回调onContactsPhoneBook = 257,但ContactsFragment会先清空mPhoneBookList并因无权限返回。 - 权限重新确认后,页面状态为
isPhoneBookSynchronized = true isPhoneBookPulling=false,但内存列表为空,旧逻辑直接显示llLoading,导致“正在同步通讯录”没有后续回调关闭。 ContactsFragment增加本地通讯录重读收敛:已同步且列表为空、未处于 PBAP 拉取时,只触发一次readPhoneBook()从本地库回填;若回填为空则关闭 loading 并显示下载入口,避免无限 loading。- 验证:
.\gradlew :YX_BlueTooth:assembleDebug编译通过。
- 2026-06-16:更新版本后复核日志
2026_0616_1437_51_345.log:- 日志更新时间为
2026/6/16 15:14:16,大小16500679。 15:09:33.236与15:13:01.840均触发BluetoothPbapClient: phoneBookPullRequest,随后分别在15:09:40.295、15:13:09.663出现onContactsPullCompleted。- 两轮 PBAP 均读到联系人
257和通话记录51,最终状态均回到isPhoneBookSynchronized=true isPhoneBookPulling=false,说明真实同步链路已完成。 - 本次日志未出现新修复分支日志
phonebook synchronized but local list is empty, reload local phonebook,暂不能证明本地空列表兜底分支已被执行。 - 日志仍出现
/system/app/YX_BlueTooth/YX_BlueTooth.apk!classes2.dex的Dex checksum does not match,建议推送系统 APK 后重启设备或清理旧 oat/dex 缓存再复测,避免运行旧代码或混合 dex。
- 日志更新时间为
- 2026-06-16:复测日志
2026_0616_1533_52_917.log验证蓝牙联系人权限恢复兜底:- 日志更新时间为
2026/6/16 15:37:06,大小11242513,本轮未检出Dex checksum does not match。 15:35:03、15:35:27、15:36:06、15:36:38多次出现通讯录权限置 0;权限关闭期间本地库仍能回调onContactsPhoneBook = 257,但 UI 按权限拒绝分支处理。15:35:16.420和15:36:50.420出现phonebook synchronized but local list is empty, reload local phonebook,证明新增兜底分支已执行。- 兜底触发后立即执行
BtContactsManager: readPhoneBook,随后回调ContactListViewModel: onContactsPhoneBook = 257,页面可从本地通讯录恢复,不再停留在无限 loading。 - 用户手动/后续触发真实 PBAP 同步时,
15:35:34.363、15:36:15.769、15:36:52.932出现phoneBookPullRequest,并分别完成onContactsPullCompleted、联系人257、通话记录51、getStartPullLivedata = false。 - 当前结论:修复有效;后续可考虑减少权限恢复时的重复
readPhoneBook()触发,但不影响主问题闭环。
- 日志更新时间为
- 2026-06-23:按 TXZ 无障碍服务
process is bad/扫描不起来问题调整YX_Voice恢复策略:- 用户前置确认本次不做文件备份,直接修改。
VoiceService不在onCreate()常态执行无障碍绑定恢复,改为 TXZonConnected()后延迟 2 秒执行一次。- 新增
mTxzConnected与mAccessibilityServiceRefreshDone状态保护:TXZ 断开后取消待执行任务并允许下次连接重新恢复;已成功恢复后不重复重绑。 - 恢复逻辑先读取当前
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,保留已有服务,只针对com.txzing.voiceassistant/com.txznet.shadow.words.ShadowAccessibilityService做移除再加回,触发系统重新绑定 TXZ 无障碍服务。 - 不主动追加
YX_Factory无障碍服务,避免扩大影响面;如果 Factory 已在列表中会被原样保留。 - 补充空列表保护,避免
ENABLED_ACCESSIBILITY_SERVICES为null或空字符串时containsAccessibilityService()空指针。 - 验证:
.\gradlew :YX_Voice:assembleDebug编译通过。后续推送设备后重点看refreshAccessibilityServiceBinding rebind txz、ensureAccessibilityServiceEnabled、ShadowService: onServiceConnected、VisualCommandModule: tryUseAccessibilityService: result = 1、registerKeywords。
- 2026-06-23:修复语音
playDislike/playUnfavorites把未收藏歌曲反向收藏的问题:- 用户确认本次不做文件备份。
- 根因:
YX_MusicSDK的MusicService将CMD_PLAY_FAVORITES和CMD_PLAY_UNFAVORITES合并到onCollectCurrentMusic();下游SongProvider.collectPlayRecord()是 toggle 逻辑,当前歌曲isCollect=0时执行“取消收藏”会变成收藏。 MusicService将收藏和取消收藏分支拆开;CMD_PLAY_UNFAVORITES改调VoiceControlManager.onUnCollectCurrentMusic()。VoiceControlManager.onUnCollectCurrentMusic()先读取当前播放歌曲,只有已收藏时才复用原 toggle 取消收藏;未收藏时跳过,避免反向收藏。- 收音机链路同步修正:
VoiceEventHelper.sendMusicUnFavorites(false)不再发送KEYCODE_MEDIA_RECORDtoggle 按键,改为向YX_Radio发送CMD_PLAY_UNFAVORITES;VoiceRadioManager收到后仅在当前电台已收藏时调用cancelCollection()。 - 验证:
.\gradlew :YX_Music:assembleDebug :YX_Voice:assembleDebug :YX_Radio:assembleDebug编译通过。
2026-06-24 playDislike atomic handling
- Continue voice
playDislikefix without backup. - Further narrowed
playDisliketo one IPC command fromYX_Voiceto the target media module instead of sending cancel-favorite and next as two independent actions. VoiceEventHelper.sendMusicDislike(...)now sendscmd.playDislikeand carries current media type withtypeKey.VoiceProtocolkeeps video behavior as next video; radio and music now dispatch onlysendMusicDislike(...).YX_MusicSDKhandlescmd.playDislikeinMusicService: USB music performs safe un-favorite only when collected, then next track; Bluetooth music does not touch USB favorite state and only sends Bluetooth next when it has focus.YX_Radiohandlescmd.playDislikeinVoiceRadioManager: cancel current station only when already collected, then callnextAutoSearch().- Verification:
.\gradlew :YX_Music:assembleDebug :YX_Voice:assembleDebug :YX_Radio:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl visible-speak cleanup
- User confirmed no backup before editing.
- Reverted temporary visible-speak validation code from
YX_CarControl: removed the system page可见测试按钮, its click log/toast, maintenance page可见测试...content descriptions, and maintenance test click logs. - Kept formal accessibility/visible-speak enhancements for the voice settings page: dynamic switch
contentDescription, voice settings control descriptions, andCustomTriStateSwitchaccessibility node state. - Verified no remaining
可见测试,visible speak test,visible speak simple,btn_visible_speak_simple_test, ormaintenance_scroll_viewtest markers underYX_CarControl/src/main. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: removed the shared
CustomTriStateSwitchaccessibility-node change to keep this submission scoped to the voice settings page only; remainingYX_CarControldiff is limited toVoiceFragment.java,fragment_voice.xml, andstrings.xml. - Verification after removal:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: kept multi-keyword visible-speak text with
#, but changed all addedfragment_voice.xmlcontentDescriptionvalues from hard-coded text to@string/...references for maintainability. - Verification after XML string-reference change:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: added matching
values-en/strings.xmltranslations for all newvoice_speak_*resources while preserving#multi-keyword format. - Verification after English resource completion:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl EnergyFragment visible-speak
- User confirmed no backup before this round of edits.
- Added native visible-speak accessibility text for the Energy page without using
shadow.register, following TXZ native page scanning viacontentDescription. - Scope:
EnergyFragment.java: dynamically updates charge/discharge switchcontentDescriptionaccording to current checked state, so closed state exposes open keywords and opened state exposes close keywords.fragment_energy.xml: addedcontentDescriptionfor charge allow, discharge allow, reset trip after last charge, more trips, and reset statistics controls.dialog_trip_record.xml: addedcontentDescriptionfor subtotal trip A/B reset buttons.values/strings.xmlandvalues-en/strings.xml: added matchingenergy_speak_*multi-keyword strings using#.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: moved
energy_speak_*resources into the voice settings resource section and labeled them with可见即可说 - 能源页面; also labeled existing visible-speak groups for System and Voice pages in both Chinese and English string files. - Verification after resource regrouping:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl SystemFragment visible-speak
- Followed reference commit
ef20ec3ca823aece8d1beb8e45442cdab7ba420fand reused the native page scanning approach. - Added
contentDescriptionvisible-speak keywords to System page clickable containers: factory reset, USB upgrade, system update, time setting, language setting, unit setting, disclaimer, privacy policy, and permission authorization. - Added matching
system_speak_*string resources invalues/strings.xmlandvalues-en/strings.xml, with multiple keywords separated by#. - No Java behavior change was needed in
SystemFragment.java; existing click listeners already dispatch the actions after TXZ triggers the scanned views. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: moved the new
system_speak_*resources into the voice settings resource section in both Chinese and English string files, because these entries are visible-speak keywords rather than ordinary System page labels. - Verification after resource regrouping:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_Voice background TTS toast
- User confirmed no backup before editing.
- Requirement: both
playTtsOnUi()andplayTtsInBackground()are in use, but foreground TTS remains only a reserved API; actual playback must continue throughsyncBackgroundTtsText. - Added
VoiceToastHelperinYX_Voiceusing systemToastwith custom view, based on the existingYX_CarControldesigned Toast style instead of using a Dialog-style helper. - Added independent
YX_VoiceToast resources:voice_toast_view.xml,voice_toast_bg.xml, day/night colors, and Toast dimensions. - Refactored
VoiceEventHelpersoplayTtsOnUi()andplayTtsInBackground()both callsendBackgroundTtsWithToast(...); this shows the designed Toast and sends TXZsyncBackgroundTtsText. - Kept foreground TTS command disabled;
playTtsOnUi()comment now explicitly states foreground TTS is not supported and the reserved entry still uses background TTS. - Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl SoundFragment visible-speak
- User requested native visible-speak support for the Sound page and previously confirmed no backup before editing.
- Added Sound page keywords through native TXZ accessibility scanning via
contentDescription; this page does not useshadow.register. - Scope:
fragment_sound.xml: added visible-speak keywords for sound field options, media/phone/navigation/voice volume bars, mute switch, and key tone switch.layout_eq.xml: added visible-speak keywords for default, classical, pop, jazz, electronic, rock, and custom EQ options.SoundFragment.java: dynamically refreshes mute and key tone switchcontentDescriptionby current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedsound_speak_*resources under可见即可说 - 声音页面.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Known validation boundary: the volume bars are custom
ItemCustomSeekBarViewcontrols; keywords are exposed for scanning, but exact voice-side adjustment behavior depends on TXZ accessibility handling of custom drag/click views. - Follow-up: per user decision, removed visible-speak keywords from the four custom volume bars for now; Sound page visible-speak scope remains sound field, EQ options, mute switch, and key tone switch.
2026-06-24 YX_CarControl ADAS hands-off TTS debounce
- User confirmed no backup before editing.
- Symptom from
2026_0624_1540_03_018.log:请手握方向盘was heard as repeated leading fragments like请 请 请 请 请 手握方向盘. - Root cause:
sendAdasLdsHandsOffWarnStChange(2)was triggered repeatedly while the warning stayed active, sosendForegroundTtsPlay("请手握方向盘")and the downstream background TTS command were sent repeatedly; each new request interrupted the previous playback. - Fix scope:
VoiceSettingManager.sendAdasLdsHandsOffWarnStChange(int sts)now only sends TTS when the ADAS hands-off warning enters0x02from a non-warning state, with a 5-second debounce against rapid signal chatter. - Expected validation: repeated
sendAdasLdsHandsOffWarnStChange sts = 2logs may still appear, but repeated TTS requests should be replaced bysendAdasLdsHandsOffWarnStChange skip duplicate tts;sendForegroundTtsPlay tts = 请手握方向盘,playTtsOnUi, and TXZsyncBackgroundTtsTextshould no longer repeat at millisecond level. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up after reboot log
2026_0624_1612_11_547.log: ADAS warning status still bounced between1and2; the previous 5-second debounce allowed another TTS after the time window expired, producing 15 sends and 15 duplicate skips. - Updated strategy: replace fixed 5-second replay debounce with a warning-cycle state gate. The first
sts=2still plays immediately, but the warning is not considered cleared untilsts != 2remains stable for 2 seconds; only after that can a latersts=2play again. - Verification after stable-clear update:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl ShowFragment visible-speak
- User requested visible-speak support for the Display page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_show.xml: added visible-speak keywords for screen off, theme mode, hidden display mode controls, wallpaper options, projection options, and video safety mode.ShowFragment.java: dynamically refreshes Bluetooth phone projection, media audio projection, and video safety modecontentDescriptionaccording to current selected/checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedshow_speak_*resources under可见即可说 - 显示页面.
- Brightness bars are custom
ItemCustomSeekBarViewcontrols and were intentionally not added per the latest user decision to skip sliders for now. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-07-01 YX_CarControl SoundFragment visible-speak short keywords
- Log
C:\Users\Administrator\Desktop\徐工日志\2026_07_01_20_22_01\yx_log\2026_0701_2010_07_922.logshowed TXZ accessibility was connected and Sound page keywords were scanned intoVisualCommandModule offline, but short utterances such as古典,爵士, and主驾returnedTxzVisualCommand matchedEntry = null. - Root cause: Sound page
contentDescriptionentries only exposed longer phrases such as古典音效and主驾音场; TXZ visible-command matching did not treat short utterances as substring matches. - Fix: prepend standalone short keywords to Sound page visible-speak string resources:
全车,主驾,副驾,默认,古典,流行,爵士,打击, and摇滚; mirrored the same style invalues-en. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up from log
C:\Users\Administrator\Desktop\徐工日志\9_45_17\yx_log\2026_0622_1451_07_056.log: utterance自定义still returnedTxzVisualCommand matchedEntry = nullbecause the scanned Sound page list only contained自定义音效#...and自定义音场#.... - Final decision: do not add a bare
自定义keyword because the Sound page has two visible自定义controls. Keep explicit自定义音效for EQ custom and自定义音场for sound-field custom to avoid duplicate keyword ambiguity. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl ConnectFragment visible-speak
- User requested visible-speak support for the Connect page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_connect.xml: added visible-speak keywords for Bluetooth settings entry, WLAN settings entry, Bluetooth switch, and WLAN switch.ConnectFragment.java: dynamically refreshes Bluetooth and WLAN switchcontentDescriptionaccording to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedconnect_speak_*resources under可见即可说 - 连接页面.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl SmartDriveFragment visible-speak
- User requested visible-speak support for the Smart Drive page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_smart_drive.xml: added visible-speak keywords for ACC time gap, LDW switch/sensitivity, AEBS switch, FCW sensitivity, blind spot monitor, driver monitor, and traffic sign recognition.SmartDriveFragment.java: dynamically refreshes LDW, AEBS, blind spot monitor, driver monitor, and traffic sign recognition switchcontentDescriptionaccording to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedsmart_speak_*resources under visible-speak resource groups.
- Hidden BSIS/MOIS switches were intentionally not added because this fragment has no matching business listener for them in the current implementation.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl LightControlFragment visible-speak
- User requested visible-speak support for the Light page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_light_control.xml: added visible-speak keywords for ambient light, breathing mode, auto dome light, safety light radio options, smart welcome light, smart high/low beam, and logo light.LightControlFragment.java: dynamically refreshes ambient light, breathing mode, logo light, auto dome light, smart welcome light, and smart high/low beam switchcontentDescriptionaccording to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedlight_speak_*resources under visible-speak resource groups.
- Custom color picker, ambient light brightness, and key backlight selector were intentionally not added because they are touch-coordinate based custom selector/slider controls.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl CarControlFragment visible-speak
- User requested visible-speak support for the Car Control page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_car_control.xml: added visible-speak keywords for adaptive suspension modes, ESC OFF, climb mode, hill start assist, downhill assist, brake energy recovery, smart door lock, smart roof, rain sensor sensitivity, and wireless charging.CarControlFragment.java: dynamically refreshes switchcontentDescriptionaccording to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.values/strings.xmlandvalues-en/strings.xml: addedcar_speak_*resources under visible-speak resource groups.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl MaintenanceFragment visible-speak
- User requested visible-speak support for the Maintenance page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_maintenance.xml: added visible-speak keywords for maintenance mileage off, 5000 km, 10000 km, 30000 km, and maintenance reset.values/strings.xmlandvalues-en/strings.xml: addedmaintenance_speak_*resources under visible-speak resource groups.
- No Java behavior change was needed; existing click listeners still handle mileage selection and reset confirmation.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-24 YX_CarControl PermissionAuthorizationFragment visible-speak
- User requested visible-speak support for the Permission Authorization secondary page and previously confirmed no backup before editing.
- Added native TXZ accessibility scanning keywords via
contentDescription; this page does not useshadow.register. - Scope:
fragment_permission_authorization.xml: added visible-speak keywords for back, authorization duration off/180 days/360 days, and all visible permission switches.PermissionAuthorizationFragment.java: dynamically refreshes permission switchcontentDescriptionaccording to current checked state, so disabled/off state exposes open keywords and enabled/on state exposes close keywords.dialog_permission_confirm.xml: added visible-speak keywords for confirm close and cancel, covering the permission close confirmation flow.values/strings.xmlandvalues-en/strings.xml: addedpermission_speak_*resources under visible-speak resource groups.
- Existing authorization duration, Settings.System persistence, permission confirmation, and switch enable/disable business logic were kept unchanged.
- Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully.
2026-06-26 YX_Voice drowsy TTS reminder dialog
- User confirmed no backup for this TTS reminder work.
- Requirement update: fatigue reminder content must be read directly from
VoiceNewConstant.DROWSY_DRIVING. - Kept the previous design decision that foreground TTS is only a reserved API; both
playTtsOnUi()andplayTtsInBackground()still send real audio through TXZsyncBackgroundTtsText. - Added
VoiceDrowsyReminderHelperinYX_Voice:- Only triggers for
VoiceNewConstant.DROWSY_DRIVING, with punctuation/blank normalization so half-width!input can still match the full-width constant. - Shows a service-safe overlay reminder dialog after the existing toast path.
- Dialog title:
消息提醒; content:VoiceNewConstant.DROWSY_DRIVING; buttons:稍后提醒and不再提醒. 稍后提醒only dismisses the current dialog;不再提醒persists a local SharedPreferences flag and suppresses future fatigue dialogs without suppressing TTS audio or toast.
- Only triggers for
- Added
SYSTEM_ALERT_WINDOWpermission and released the overlay fromVoiceService.onDestroy()to avoid stale windows during service restart. - Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-26 YX_Voice remindLaterOrNever command
- User requested voice command
remindLaterOrNeverfor the fatigue reminder dialog buttons稍后提醒and不再提醒. - Added TXZ protocol constant
CMD_REMIND_LATER_OR_NEVER = "remindLaterOrNever"inYX_Voice. VoiceProtocolnow consumesremindLaterOrNeverlocally and callsVoiceDrowsyReminderHelperdirectly, because the reminder overlay belongs to the voice process and does not need cross-process dispatch.- Parameter parsing is tolerant across
type,value,action,cmd,text,word,keywordList,name, and raw JSON fallback:- Contains
不再/never/noMore/not_remind: persist不再提醒and dismiss. - Contains
稍后/later/snooze/delay: dismiss only.
- Contains
- Touch buttons and voice command now share the same internal action handler.
- Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-26 YX_Voice drowsy TTS test broadcast
- Added handling for the already registered
com.voice.test.tts.ybtest broadcast inYX_VoiceTestReceiver. - If the broadcast has no
ttsextra, it defaults toVoiceNewConstant.DROWSY_DRIVING, so a single adb command can trigger the fatigue TTS toast/dialog chain. - If
--es tts ...is supplied, that custom text is sent through the sameplayTtsOnUi()path. - Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-26 YX_Voice drowsy dialog day mode
- Continued without backup per user preference.
- Matched the drowsy reminder dialog day/night colors to the
YX_CarControlsystem version dialog style: light day background/text/buttons, dark night background/text/buttons, blue pressed state. - Added blank-area dismiss for the overlay root while the dialog container consumes inner clicks, matching
setCanceledOnTouchOutside(true)behavior from the reference dialog. - Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully. - Follow-up: moved the drowsy dialog title to
strings.xml, and moved fixedremindLaterOrNeverprotocol attr values稍后提醒/不再提醒toVoiceNewProtocolConst. - Verification after constant/resource cleanup:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-26 YX_Voice dialog/toast day-night compatibility
- Continued without backup per user preference.
- Requirement: the voice reminder dialog and TTS toast must follow the current day/night theme, referencing the vehicle settings theme behavior.
- Added
VoiceThemeHelperinYX_Voice:- Reads the actual theme first from
Settings.Global.REQUEST_THEME_MODE, which is written by the car-control theme manager. - Falls back to
Settings.System[DataUtils.THEME_MODE_CHANGED]. - In auto mode, falls back to
Settings.Global.POST_LIGHT_STATUS. - Finally falls back to the current resource
Configuration.UI_MODE_NIGHT_MASK.
- Reads the actual theme first from
- Updated
VoiceDialogHelperandVoiceToastHelperto inflate their custom views with the themed context, sovalues/colors.xmlandvalues-night/colors.xmlare selected correctly. - Existing day/night color resources are kept in
YX_Voice: light dialog/toast colors for day mode, dark dialog/toast colors for night mode.
2026-06-26 YX_Voice dialog theme refresh while showing
- User reported the dialog did not change after switching day/night mode; log
2026_0626_1644_36_540.logwas analyzed. - Key timeline from the log:
16:45:28.982voice received the smoking reckless driving TTS and scheduled the safety dialog.16:45:30.044VoiceDialogHelper: showInternal: drowsy reminder shown, so the overlay view was already added toWindowManager.16:45:34.464VoiceEventHelper: themeMode 1andsyncScreenMode status = darkMode, meaning the theme switch happened several seconds after the overlay was created.
- Root cause: a custom
WindowManageroverlay does not automatically reinflate or re-resolvevalues-nightresources when the system theme changes after it is already visible. - Fix:
- Added
VoiceDialogHelper.onThemeChanged()to refresh an already-visible dialog by preserving the current title/content, removing the old overlay view, and adding a new themed view. SettingCommonObservernow calls the refresh hook onTHEME_MODE_CHANGEDand also listens toSettings.Global.REQUEST_THEME_MODE.VoiceThemeHelpernow prefers the currentSettings.System[THEME_MODE_CHANGED]value for manual light/dark mode; auto mode still usesREQUEST_THEME_MODE/POST_LIGHT_STATUSfallback.
- Added
- Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-26 YX_Voice toast theme refresh while showing
- User reported the TTS toast did not change after switching day/night mode; log
2026_0626_1705_01_941.logwas analyzed. - Key timeline:
17:06:15.791/17:06:17.489VoiceEventHelper: playTtsOnUireceived HVAC CO2 TTS text and showed toast.17:06:18.900user clicked dark mode switch, then17:06:18.917voice syncedlightMode.- The screenshot still showed black toast because the custom toast view was created while the theme was still dark, before the theme switch.
- Root cause: custom Toast views also do not re-resolve resources after being shown; cancelling/recreating is required if theme changes during the toast display window.
- Fix:
VoiceToastHelpernow keeps the last visible toast text and visibility window.- On
THEME_MODE_CHANGED/REQUEST_THEME_MODEobserver callback, it delays briefly, cancels the old toast, and re-shows the same text with the current themed context. - Added
VoiceToastHelper: refreshForThemeChangedInternallog for validation.
- Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-27 YX_CarControl fatigue reminder interval UI
- User confirmed no backup and required only adding the new fatigue reminder feature, without changing historical feature logic.
- Added the new Smart Drive "疲劳监测提醒" UI under existing "驾驶人员监测": switch + reminder interval control.
- Interval requirement: 0~60 minutes, default 20 minutes, step 10 minutes; progress area is 702dp wide and 100dp high, so each 10-minute step maps to 117dp.
- Existing progress controls were checked:
ItemCustomSeekBarViewis shared by Sound/Light/Display pages and normally displays an icon.CustomLightSelectoris a 1~5 backlight selector with icon and dividers.- EQ/native SeekBars are not suitable for this horizontal reminder UI.
- To avoid impacting shared historical controls, the temporary shared-control icon-removal approach was dropped; a dedicated no-icon
FatigueReminderIntervalSeekBarViewis used only by the new Smart Drive fatigue reminder interval layout. - Verification:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Follow-up: the new fatigue reminder switch/interval still uses local keys in
SmartDriveFragment, but reads/writes throughDataManager+DataUtils.STATE_URIand now registersContentObservercallbacks, matching the existing data-center sync style.YX_CarControlcompiles againstsdklibs/yx_support_classes.jar, so adding constants only inYX_Supportsource does not affect this module build unless the support jar is regenerated or the dependency is changed. - Verification after the data-center observer adjustment:
.\gradlew :YX_CarControl:assembleDebugcompleted successfully. - Update: per user request, moved the fatigue reminder keys to
YX_SupportDataUtils:FATIGUE_REMINDER_SWITCHandFATIGUE_REMINDER_INTERVAL_MINUTE. The interval value is stored as an integer minute value, default 20. - Data center parity with
FATIGUE_MONITOR_SWITCH: addedYX_DataCenterServiceinitialization inYXContentProvider.mHasState, update dispatch inYXContentProvider.update(), newMessageEventIDs 2097/2098, andProviderDataSharedPreferencesputInt/getIntpersistence for the reminder switch and integer interval. - Build note: both
YX_CarControlandYX_DataCenterServicecompile againstsdklibs/yx_support_classes.jar, soYX_Support:makeJaror the existing support-jar replacement flow is needed before compiling these consumers with the newDataUtilsconstants.
2026-06-28 YX_Voice fatigue reminder interval
- User confirmed
YX_Voicemust associate the SmartDrive fatigue reminder settings:DataUtils.FATIGUE_REMINDER_SWITCHDataUtils.FATIGUE_REMINDER_INTERVAL_MINUTE
DROWSY_DRIVING,DISTRACTED_DRIVING,SMOKING_RECKLESS_DRIVING, andCALLING_RECKLESS_DRIVINGreminder dialogs are now gated by the reminder switch and throttled by the interval minute value.VoiceDialogHelperreads the DataCenter switch before showing; when enabled, it stores the last shown time and skips repeated dialogs until the configured interval expires. Interval0means no throttling.- Update: user clarified
不再提醒and稍后提醒must not persist locally and must recover after reboot.VoiceDialogHelpernow keeps both the last shown time and the current-process "never remind" flag only in static memory; it does not write SharedPreferences or changeFATIGUE_REMINDER_SWITCHfor these two dialog actions. - Update: user clarified the four dialog reminders are four independent functions. The current-process "never remind" flag and the last shown time are now tracked separately for
DROWSY_DRIVING,DISTRACTED_DRIVING,SMOKING_RECKLESS_DRIVING, andCALLING_RECKLESS_DRIVING; one type's button action or interval throttle does not suppress the other types. SettingCommonObserverandBaseVoiceServicenow observe the two fatigue reminder URIs; turning the switch off dismisses a pending/showing reminder overlay.- Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-06-29 YX_Voice radio syncMediaFocus
- User confirmed no backup before editing.
- Log
2026_0629_1152_07_466.logshowed Radio playback was active and TXZ receivedsyncFocusPlayStatus=trueplussyncRadioPlayType=fm, but nosyncMediaFocus: radio. - Root cause:
MediaObserveronly sent media focus onSOURCE_IDchanges; Radio startup in the log updatedRADIO_INFOplayback state without a reliableSOURCE_IDchange notification. - Fix: when
RADIO_INFOreportsmState == 2and currentSOURCE_IDisYXUtils.SOURCE_RADIO,MediaObservernow sendsnotifySyncMediaFocus(VoiceNewProtocolConst.RADIO)before syncing play status. - Verification:
.\gradlew :YX_Voice:assembleDebugcompleted successfully.
2026-07-01 YX_BlueTooth ACC state cleanup
- User confirmed no backup before editing.
- Requirement: check whether Bluetooth still depends on legacy
ACC_STATE; Xugong currently follows power mode rather than old ACC state. - Finding:
YX_BlueToothBluetoothServicestill registeredACC_STATE, but the old disconnect/reconnect logic in that branch had already been commented out and only loggedACC state. - Fix: removed the unused
ACC_STATE_URIconstant, removed theACC_STATEobserver registration, and deleted the deadcase ACC_STATE_URIbranch. - Kept the active
LOW_VOLTAGE_POWER_STATElogic unchanged because it still performs Bluetooth phone/headset disconnect and phone reconnect behavior; only changed misleading logs/comments fromCAN ACC OFF/ONtoLOW_VOLTAGE_POWER_STATE OFF/ON. - Verification:
.\gradlew :YX_BlueTooth:assembleDebug --console plaincompleted successfully.
2026-07-02 YX_CarControl sound page visible-speak coverage check
- Rechecked Sound page main visible controls:
- Sound field: all-cabin, driver, passenger, custom sound field.
- Sound effect: default, classical, pop, jazz, electronic, rock, custom sound effect.
- Switches: mute and key tone.
- Sliders are intentionally not added to visible-speak yet, matching the previous decision.
- Fixes after the coverage check:
- The clickable custom-effect triangle in
layout_eq.xmlnow usessound_speak_eq_userinstead of the literalTriangle Button. sound_speak_close_mutenow also includes barejingyin/Mute, so the same short utterance can match both on and off states.
- The clickable custom-effect triangle in
- Boundary note: the custom EQ floating window (
layout_eq_user.xml/EqUserFloatView) has its own EQ buttons and seek bars but is a service overlay / secondary UI, not currently treated as part of the Sound main-page visible-speak scope. - Verification:
.\gradlew :YX_CarControl:assembleDebug --console plaincompleted successfully.
2026-07-02 YX_CarControl voice page visible-speak short keywords
- Analyzed log
2026_0702_1025_08_858.logfor Voice page visible-speak. - At
15:08:37,VoiceFragment: onStartandsyncVoiceSettingStatus status = truewere logged. - TXZ offline list contained Voice page entries such as
打开免唤醒#开启免唤醒,免唤醒10秒#设置免唤醒10秒#选择10秒,免唤醒15秒#设置免唤醒15秒#选择15秒, and免唤醒30秒#设置免唤醒30秒#选择30秒. - User utterance
免唤醒failed because the visible-speak entry only had open/close phrases, not the bare short keyword. - User utterance
10秒was recognized by TXZ ASR as十秒; the visible-speak entry did not include十秒, somatchedEntry = null. - Fix:
- Added bare
语音唤醒to both voice wakeup switch states. - Added bare
免唤醒to both wake-free switch states. - Added short numeric and Chinese-number forms for duration entries:
10秒#十秒,15秒#十五秒,30秒#三十秒.
- Added bare
- Verification:
.\gradlew :YX_CarControl:assembleDebug --console plaincompleted successfully.
2026-07-02 YX_CarControl sound page visible-speak key tone
- User reported saying bare
按键音on Sound page did not respond. - Finding:
SoundFragmentdynamically sets the key-tone switchcontentDescriptionbased on current state; original entries only exposed打开按键音#开启按键音or关闭按键音#关掉按键音. - Root cause inference: TXZ visible-speak matching does not reliably treat
按键音as a substring match for these longer phrases. - Fix: added bare
按键音to bothsound_speak_open_key_toneandsound_speak_close_key_tone; English resources added matching bareKey tone. - Expected verification: on Sound page, say
按键音; log should show the offline entry containing按键音#...andTxzVisualCommandmatched entry for按键音.
2026-07-02 YX_CarControl SmartDrive fatigue visible-speak short keywords
- Analyzed log
C:/Users/Administrator/Desktop/徐工日志/2026_07_02_15_25_54/yx_log/2026_0702_1025_08_858.log. - Root cause: SmartDrive page offline visible-speak list only exposed state-specific phrases such as
关闭疲劳监测提醒#关掉疲劳监测提醒; the bare utterance疲劳监测提醒producedmatchedEntry = null. - Fix: added bare short keywords to both open and close resources for
smart_speak_*_fatigue_monitorandsmart_speak_*_fatigue_reminder. - Verification:
.\gradlew :YX_CarControl:assembleDebug --console plaincompleted successfully.
2026-07-03 YX_Launcher boot-time Bluetooth music widget play
- User confirmed no backup before editing.
- Requirement: after boot, clicking the Launcher main-page music card play button should play Bluetooth music.
- Root cause:
YX_Launchermain page usesWidgetsMedia, whose play/prev/next buttons only sent global media keys.- On cold boot, Bluetooth music focus/MediaSession may not yet be established, so
KEYCODE_MEDIA_PLAY_PAUSEcan be ignored and does not explicitly trigger Bluetooth AVRCP play. WidgetsMedia.init()queried current focus package but hard-coded the initial client id as USB music, socom.yaxon.musiccould be treated as USB music even whenSOURCE_IDwas Bluetooth music.SystemUtils.startBtMusic()sendsis_click_bluetooth_music=true, butUsbMainActivityonly handledVoiceControlManager.PAGE_TYPE; the legacy Bluetooth-music extra was ignored.
- Fix:
WidgetsMedianow resolves the initial card source fromSOURCE_IDwhen focus package iscom.yaxon.musicor null.- For Bluetooth music source, Launcher card play/prev/next sends explicit broadcasts to
YX_Musicinstead of relying only on global media keys. - Added
BluetoothMusicControlReceiverinYX_Musicto callBtMusicControlManagerdirectly for play/pause/previous/next. - Play from stopped/cold state retries for up to 10 attempts at 500 ms intervals, with a request token so a later pause cancels stale retries.
UsbMainActivitynow handlesis_click_bluetooth_music=truein both initial launch andonNewIntent()and switches to the Bluetooth music page.
- Verification:
.\gradlew :YX_Music:assembleDebug --console plaincompleted successfully..\gradlew :YX_Launcher:assembleDebug --console plaincompleted successfully.
2026-07-03 YX_Launcher Bluetooth music widget retest log analysis
- User provided retest logs:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_12_02/yx_log/2026_0703_1050_00_000/2026_0703_1049_41_742.logC:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_12_02/yx_log/2026_0703_1050_33_402.log
- Key evidence:
- At
07-03 11:02:37.547,WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE:appears, proving the Launcher card play button click reachedWidgetsMedia. - No
WidgetMedia: sendBtMusicControl action = ..., noBluetoothMusicControlReceiver: onReceive action = ..., noBtMusicControlManager: playBtMusic, and noBluetoothAvrcpClient: playappear in the same retest logs. - Around the click,
NuPlayerDriver/MediaPlayerlogs appear, indicating the click path did not execute the explicit Bluetooth music broadcast control path. - Earlier logs show
WidgetMedia: initMedia: packageName ->null ,clientId: com.yaxon.musicsdk.managerandinitMedia:onMusicInfo, so the Launcher widget initially identified the source as USB music. - Later
WidgetMedia: onBtMusicInfologs prove Bluetooth music metadata can still update the card, but metadata display alone does not prove the button control path is using Bluetooth control.
- At
- Current conclusion: this retest log is consistent with either an old Launcher/Music package still running, or
isBtMusicControlSource()evaluating false when the click happens. The click reached Launcher, but did not reach the new explicit Bluetooth music control receiver. - Next validation/fix direction:
- On device, confirm the flashed
YX_Launcher.apkcontains thesendBtMusicControlcode andYX_Music.apkcontainsBluetoothMusicControlReceiver. - Add a click-time diagnostic log for
mCurrentMediaSourceandUtils.isBtMusicSource()if another code change is needed. - If source detection is false at click time, make
WidgetsMediaprefer the currently displayedBtMusicInfo/ latest card mode for play/prev/next routing, not onlySOURCE_ID.
- On device, confirm the flashed
2026-07-03 YX_BluetoothSDK Bluetooth music auto-pause after boot
- User asked to re-analyze log
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_28_17/yx_log/2026_0703_1127_33_000.logafter revertingYX_LauncherWidgetsMediaand theYX_Musicexplicit receiver changes. - Key evidence:
- This log does not contain
WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE, so it does not prove the Launcher play button click enteredWidgetsMediain this capture. - At
07-03 11:27:30.608, Bluetooth A2DP reportsStream started. - At
07-03 11:27:30.618/30.619,BluetoothMediaBrowserServicereportsPlaybackState state=3, meaning Bluetooth music did enter playing state. - At
07-03 11:27:30.643,BtMusicManager: onPlaybackStateChanged isPlaying = true. - Immediately after,
BtMusicAudioManager: setA2dpMute,informAudioFocusStateNative:0,BtMusicManager: onPlaybackStateChanged pauseBtMusic>,BtMusicManager: pauseBtMusic, andBluetoothAvrcpClient: pauseappear.
- This log does not contain
- Root cause inference from current code:
BtMusicManager.onPlaybackStateChanged()contains protection logic: if Bluetooth music starts while!isForeground()and!mBtMusicAudioManager.isFocus(), it mutes A2DP and callspauseBtMusic().- During Launcher main-page startup, Bluetooth music fragment is not foreground and Bluetooth music audio focus is not held, so when phone-side/A2DP playback starts, the SDK treats it as unauthorized background playback and actively pauses it.
- Current conclusion: this log points to
YX_BluetoothSDKforeground/focus guard actively stopping Bluetooth music, not to AVRCP/A2DP inability to play. The system can play briefly, then the SDK pauses it because the Bluetooth music page is not foreground and focus is false. - Next fix direction: for Launcher card play-after-boot, ensure the Bluetooth music control path first marks/request-focus for Bluetooth music or create a controlled exception in
BtMusicManagerfor a user-initiated Launcher card play request; avoid relying only on raw global media key when the Bluetooth music page is not foreground.
2026-07-03 YX_Launcher Bluetooth music widget latest 11:33 log analysis
- User provided latest log:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_34_00/yx_log/2026_0703_1133_34_699.log
- Current code state after revert:
YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.javaplay/previous/next buttons only callYXUtils.sendKey(...).- No
sendBtMusicControl, no explicitYX_Musicreceiver, and no current-source based Bluetooth control path exists in the reverted code.
- Key evidence:
- At
07-03 11:33:27.386,BtMusicManagerreceives Bluetooth metadata: artist王菲, title如愿 (电影《我和我的父辈》主题推广曲). - At
07-03 11:33:27.429, Bluetooth playback state is stopped:BtMusicManager: onPlaybackStateChanged isPlaying = false. - At
07-03 11:33:27.735, Launcher receivesWidgetMedia: onBtMusicInfo, so the card can display Bluetooth music information. - At
07-03 11:33:42.946and07-03 11:33:48.389, the Launcher card play button click reachesWidgetsMedia:KEYCODE_MEDIA_PLAY_PAUSE. - At
07-03 11:33:49.325, previous button click reachesWidgetsMedia:KEYCODE_MEDIA_PREVIOUS. - Immediately after these clicks, logs only show
NuPlayerDriver/MediaPlayer/bus7_system_sound_outshort system-audio playback and stop/reset. - The log contains no
BtMusicControlManager: playBtMusic, noBtMusicManager: playBtMusic, noBluetoothAvrcpClient: play, nobluetooth music requestAudioFocus, and nosetA2dpUnMuteafter the Launcher button clicks.
- At
- Current conclusion:
- This log proves the Launcher button click is received, but the click is not converted into a Bluetooth music play command.
- Metadata display is working through
BtInfoPushManager -> WidgetMedia, but control still depends on global media keys. - In the boot/main-Launcher scene, Bluetooth music is not foreground and does not hold focus, so the Bluetooth SDK media session is not ready to receive/respond to
KEYCODE_MEDIA_PLAY_PAUSE. - Combined with the 11:27 log, the failure is a two-part issue: Launcher sends only global media keys, and
BtMusicManagerblocks or pauses Bluetooth playback when it starts without foreground/focus.
- Next fix direction:
- Do not rely on raw global media keys for Launcher Bluetooth card controls.
- Add a controlled Bluetooth-music play entry that first requests/marks Bluetooth music focus, then calls
playBtMusic(), or explicitly route Launcher card play/previous/next to a Bluetooth control API when the card is showing Bluetooth music. - Keep the
BtMusicManagerbackground-play guard intact unless there is a verified user-initiated Launcher play flag/focus path.
2026-07-03 YX_BluetoothSDK media-key fallback for Launcher BT widget
- User clarified: do not modify
YX_Launcher. - Fix scope:
YX_Launcherremains unchanged;WidgetsMediastill sendsKEYCODE_MEDIA_PLAY_PAUSE/ previous / next.- Change is limited to
YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/audio/BtMusicAudioManager.java.
- Fix:
BtMusicAudioManagernow registers its Bluetooth musicMediaSessionduring initialization instead of only after Bluetooth music obtains audio focus.- The session pre-handles media keys and only takes over when Bluetooth music already has focus, current
SOURCE_IDisSOURCE_BT_MUSIC, or boot-timeSOURCE_IDisSOURCE_NULLwhile validBT_MUSIC_INFOexists. - When it takes over a play/previous/next key without focus, it requests Bluetooth music audio focus first, then calls
playBtMusic()/preBtMusic()/nextBtMusic(). releaseAudioFocus()no longer releases the MediaSession; it downgrades the session after focus loss so boot-time Launcher media keys can still reach Bluetooth music later.
- Verification:
.\gradlew :YX_BluetoothSDK:assembleDebug --console plaincompleted successfully.- Confirmed no diff in
YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java.
- Retest log expectation:
- After clicking Launcher card play, logs should show
BtMusicAudioManager: onMediaButtonEvent pre-handle,bluetooth music requestAudioFocus,setA2dpUnMute,BtMusicManager: playBtMusic, andBluetoothAvrcpClient: play. - The previous failure pattern should disappear: click should no longer only show
NuPlayerDriver/MediaPlayershort system sound with no Bluetooth play command.
- After clicking Launcher card play, logs should show
2026-07-03 YX_BluetoothSDK first-click media-key routing follow-up
- User provided retest log:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_13_50_05/yx_log/2026_0703_1348_33_113.log
- Key evidence:
- At
07-03 13:48:28.358, Launcher already received Bluetooth music info:WidgetMedia: onBtMusicInfo ... mPlaySts = 0. - First click around
07-03 13:48:51.498only showsMcuService: WindowManagerGlobal = ACTION_DOWNand system click-audio logs. There is noMediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE, noBtMusicAudioManager: onMediaButtonEvent, and noBtMusicManager: playBtMusic. - After the first click, Android reports
Media button session is changed to com.yaxon.music/Music. - Second click around
07-03 13:48:54.527is routed correctly:MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music, thenBtMusicAudioManager: onMediaButtonEvent,bluetooth music requestAudioFocus: result = 1,setA2dpUnMute,BtMusicManager: playBtMusic, andBluetoothAvrcpClient: play.
- At
- Updated conclusion:
- The remaining symptom is not AVRCP play failure. The second click proves the Bluetooth SDK play path works.
- The first click fails because the Bluetooth music
MediaSessionbecomes the active media-button session only after the first click/system playback activity, so the first media key is not delivered to Bluetooth music.
- Fix update:
- Keep
YX_LauncherandYX_Musicunchanged. - In
BtMusicAudioManager, allowFLAG_EXCLUSIVE_GLOBAL_PRIORITYwhen Bluetooth music is not focused but is a safe inactive media-button candidate: A2DP connected and eitherSOURCE_ID == SOURCE_BT_MUSIC, orSOURCE_ID == SOURCE_NULLwith valid Bluetooth music info. - In
BtMusicManager, refresh the Bluetooth music MediaSession after A2DP connect, playback-state updates, and metadata updates so the session is ready before the first Launcher card click.
- Keep
- Verification:
.\gradlew :YX_BluetoothSDK:assembleDebug --console plaincompleted successfully.- Confirmed no diff in
YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java. - Confirmed no diff under
YX_Music.
- Next retest expectation:
- The first Launcher card play click should immediately show
MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Musicfollowed byBtMusicAudioManager: onMediaButtonEvent pre-handle,bluetooth music requestAudioFocus,setA2dpUnMute,BtMusicManager: playBtMusic, andBluetoothAvrcpClient: play.
- The first Launcher card play click should immediately show
2026-07-03 YX_BluetoothSDK 14:54 log coverage check
- User provided log:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_14_54_53/yx_log/2026_0703_1454_33_435.log
- Coverage result:
- The log has only about 5720 lines and mainly covers early boot around
01-01 08:00:01to01-01 08:00:10plus Android startup timestamps around06-30 11:09:15to06-30 11:09:16. - It does not contain
WidgetMedia,KEYCODE_MEDIA_PLAY_PAUSE,BtMusicAudioManager,BtMusicManager,BluetoothAvrcpClient,MediaSessionService: Sending KeyEvent, or07-03 14:54runtime events. - The same directory only contains an empty zipped log entry
2026_0703_1454_05_407.log.
- The log has only about 5720 lines and mainly covers early boot around
- Conclusion:
- This capture does not cover the Launcher music-card click or Bluetooth music playback path, so it cannot validate whether the first-click fix succeeded or failed.
- Next log requirement:
- Need a log segment that includes the actual click time and contains at least Launcher
WidgetMediaor systemMediaSessionServicemedia-key lines.
- Need a log segment that includes the actual click time and contains at least Launcher
2026-07-03 YX_BluetoothSDK 15:02 retest validates first-click fix
- User provided log:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_15_03_26/yx_log/2026_0703_1502_33_334.log
- Key evidence:
- At
07-03 15:02:28.813, Bluetooth metadata arrived:BtMusicManager: artist = 伍佰 title = 世界第一等(Live). - At
07-03 15:02:28.878, before any Launcher click, Android changed the global priority session tocom.yaxon.music/Music. - At
07-03 15:02:29.311, Launcher received Bluetooth info and displayed the card:WidgetMedia: onBtMusicInfo ... mPlaySts = 0. - At
07-03 15:02:49.194, the first Launcher card play click reachedWidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE. - The same first click was routed immediately to Bluetooth music:
MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music. BtMusicAudioManagerreceived the media button whileisFocus=false, requested focus, unmuted A2DP, then calledBtMusicManager: playBtMusicandBluetoothAvrcpClient: play.- At
07-03 15:02:49.633and continuously afterward,BtMusicManager: onPlaybackStateChanged isPlaying = trueappears, with no subsequentpauseBtMusicorBluetoothAvrcpClient: pause.
- At
- Conclusion:
- The SDK fix is effective for this retest. First Launcher card click now starts Bluetooth music directly; the previous "first click only arms the session, second click plays" pattern is gone.
2026-07-03 YX_BluetoothSDK 15:04 retest also validates first-click fix
- User provided log:
C:/Users/Administrator/Desktop/徐工日志/2026_07_03_15_05_45/yx_log/2026_0703_1504_32_996.log
- Key evidence:
- At
07-03 15:04:27.802, Bluetooth metadata arrived:BtMusicManager: artist = 任贤齐 title = 伤心太平洋 (电视剧《神雕侠侣》片尾曲). - At
07-03 15:04:27.835, before the click, global priority session changed tocom.yaxon.music/Music. - At
07-03 15:04:28.211, Launcher card displayed Bluetooth music info withmPlaySts = 0. - At
07-03 15:04:50.143, first Launcher play click loggedWidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE. - The first click was immediately routed to Bluetooth music:
MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music. BtMusicAudioManagerreceived both down/up events whileisFocus=false, requested focus, unmuted A2DP, calledBtMusicManager: playBtMusic, andBluetoothAvrcpClient: play.- At
07-03 15:04:50.599and afterward, playback state remainedisPlaying = true, with nopauseBtMusicor AVRCP pause afterward.
- At
- Conclusion:
- This second retest confirms the fix is stable for repeated boot/Launcher-card first-click scenarios.
2026-07-03 YX_BluetoothSDK BT music restore after power off
- New requirement: if Bluetooth music is playing and power-off interrupts it, next power-on should restore Bluetooth music playback.
- Scope remains limited to
YX_BluetoothSDK;YX_LauncherandYX_Musicare not modified. - Fix:
- Added SDK private persisted flags
bt_music_had_focusandbt_music_was_playing. BtMusicAudioManagerpersists Bluetooth focus ownership after successful focus request.BtMusicManagerpersists Bluetooth playback state only when Bluetooth music is the explicitSOURCE_IDor has focus.- Added
ACC_STATEandPOWER_MODEobservers.POWER_MODE=0/3snapshots power-off state;POWER_MODE=1andACC_STATE=ONtrigger restore checks. - Restore requires all conditions: persisted had-focus, persisted was-playing, explicit
SOURCE_ID == SOURCE_BT_MUSIC, ACC/IGN on, A2DP connected, and currently not playing. - Clear restore state only during normal ACC-on source/focus loss paths, not during
POWER_MODE=0/3/4power-down phases.
- Added SDK private persisted flags
- Risk control:
- Auto-restore does not use the Launcher default Bluetooth-source fallback. Missing
SOURCE_IDusesSOURCE_NULLand will not auto-play. - USB music and radio are protected because restore requires explicit Bluetooth source.
- Manual pause clears
bt_music_was_playingwhile powered on and focused.
- Auto-restore does not use the Launcher default Bluetooth-source fallback. Missing
- Verification:
.\gradlew :YX_BluetoothSDK:assembleDebug --console plaincompleted successfully.- Confirmed
git diff -- YX_Launcher YX_Musicis empty.
2026-07-07 YX_BluetoothSDK pause Bluetooth music during TXZ TTS
- User confirmed no backup before editing.
- Log analyzed:
C:/Users/Administrator/Desktop/徐工日志/2026_07_07_19_07_57/yx_log/2026_0707_1849_48_792.log
- Finding:
- TXZ TTS requests
Synthesisaudio focus withfocusGain=2. - Bluetooth music receives
AUDIOFOCUS_LOSS_TRANSIENT, but previousBtMusicAudioManagerhandling only calledsetA2dpMute(), so the phone-side Bluetooth playback could continue progressing. - Radio and USB music handle transient focus loss as a real paused playback state, which explains the different behavior.
- TXZ TTS requests
- Fix:
- In
BtMusicAudioManager,AUDIOFOCUS_LOSS_TRANSIENTnow records whether Bluetooth music was playing, callssetA2dpMute(), and callspauseBtMusic()when it was playing. - Existing
AUDIOFOCUS_GAINrecovery logic remains responsible for auto-resuming only whenisPlayingLossFocusis true and the user did not pause manually.
- In
- Verification:
.\gradlew :YX_BluetoothSDK:assembleDebugcompleted successfully.