This repository is a multi-module Android project defined in `settings.gradle`. App modules such as `YX_Voice`, `YX_Radio`, `YX_Factory`, and `YX_SystemUI` follow the standard `src/main`, `src/test`, and `src/androidTest` layout. Shared code lives in SDK and base modules like `YX_BluetoothSDK`, `YX_AutoMotiveSDK`, `YX_Lib_CommonBase`, and `YX_Lib_UIStyleBase`. Prebuilt platform jars are stored in `sdklibs/`, generated APKs are copied into `third_apps/<Module>/`, and root scripts such as `build_apk.sh` handle CI-oriented packaging.
## Build, Test, and Development Commands
Use the Gradle wrapper from the repository root.
-`.\gradlew clean` clears root and module build outputs.
-`.\gradlew :YX_Voice:assembleDebug` builds one app module for local iteration.
-`.\gradlew :YX_BluetoothSDK:testDebugUnitTest` runs one module's JVM tests.
-`.\gradlew :YX_Factory:connectedDebugAndroidTest` runs instrumentation tests on a connected device.
-`bash build_apk.sh debug` or `bash build_apk.sh release` performs the multi-APK packaging flow used for `third_apps/`.
Prefer module-scoped commands over full-repo builds because the workspace is large.
## Coding Style & Naming Conventions
Follow existing Android Java and Gradle conventions: 4-space indentation, braces on the same line, `UpperCamelCase` for classes, `lowerCamelCase` for methods and fields, and `SCREAMING_SNAKE_CASE` for constants. Keep package names under `com.yaxon...` or the existing module namespace. Name resources by type and feature, such as `activity_back_car.xml`, `ic_launcher.png`, or `radio_widget_info.xml`.
## Testing Guidelines
Unit tests live under `src/test/java`; device tests live under `src/androidTest/java`. Existing test classes use the `ExampleUnitTest` and `ExampleInstrumentedTest` pattern, but new tests should use feature-specific names such as `BluetoothPbapClientTest`. Add or update tests for module behavior changes when practical, especially in shared SDK modules.
## Commit & Pull Request Guidelines
Recent history uses scoped subjects like `feat(YX_SystemUI): ...`, `fix(YX_Radio): ...`, and `modify(YX_Voice): ...`. Keep that format: `<type>(<module>): <summary>`. Pull requests should state the affected modules, user-visible behavior changes, test coverage or device validation performed, and screenshots when UI layouts or drawables change.
## Security & Configuration Tips
Do not commit real secrets, device-specific `local.properties`, or modified signing material. `config.gradle` currently references shared signing and SDK paths, so treat local environment changes as workstation-only unless the team explicitly standardizes them.
- Continue voice `playDislike` fix without backup.
- Further narrowed `playDislike` to one IPC command from `YX_Voice` to the target media module instead of sending cancel-favorite and next as two independent actions.
-`VoiceEventHelper.sendMusicDislike(...)` now sends `cmd.playDislike` and carries current media type with `typeKey`.
-`VoiceProtocol` keeps video behavior as next video; radio and music now dispatch only `sendMusicDislike(...)`.
-`YX_MusicSDK` handles `cmd.playDislike` in `MusicService`: 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_Radio` handles `cmd.playDislike` in `VoiceRadioManager`: cancel current station only when already collected, then call `nextAutoSearch()`.
- 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, and `CustomTriStateSwitch` accessibility node state.
- Verified no remaining `可见测试`, `visible speak test`, `visible speak simple`, `btn_visible_speak_simple_test`, or `maintenance_scroll_view` test markers under `YX_CarControl/src/main`.
- Follow-up: removed the shared `CustomTriStateSwitch` accessibility-node change to keep this submission scoped to the voice settings page only; remaining `YX_CarControl` diff is limited to `VoiceFragment.java`, `fragment_voice.xml`, and `strings.xml`.
- Verification after removal: `.\gradlew :YX_CarControl:assembleDebug` completed successfully.
- Follow-up: kept multi-keyword visible-speak text with `#`, but changed all added `fragment_voice.xml``contentDescription` values from hard-coded text to `@string/...` references for maintainability.
- Verification after XML string-reference change: `.\gradlew :YX_CarControl:assembleDebug` completed successfully.
- Follow-up: added matching `values-en/strings.xml` translations for all new `voice_speak_*` resources while preserving `#` multi-keyword format.
- Verification after English resource completion: `.\gradlew :YX_CarControl:assembleDebug` completed successfully.
- 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 via `contentDescription`.
- Scope:
-`EnergyFragment.java`: dynamically updates charge/discharge switch `contentDescription` according to current checked state, so closed state exposes open keywords and opened state exposes close keywords.
-`fragment_energy.xml`: added `contentDescription` for charge allow, discharge allow, reset trip after last charge, more trips, and reset statistics controls.
-`dialog_trip_record.xml`: added `contentDescription` for subtotal trip A/B reset buttons.
-`values/strings.xml` and `values-en/strings.xml`: added matching `energy_speak_*` multi-keyword strings using `#`.
- 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:assembleDebug` completed successfully.
- Followed reference commit `ef20ec3ca823aece8d1beb8e45442cdab7ba420f` and reused the native page scanning approach.
- Added `contentDescription` visible-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 in `values/strings.xml` and `values-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.
- 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:assembleDebug` completed successfully.
## 2026-06-24 YX_Voice background TTS toast
- User confirmed no backup before editing.
- Requirement: both `playTtsOnUi()` and `playTtsInBackground()` are in use, but foreground TTS remains only a reserved API; actual playback must continue through `syncBackgroundTtsText`.
- Added `VoiceToastHelper` in `YX_Voice` using system `Toast` with custom view, based on the existing `YX_CarControl` designed Toast style instead of using a Dialog-style helper.
- Refactored `VoiceEventHelper` so `playTtsOnUi()` and `playTtsInBackground()` both call `sendBackgroundTtsWithToast(...)`; this shows the designed Toast and sends TXZ `syncBackgroundTtsText`.
- Kept foreground TTS command disabled; `playTtsOnUi()` comment now explicitly states foreground TTS is not supported and the reserved entry still uses background TTS.
- 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 use `shadow.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 switch `contentDescription` by current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `sound_speak_*` resources under `可见即可说 - 声音页面`.
- Known validation boundary: the volume bars are custom `ItemCustomSeekBarView` controls; 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.
- 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, so `sendForegroundTtsPlay("请手握方向盘")` 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 enters `0x02` from a non-warning state, with a 5-second debounce against rapid signal chatter.
- Expected validation: repeated `sendAdasLdsHandsOffWarnStChange sts = 2` logs may still appear, but repeated TTS requests should be replaced by `sendAdasLdsHandsOffWarnStChange skip duplicate tts`; `sendForegroundTtsPlay tts = 请手握方向盘`, `playTtsOnUi`, and TXZ `syncBackgroundTtsText` should no longer repeat at millisecond level.
- Follow-up after reboot log `2026_0624_1612_11_547.log`: ADAS warning status still bounced between `1` and `2`; 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=2` still plays immediately, but the warning is not considered cleared until `sts != 2` remains stable for 2 seconds; only after that can a later `sts=2` play again.
- Verification after stable-clear update: `.\gradlew :YX_CarControl:assembleDebug` completed successfully.
- 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 use `shadow.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 mode `contentDescription` according to current selected/checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `show_speak_*` resources under `可见即可说 - 显示页面`.
- Brightness bars are custom `ItemCustomSeekBarView` controls and were intentionally not added per the latest user decision to skip sliders for now.
## 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.log` showed TXZ accessibility was connected and Sound page keywords were scanned into `VisualCommandModule offline`, but short utterances such as `古典`, `爵士`, and `主驾` returned `TxzVisualCommand matchedEntry = null`.
- Root cause: Sound page `contentDescription` entries 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 in `values-en`.
- Follow-up from log `C:\Users\Administrator\Desktop\徐工日志\9_45_17\yx_log\2026_0622_1451_07_056.log`: utterance `自定义` still returned `TxzVisualCommand matchedEntry = null` because 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.
- 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 use `shadow.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 switch `contentDescription` according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `connect_speak_*` resources under `可见即可说 - 连接页面`.
- 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 use `shadow.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 switch `contentDescription` according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `smart_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.
- 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 use `shadow.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 switch `contentDescription` according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `light_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.
- 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 use `shadow.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 switch `contentDescription` according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
-`values/strings.xml` and `values-en/strings.xml`: added `car_speak_*` resources under visible-speak resource groups.
- 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 use `shadow.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 switch `contentDescription` according 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.xml` and `values-en/strings.xml`: added `permission_speak_*` resources under visible-speak resource groups.
- Existing authorization duration, Settings.System persistence, permission confirmation, and switch enable/disable business logic were kept unchanged.
- 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()` and `playTtsInBackground()` still send real audio through TXZ `syncBackgroundTtsText`.
- Added `VoiceDrowsyReminderHelper` in `YX_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.
- Added `SYSTEM_ALERT_WINDOW` permission and released the overlay from `VoiceService.onDestroy()` to avoid stale windows during service restart.
- User requested voice command `remindLaterOrNever` for the fatigue reminder dialog buttons `稍后提醒` and `不再提醒`.
- Added TXZ protocol constant `CMD_REMIND_LATER_OR_NEVER = "remindLaterOrNever"` in `YX_Voice`.
-`VoiceProtocol` now consumes `remindLaterOrNever` locally and calls `VoiceDrowsyReminderHelper` directly, 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:
- Added handling for the already registered `com.voice.test.tts.yb` test broadcast in `YX_Voice``TestReceiver`.
- If the broadcast has no `tts` extra, it defaults to `VoiceNewConstant.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 same `playTtsOnUi()` path.
- Matched the drowsy reminder dialog day/night colors to the `YX_CarControl` system 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.
- Follow-up: moved the drowsy dialog title to `strings.xml`, and moved fixed `remindLaterOrNever` protocol attr values `稍后提醒` / `不再提醒` to `VoiceNewProtocolConst`.
- Verification after constant/resource cleanup: `.\gradlew :YX_Voice:assembleDebug` completed successfully.
- Requirement: the voice reminder dialog and TTS toast must follow the current day/night theme, referencing the vehicle settings theme behavior.
- Added `VoiceThemeHelper` in `YX_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`.
- Updated `VoiceDialogHelper` and `VoiceToastHelper` to inflate their custom views with the themed context, so `values/colors.xml` and `values-night/colors.xml` are 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.log` was analyzed.
- Key timeline from the log:
-`16:45:28.982` voice received the smoking reckless driving TTS and scheduled the safety dialog.
-`16:45:30.044``VoiceDialogHelper: showInternal: drowsy reminder shown`, so the overlay view was already added to `WindowManager`.
-`16:45:34.464``VoiceEventHelper: themeMode 1` and `syncScreenMode status = darkMode`, meaning the theme switch happened several seconds after the overlay was created.
- Root cause: a custom `WindowManager` overlay does not automatically reinflate or re-resolve `values-night` resources 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.
-`SettingCommonObserver` now calls the refresh hook on `THEME_MODE_CHANGED` and also listens to `Settings.Global.REQUEST_THEME_MODE`.
-`VoiceThemeHelper` now prefers the current `Settings.System[THEME_MODE_CHANGED]` value for manual light/dark mode; auto mode still uses `REQUEST_THEME_MODE` / `POST_LIGHT_STATUS` fallback.
## 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.log` was analyzed.
- Key timeline:
-`17:06:15.791` / `17:06:17.489``VoiceEventHelper: playTtsOnUi` received HVAC CO2 TTS text and showed toast.
-`17:06:18.900` user clicked dark mode switch, then `17:06:18.917` voice synced `lightMode`.
- 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:
-`VoiceToastHelper` now keeps the last visible toast text and visibility window.
- On `THEME_MODE_CHANGED` / `REQUEST_THEME_MODE` observer callback, it delays briefly, cancels the old toast, and re-shows the same text with the current themed context.
- Added `VoiceToastHelper: refreshForThemeChangedInternal` log for validation.
- 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:
-`ItemCustomSeekBarView` is shared by Sound/Light/Display pages and normally displays an icon.
-`CustomLightSelector` is 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 `FatigueReminderIntervalSeekBarView` is used only by the new Smart Drive fatigue reminder interval layout.
- Follow-up: the new fatigue reminder switch/interval still uses local keys in `SmartDriveFragment`, but reads/writes through `DataManager` + `DataUtils.STATE_URI` and now registers `ContentObserver` callbacks, matching the existing data-center sync style. `YX_CarControl` compiles against `sdklibs/yx_support_classes.jar`, so adding constants only in `YX_Support` source 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:assembleDebug` completed successfully.
- Update: per user request, moved the fatigue reminder keys to `YX_Support``DataUtils`: `FATIGUE_REMINDER_SWITCH` and `FATIGUE_REMINDER_INTERVAL_MINUTE`. The interval value is stored as an integer minute value, default 20.
- Data center parity with `FATIGUE_MONITOR_SWITCH`: added `YX_DataCenterService` initialization in `YXContentProvider.mHasState`, update dispatch in `YXContentProvider.update()`, new `MessageEvent` IDs 2097/2098, and `ProviderData` SharedPreferences `putInt/getInt` persistence for the reminder switch and integer interval.
- Build note: both `YX_CarControl` and `YX_DataCenterService` compile against `sdklibs/yx_support_classes.jar`, so `YX_Support:makeJar` or the existing support-jar replacement flow is needed before compiling these consumers with the new `DataUtils` constants.
## 2026-06-28 YX_Voice fatigue reminder interval
- User confirmed `YX_Voice` must associate the SmartDrive fatigue reminder settings:
-`DataUtils.FATIGUE_REMINDER_SWITCH`
-`DataUtils.FATIGUE_REMINDER_INTERVAL_MINUTE`
-`DROWSY_DRIVING`, `DISTRACTED_DRIVING`, `SMOKING_RECKLESS_DRIVING`, and `CALLING_RECKLESS_DRIVING` reminder dialogs are now gated by the reminder switch and throttled by the interval minute value.
-`VoiceDialogHelper` reads the DataCenter switch before showing; when enabled, it stores the last shown time and skips repeated dialogs until the configured interval expires. Interval `0` means no throttling.
- Update: user clarified `不再提醒` and `稍后提醒` must not persist locally and must recover after reboot. `VoiceDialogHelper` now keeps both the last shown time and the current-process "never remind" flag only in static memory; it does not write SharedPreferences or change `FATIGUE_REMINDER_SWITCH` for 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`, and `CALLING_RECKLESS_DRIVING`; one type's button action or interval throttle does not suppress the other types.
-`SettingCommonObserver` and `BaseVoiceService` now observe the two fatigue reminder URIs; turning the switch off dismisses a pending/showing reminder overlay.
- Log `2026_0629_1152_07_466.log` showed Radio playback was active and TXZ received `syncFocusPlayStatus=true` plus `syncRadioPlayType=fm`, but no `syncMediaFocus: radio`.
- Root cause: `MediaObserver` only sent media focus on `SOURCE_ID` changes; Radio startup in the log updated `RADIO_INFO` playback state without a reliable `SOURCE_ID` change notification.
- Fix: when `RADIO_INFO` reports `mState == 2` and current `SOURCE_ID` is `YXUtils.SOURCE_RADIO`, `MediaObserver` now sends `notifySyncMediaFocus(VoiceNewProtocolConst.RADIO)` before syncing play status.
- Requirement: check whether Bluetooth still depends on legacy `ACC_STATE`; Xugong currently follows power mode rather than old ACC state.
- Finding: `YX_BlueTooth``BluetoothService` still registered `ACC_STATE`, but the old disconnect/reconnect logic in that branch had already been commented out and only logged `ACC state`.
- Fix: removed the unused `ACC_STATE_URI` constant, removed the `ACC_STATE` observer registration, and deleted the dead `case ACC_STATE_URI` branch.
- Kept the active `LOW_VOLTAGE_POWER_STATE` logic unchanged because it still performs Bluetooth phone/headset disconnect and phone reconnect behavior; only changed misleading logs/comments from `CAN ACC OFF/ON` to `LOW_VOLTAGE_POWER_STATE OFF/ON`.
- 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.xml` now uses `sound_speak_eq_user` instead of the literal `Triangle Button`.
-`sound_speak_close_mute` now also includes bare `jingyin` / `Mute`, so the same short utterance can match both on and off states.
- 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.
## 2026-07-02 YX_CarControl sound page visible-speak key tone
- User reported saying bare `按键音` on Sound page did not respond.
- Finding: `SoundFragment` dynamically sets the key-tone switch `contentDescription` based 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 both `sound_speak_open_key_tone` and `sound_speak_close_key_tone`; English resources added matching bare `Key tone`.
- Expected verification: on Sound page, say `按键音`; log should show the offline entry containing `按键音#...` and `TxzVisualCommand` matched entry for `按键音`.
## 2026-07-02 YX_CarControl SmartDrive fatigue visible-speak short keywords
- Root cause: SmartDrive page offline visible-speak list only exposed state-specific phrases such as `关闭疲劳监测提醒#关掉疲劳监测提醒`; the bare utterance `疲劳监测提醒` produced `matchedEntry = null`.
- Fix: added bare short keywords to both open and close resources for `smart_speak_*_fatigue_monitor` and `smart_speak_*_fatigue_reminder`.
## 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_Launcher` main page uses `WidgetsMedia`, 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_PAUSE` can 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, so `com.yaxon.music` could be treated as USB music even when `SOURCE_ID` was Bluetooth music.
-`SystemUtils.startBtMusic()` sends `is_click_bluetooth_music=true`, but `UsbMainActivity` only handled `VoiceControlManager.PAGE_TYPE`; the legacy Bluetooth-music extra was ignored.
- Fix:
-`WidgetsMedia` now resolves the initial card source from `SOURCE_ID` when focus package is `com.yaxon.music` or null.
- For Bluetooth music source, Launcher card play/prev/next sends explicit broadcasts to `YX_Music` instead of relying only on global media keys.
- Added `BluetoothMusicControlReceiver` in `YX_Music` to call `BtMusicControlManager` directly 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.
-`UsbMainActivity` now handles `is_click_bluetooth_music=true` in both initial launch and `onNewIntent()` and switches to the Bluetooth music page.
- At `07-03 11:02:37.547`, `WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE:` appears, proving the Launcher card play button click reached `WidgetsMedia`.
- No `WidgetMedia: sendBtMusicControl action = ...`, no `BluetoothMusicControlReceiver: onReceive action = ...`, no `BtMusicControlManager: playBtMusic`, and no `BluetoothAvrcpClient: play` appear in the same retest logs.
- Around the click, `NuPlayerDriver` / `MediaPlayer` logs 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.manager` and `initMedia:onMusicInfo`, so the Launcher widget initially identified the source as USB music.
- Later `WidgetMedia: onBtMusicInfo` logs prove Bluetooth music metadata can still update the card, but metadata display alone does not prove the button control path is using Bluetooth control.
- 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.apk` contains the `sendBtMusicControl` code and `YX_Music.apk` contains `BluetoothMusicControlReceiver`.
- Add a click-time diagnostic log for `mCurrentMediaSource` and `Utils.isBtMusicSource()` if another code change is needed.
- If source detection is false at click time, make `WidgetsMedia` prefer the currently displayed `BtMusicInfo` / latest card mode for play/prev/next routing, not only `SOURCE_ID`.
## 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.log` after reverting `YX_Launcher``WidgetsMedia` and the `YX_Music` explicit receiver changes.
- Key evidence:
- This log does not contain `WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE`, so it does not prove the Launcher play button click entered `WidgetsMedia` in this capture.
- At `07-03 11:27:30.608`, Bluetooth A2DP reports `Stream started`.
- At `07-03 11:27:30.618/30.619`, `BluetoothMediaBrowserService` reports `PlaybackState state=3`, meaning Bluetooth music did enter playing state.
- At `07-03 11:27:30.643`, `BtMusicManager: onPlaybackStateChanged isPlaying = true`.
-`BtMusicManager.onPlaybackStateChanged()` contains protection logic: if Bluetooth music starts while `!isForeground()` and `!mBtMusicAudioManager.isFocus()`, it mutes A2DP and calls `pauseBtMusic()`.
- 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_BluetoothSDK` foreground/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 `BtMusicManager` for 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
-`YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java` play/previous/next buttons only call `YXUtils.sendKey(...)`.
- No `sendBtMusicControl`, no explicit `YX_Music` receiver, and no current-source based Bluetooth control path exists in the reverted code.
- Key evidence:
- At `07-03 11:33:27.386`, `BtMusicManager` receives 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 receives `WidgetMedia: onBtMusicInfo`, so the card can display Bluetooth music information.
- At `07-03 11:33:42.946` and `07-03 11:33:48.389`, the Launcher card play button click reaches `WidgetsMedia`: `KEYCODE_MEDIA_PLAY_PAUSE`.
- At `07-03 11:33:49.325`, previous button click reaches `WidgetsMedia`: `KEYCODE_MEDIA_PREVIOUS`.
- Immediately after these clicks, logs only show `NuPlayerDriver` / `MediaPlayer` / `bus7_system_sound_out` short system-audio playback and stop/reset.
- The log contains no `BtMusicControlManager: playBtMusic`, no `BtMusicManager: playBtMusic`, no `BluetoothAvrcpClient: play`, no `bluetooth music requestAudioFocus`, and no `setA2dpUnMute` after the Launcher button clicks.
- 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 `BtMusicManager` blocks 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 `BtMusicManager` background-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
- Change is limited to `YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/audio/BtMusicAudioManager.java`.
- Fix:
-`BtMusicAudioManager` now registers its Bluetooth music `MediaSession` during 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_ID` is `SOURCE_BT_MUSIC`, or boot-time `SOURCE_ID` is `SOURCE_NULL` while valid `BT_MUSIC_INFO` exists.
- 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.
- 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`, and `BluetoothAvrcpClient: play`.
- The previous failure pattern should disappear: click should no longer only show `NuPlayerDriver` / `MediaPlayer` short system sound with no Bluetooth play command.
- 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.498` only shows `McuService: WindowManagerGlobal = ACTION_DOWN` and system click-audio logs. There is no `MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE`, no `BtMusicAudioManager: onMediaButtonEvent`, and no `BtMusicManager: 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.527` is routed correctly: `MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music`, then `BtMusicAudioManager: onMediaButtonEvent`, `bluetooth music requestAudioFocus: result = 1`, `setA2dpUnMute`, `BtMusicManager: playBtMusic`, and `BluetoothAvrcpClient: play`.
- 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 `MediaSession` becomes 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_Launcher` and `YX_Music` unchanged.
- In `BtMusicAudioManager`, allow `FLAG_EXCLUSIVE_GLOBAL_PRIORITY` when Bluetooth music is not focused but is a safe inactive media-button candidate: A2DP connected and either `SOURCE_ID == SOURCE_BT_MUSIC`, or `SOURCE_ID == SOURCE_NULL` with 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.
- 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/Music` followed by `BtMusicAudioManager: onMediaButtonEvent pre-handle`, `bluetooth music requestAudioFocus`, `setA2dpUnMute`, `BtMusicManager: playBtMusic`, and `BluetoothAvrcpClient: play`.
- The log has only about 5720 lines and mainly covers early boot around `01-01 08:00:01` to `01-01 08:00:10` plus Android startup timestamps around `06-30 11:09:15` to `06-30 11:09:16`.
- It does not contain `WidgetMedia`, `KEYCODE_MEDIA_PLAY_PAUSE`, `BtMusicAudioManager`, `BtMusicManager`, `BluetoothAvrcpClient`, `MediaSessionService: Sending KeyEvent`, or `07-03 14:54` runtime events.
- The same directory only contains an empty zipped log entry `2026_0703_1454_05_407.log`.
- 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 `WidgetMedia` or system `MediaSessionService` media-key lines.
- 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 to `com.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 reached `WidgetMedia: 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`.
-`BtMusicAudioManager` received the media button while `isFocus=false`, requested focus, unmuted A2DP, then called `BtMusicManager: playBtMusic` and `BluetoothAvrcpClient: play`.
- At `07-03 15:02:49.633` and continuously afterward, `BtMusicManager: onPlaybackStateChanged isPlaying = true` appears, with no subsequent `pauseBtMusic` or `BluetoothAvrcpClient: pause`.
- 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
- 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 to `com.yaxon.music/Music`.
- At `07-03 15:04:28.211`, Launcher card displayed Bluetooth music info with `mPlaySts = 0`.
- At `07-03 15:04:50.143`, first Launcher play click logged `WidgetMedia: 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`.
-`BtMusicAudioManager` received both down/up events while `isFocus=false`, requested focus, unmuted A2DP, called `BtMusicManager: playBtMusic`, and `BluetoothAvrcpClient: play`.
- At `07-03 15:04:50.599` and afterward, playback state remained `isPlaying = true`, with no `pauseBtMusic` or AVRCP pause afterward.
- 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_Launcher` and `YX_Music` are not modified.
- Fix:
- Added SDK private persisted flags `bt_music_had_focus` and `bt_music_was_playing`.
-`BtMusicAudioManager` persists Bluetooth focus ownership after successful focus request.
-`BtMusicManager` persists Bluetooth playback state only when Bluetooth music is the explicit `SOURCE_ID` or has focus.
- Added `ACC_STATE` and `POWER_MODE` observers. `POWER_MODE=0/3` snapshots power-off state; `POWER_MODE=1` and `ACC_STATE=ON` trigger 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/4` power-down phases.
- Risk control:
- Auto-restore does not use the Launcher default Bluetooth-source fallback. Missing `SOURCE_ID` uses `SOURCE_NULL` and will not auto-play.
- USB music and radio are protected because restore requires explicit Bluetooth source.
- Manual pause clears `bt_music_was_playing` while powered on and focused.
- TXZ TTS requests `Synthesis` audio focus with `focusGain=2`.
- Bluetooth music receives `AUDIOFOCUS_LOSS_TRANSIENT`, but previous `BtMusicAudioManager` handling only called `setA2dpMute()`, 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.
- Fix:
- In `BtMusicAudioManager`, `AUDIOFOCUS_LOSS_TRANSIENT` now records whether Bluetooth music was playing, calls `setA2dpMute()`, and calls `pauseBtMusic()` when it was playing.
- Existing `AUDIOFOCUS_GAIN` recovery logic remains responsible for auto-resuming only when `isPlayingLossFocus` is true and the user did not pause manually.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.