Skip to content

feat(reminder): add local TTS for high-strength alarms - #363

Merged
LUPENGHAN merged 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:pr3-local-tts
Aug 24, 2026
Merged

feat(reminder): add local TTS for high-strength alarms#363
LUPENGHAN merged 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:pr3-local-tts

Conversation

@LUPENGHAN

@LUPENGHAN LUPENGHAN commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • 为高强度提醒新增本地端上语音播报(TTS),只在 reminder_strength === 'high' 时触发(AlarmSoundTierfullspeech_text 非空),叠加在原有的三档铃声之上。
  • composeReminderSpeech()/strengthDelivery.ts 拼出播报文案("标题,该出发了,现在是 HH:MM",全天日程走按日期的另一套文案),沿 AlarmSchedulerPort -> NativeAlarmScheduler -> TimeflowAlarmBridge -> AlarmModule -> AlarmScheduler/AlarmReceiver/AlarmSoundService/RingActivity 这条链路传下去。
  • AlarmTtsEngine.kt:一个共享的 TextToSpeech 单例,在 AlarmModule 构造时(App 启动、几乎总在前台)就主动绑定,不放在 AlarmSoundService.onCreate()(常在后台/受限状态)里懒绑定——降低被 Android 后台进程限制卡住绑定的风险。
  • AndroidManifest.xml 加了 android.intent.action.TTS_SERVICE<queries> 声明:不加的话,targetSdk 30+ 的包可见性限制会让 PackageManager 看不到已安装的 TTS 引擎,TextToSpeech 初始化会报 status=ERROR,即便这个引擎在系统设置里确实装着、能用。
  • 删掉了上游原本"每条提醒都念"的 ReminderSpeechFormatter.java/reminderSpeech.ts,以及因此变得完全冗余的 plugins/withTimeflowAlarm.js(它声明的权限已经被模块自己的 AndroidManifest.xml 覆盖)。
  • 顺手清掉了 AlarmSoundService.javaLocalReminderApplication.ts 里残留的 TTS 诊断日志。
  • 测试修正:presentNow() 现在会往原生桥多传一个 speech_text 参数,把 fix(reminder): 三档铃声 + 原生时间触发 + 后台守护补挂 #355 引入的两条 presentNow 断言同步更新。

Test plan

  • 本分支已 rebase 到最新 mainnpm run check(lint、format、typecheck、jest + vitest,共 703 条测试)本地全部通过。

🤖 Generated with Claude Code

LUPENGHAN and others added 3 commits August 24, 2026 19:03
Adds on-device text-to-speech for high-strength reminders, replacing
the discarded upstream design (which spoke every alarm with sound
enabled) with a strength-gated one layered on top of the existing
tiered ring sound: only reminder_strength === 'high' triggers speech,
via AlarmSoundTier 'full' + a non-empty speech_text.

- composeReminderSpeech()/strengthDelivery.ts builds the spoken text
  as "title, time to go, it's now HH:MM" (or a date-based line for
  all-day schedules), threaded through AlarmSchedulerPort ->
  NativeAlarmScheduler -> TimeflowAlarmBridge -> AlarmModule ->
  AlarmScheduler/AlarmReceiver/AlarmSoundService/RingActivity.
- AlarmTtsEngine.kt: a shared TextToSpeech singleton, eagerly bound at
  AlarmModule construction time (app-launch, almost always foreground)
  rather than lazily inside AlarmSoundService.onCreate() (often
  background/restricted) -- reduces the risk of an Android background
  process restriction blocking the engine bind.
- AndroidManifest.xml <queries> entry for
  android.intent.action.TTS_SERVICE: without it, targetSdk 30+ package
  visibility hides installed TTS engines from PackageManager and
  TextToSpeech init fails with status=ERROR even when an engine is
  genuinely installed and works from system Settings.
- Drops upstream's ReminderSpeechFormatter.java/reminderSpeech.ts
  (the "speak every alarm" formatter) and the now-fully-redundant
  plugins/withTimeflowAlarm.js, whose permission declarations were
  already covered by the module's own AndroidManifest.xml.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ertions

PR1's presentNow tests (now on main via 1024XEngineer#355) assert 6 native args;
this branch's TTS commit added a 7th speech_text param to
presentNow's native call. Update the two assertions to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.56098% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...d/src/features/reminder/domain/strengthDelivery.ts 96.77% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已检查固定提交 80b3ec8891d0ad6b16de6d517568b00c5f0d438f...ff98a13f26f495a9da00af1e4fb96988b099c74f 的完整提醒送达链路,重点覆盖时间/地点提醒、强度门控、snooze 持久化、Android Intent 传递与 TTS 失败回退。以下两处会造成用户可见的提醒行为回归。

验证:git diff --check 通过;仓库依赖中的 jest/tsc 可执行文件没有执行权限,因此无法在当前工作区运行聚焦测试或类型检查。

Comment thread frontend/src/features/reminder/domain/strengthDelivery.ts Outdated
LUPENGHAN and others added 2 commits August 24, 2026 19:44
…nous TTS enqueue failure

Fixes two P1 findings from fennoai's review of 1024XEngineer#363:

- composeReminderSpeech() formatted schedule.start_time unconditionally,
  but the alarm actually fires at resolveEffectiveTriggerAt(schedule) --
  earlier than start_time for before_start reminders, and snoozed_until
  once snoozed. A 15-minutes-before reminder announced the event's
  start time instead of the moment it actually rang, and a snoozed
  reminder kept repeating the original stale time.

- AlarmTtsEngine.speak() returns TextToSpeech.ERROR when the engine
  can't enqueue speech synchronously, but speakCurrent() ignored the
  return value. No utterance callback fires in that case, so onError()
  is never reached, and by then the bundled-audio fallback has already
  been stopped -- the high-strength alarm goes silent. Check the
  return value and route into the same onSpeechError() fallback used
  by the async error path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6ba1979 already removed two of these "定位问题排查完可以删" markers but
missed four more with the same marker, still firing unconditionally:

- reminderGuardTask.ts: [guard] tick fired on every single guard task
  wakeup (as often as every 15s in the densest polling tier), and
  [guard] geofence eval fired on every headless location evaluation.
- LocalReminderApplication.ts: the foreground-session equivalents,
  applyLocationSample and geofence eval, fired on every location
  callback.

Also drops distanceMeters/center, which only existed to feed the
deleted logs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@LUPENGHAN
LUPENGHAN merged commit a2a2ab5 into 1024XEngineer:main Aug 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants