Skip to content

feat(reminder): add alarm scheduling, RN bridge, and ring activity/service - #265

Draft
LUPENGHAN wants to merge 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/reminder-native-core
Draft

feat(reminder): add alarm scheduling, RN bridge, and ring activity/service#265
LUPENGHAN wants to merge 5 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/reminder-native-core

Conversation

@LUPENGHAN

Copy link
Copy Markdown
Contributor

关联 Issue

Part of #263

依赖 #264(先合,这个 draft 里现在的 diff 会包含 #264 还没合的内容,等它合了 rebase 一下就会瘦身)

改动

  • AlarmScheduler(AlarmManager 调度 + SharedPreferences 持久化 + 重启/更新后重排)、AlarmReceiver/BootReceiver(广播入口)、AlarmModule/AlarmNativeBridge/AlarmPackage(RN 桥接层)、RingActivity/AlarmSoundService(全屏响铃,构建在 feat(reminder): add alarm ring UI toolkit and build scaffolding #264 的 AlarmRingUi 之上)
  • 这 8 个文件互相直接引用(比如 AlarmReceiver 构造 AlarmSoundService,RingActivity 反过来调 AlarmScheduler/AlarmNativeBridge),且是同一个 Gradle 模块(javac/kotlinc 整个 src/main/java 一起编译),拆不开——除非改用字符串类名/反射解耦,那样会引入运行时才能发现错误的风险,不划算

验证

  • npx tsc --noEmitnpx eslint . 全绿
  • 原生模块无法完整真机验证,如实说明

本轮不含

Part of 1024XEngineer#263.

The zero-dependency slice of the timeflow-alarm native module:
AlarmContract (shared constants), DayRulerView and AlarmRingUi (the
hand-drawn ring-screen UI -- colors, typefaces, entrance animation,
pill buttons, self-ticking clock). None of these three import anything
else in the module, so this compiles standalone; the scheduling/RN
bridge/ring Activity+Service classes that DO reference each other are
a separate PR on top of this one.

Also the app-level scaffolding: app.config.js (replaces static
app.json so plugins can read env vars), the withTimeflowAlarm config
plugin, and the new package dependency.

Not included: Baidu location module -- dropped entirely per 1024XEngineer#263's
decision to use system geofencing instead.
@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timeflow Ready Ready Preview Aug 17, 2026 7:25am

react-native.config.js still registered the deleted
modules/timeflow-baidu-location for autolinking, package-lock.json
still had an extraneous entry for it, and CI still exported a
TIMEFLOW_BAIDU_LOCATION_API_KEY placeholder for a plugin that's no
longer registered in app.config.js. None of these broke anything
(Expo silently skips the missing directory) but all three kept
describing an integration 1024XEngineer#263 said to drop entirely.

Verified: tsc/eslint clean, `expo prebuild --platform android
--no-install` still succeeds.
Code review (PR 1024XEngineer#264): formatDate() constructed a new SimpleDateFormat
on every call, including every 20-second RingRoot tick for as long as
the ring screen is visible -- unnecessary allocation on a repeating
timer. Cached as a static final field; its locale is hardcoded to
Locale.CHINA so there's no correctness risk. formatClock() is left as
a fresh SimpleDateFormat per call since it uses Locale.getDefault(),
which can change at runtime -- caching it would risk showing a stale
locale, not worth it for this level of savings.
…rvice

Part of 1024XEngineer#263.

The rest of the timeflow-alarm module: AlarmScheduler (AlarmManager
scheduling + SharedPreferences persistence + reboot/update
rescheduling), AlarmReceiver/BootReceiver (broadcast entry points),
AlarmModule/AlarmNativeBridge/AlarmPackage (the RN-facing surface),
and RingActivity/AlarmSoundService (the full-screen ring
Activity/Service, built on the AlarmRingUi toolkit from the previous
PR). These 8 files reference each other directly (e.g. AlarmReceiver
constructs AlarmSoundService, RingActivity calls back into
AlarmScheduler/AlarmNativeBridge) and are compiled together as one
Gradle module regardless of PR boundaries, so they can't be split
further without introducing string-based/reflective class lookups
purely to shrink the diff -- not worth the runtime-safety tradeoff.
Code review (PR 1024XEngineer#265): AlarmSoundService.firedNotified was a
service-lifetime boolean, not per-alarm. If a second alarm fired while
the service was already ringing a first one (two reminders scheduled
a few minutes apart), onStartCommand's `if (!firedNotified)` guard was
already tripped, so notifyFired() -- and with it the only signal JS
gets that this alarm rang -- was silently skipped for every alarm
after the first, even though its persisted record was still deleted
via removeFromSavedAlarms(). Keyed the guard by alarmId instead.

Also extracted the alarmId/scheduleId/title extraction-and-fallback
logic (legacy-<requestCode> id, scheduleIdForAlarm lookup, default
title) that AlarmSoundService and RingActivity each reimplemented
identically into AlarmContract.ExtractedExtras, so the two can't drift.

Not fixed: AlarmNativeBridge.consumeDispositions() clears its
SharedPreferences buffer before the JS caller has durably persisted
what it returned, so a process death in that window loses the
disposition. Fixing it needs a two-phase consume/ack contract on the
JS side, which doesn't exist yet in this stack -- flagging for the
wiring PR instead of half-fixing the native side alone.
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.

1 participant