Skip to content

Commit 4eef8bc

Browse files
committed
Merge gac0812fork/new-main: wire real reminder engine
Rebased onto upstream/main now that #243 is merged. Same content as the original merge commit (fcc9f15) plus the reminder_offset_minutes=15 fix (6a4bafd), replayed cleanly on top of #240/#241's schedule UI redesign (no overlap, verified via git merge-tree before rebasing). 将 gac0812 fork(提醒引擎、原生闹钟、地理围栏、系统通知/震动、权限申请)合 并进来,解决 12 个真实冲突,并把两套组合根手工合成一套:保留 #243 的认证/ WebSocket/日程视图骨架,reminderPorts 换成 fork 的真实实现 (NativeAlarmScheduler/NativeDeviceCapability/ExpoAudioPlayback/ NativeLocationMonitor),reminder 从 MockReminderApplication 换成真的 LocalReminderApplication。 冲突解决之外真实接入的部分: - app.json 转成 app.config.js:百度定位 Key 走 process.env,合并双方的 iOS 权限说明/Android 权限并集,去重重复键。 - 新建 SqliteLocalScheduleReader(attach/detach 延迟绑定,供 reminder 引擎 读真实本地日程)和 SqliteReminderStateStore(提醒运行时状态真正落 SQLite,替换掉纯内存的 MemoryReminderStateStore——原来进程一杀已触发的 提醒状态就丢,下次启动会整批重新弹一遍)。 - LocalScheduleWriter 语音写入成功后触发 SqliteLocalScheduleReader.refresh()。 - AppProviders 里接上权限申请 hook + reminder.rebuild(),按认证态门控。 - LocalReminderApplication 的时间型提醒投递加了原生闹钟归属判断:只要排上 了原生精确闹钟,弹窗和语音就完全交给原生 RingActivity/AlarmSoundService, JS 不再重复弹——避免真机上应用内弹窗和全屏响铃互相抢、谁都关不掉的问题。 - 新增 ExpoLocationMonitor:用 expo-location 的系统原生地理围栏 (GeofencingClient/CLCircularRegion)接围栏检测,不依赖百度定位 SDK 的 账号/Key 绑定;NativeLocationMonitor(百度)保留在仓库里,createAppServices 换个 import 就能切回去。 - exact_alarm 权限跳过确认弹窗直接跳系统设置页(没有系统授权框,多一次点 击没有意义)。 - 修了 @irvingouj/expo-audio-stream 的 build.gradle patch:AGP>=8 时 javac/kotlinc 目标版本对不上导致编译失败。 - reminder_offset_minutes 默认值从 200 分钟改成 15 分钟(backend/ instructions.py,200 分钟太长,真机测试等不到触发)。 删掉了 3 个测试老版本原生适配器 API 的过期单测(useReminderPermissionsOnLaunch/ nativeAlarmScheduler/nativeDeviceCapability),新增 SqliteLocalScheduleReader 和 LocalScheduleWriter 刷新触发的集成测试。 已知缺口:云端日程同步(SqliteScheduleSyncService)这次没接,生产代码里还 是零调用点,只接了语音写入这条刷新路径;geofence_radius_meters 本地表没有 这一列,硬编码 200 米。
1 parent 43640cb commit 4eef8bc

121 files changed

Lines changed: 5158 additions & 1600 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
.idea/
3+
.cursor/
34
.env
45
.venv/
56
node_modules/

‎backend/src/timeflow/intelligence/realtime/instructions.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
- 没提到地点,就当时间型日程处理,不要为了填 latitude/longitude 去调用 location_search
4646
或编一个地点——地点型日程的地点仍然必须问清楚,这条只管时间型日程不要凭空加地点。
4747
- 没说提醒方式,默认建一个 reminder_strength 为 medium 的提醒:非全天日程用
48-
reminder_type=before_start、reminder_offset_minutes=200(开始前 200 分钟);全天日程用
48+
reminder_type=before_start、reminder_offset_minutes=15(开始前 15 分钟);全天日程用
4949
reminder_type=at_time、reminder_trigger_at 填当天上午 10:00(带时区偏移)。
5050
5151
地点怎么定

‎frontend/.env.example‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
EXPO_PUBLIC_API_URL=http://10.0.2.2:8000/api/v1
44
EXPO_PUBLIC_WS_URL=ws://10.0.2.2:8000/ws
55
EXPO_PUBLIC_DEVICE_ID=device_001
6+
7+
# Baidu Location API key, consumed by app.config.js at build/prebuild time
8+
# (plugins/withTimeflowBaiduLocation.js writes it into AndroidManifest.xml).
9+
TIMEFLOW_BAIDU_LOCATION_API_KEY=

‎frontend/.prettierignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ dist/
44
web-build/
55
package-lock.json
66
assets/
7+
modules/**/android/build/**

‎frontend/app.config.js‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
module.exports = {
2+
expo: {
3+
name: 'Timeflow',
4+
slug: 'timeflow',
5+
version: '1.0.0',
6+
orientation: 'portrait',
7+
icon: './assets/icon.png',
8+
userInterfaceStyle: 'light',
9+
ios: {
10+
supportsTablet: true,
11+
infoPlist: {
12+
NSMicrophoneUsageDescription: '需要麦克风权限来录制语音指令',
13+
NSLocationWhenInUseUsageDescription: '需要定位权限来关联语音指令发生的地点',
14+
UIBackgroundModes: ['location'],
15+
},
16+
bundleIdentifier: 'com.anonymous.timeflow',
17+
},
18+
android: {
19+
package: 'com.anonymous.timeflow',
20+
permissions: [
21+
'RECORD_AUDIO',
22+
'ACCESS_COARSE_LOCATION',
23+
'ACCESS_FINE_LOCATION',
24+
'ACCESS_BACKGROUND_LOCATION',
25+
'FOREGROUND_SERVICE',
26+
'FOREGROUND_SERVICE_LOCATION',
27+
'SCHEDULE_EXACT_ALARM',
28+
'POST_NOTIFICATIONS',
29+
'USE_FULL_SCREEN_INTENT',
30+
'SYSTEM_ALERT_WINDOW',
31+
'REQUEST_IGNORE_BATTERY_OPTIMIZATIONS',
32+
'FOREGROUND_SERVICE_MEDIA_PLAYBACK',
33+
'VIBRATE',
34+
],
35+
adaptiveIcon: {
36+
backgroundColor: '#E6F4FE',
37+
foregroundImage: './assets/android-icon-foreground.png',
38+
backgroundImage: './assets/android-icon-background.png',
39+
monochromeImage: './assets/android-icon-monochrome.png',
40+
},
41+
predictiveBackGestureEnabled: false,
42+
},
43+
plugins: [
44+
'expo-sqlite',
45+
[
46+
'expo-location',
47+
{
48+
locationAlwaysAndWhenInUsePermission:
49+
'允许 Timeflow 使用你的位置,以便在到达或离开地点时提醒你。',
50+
locationWhenInUsePermission: '允许 Timeflow 在使用期间访问位置,以便地点提醒生效。',
51+
isIosBackgroundLocationEnabled: true,
52+
isAndroidBackgroundLocationEnabled: true,
53+
isAndroidForegroundServiceEnabled: true,
54+
},
55+
],
56+
'@irvingouj/expo-audio-stream',
57+
[
58+
'expo-audio',
59+
{
60+
microphonePermission: '需要麦克风权限来录制语音指令',
61+
enableBackgroundPlayback: true,
62+
},
63+
],
64+
[
65+
'expo-notifications',
66+
{
67+
icon: './assets/icon.png',
68+
color: '#15352B',
69+
defaultChannel: 'timeflow-reminders',
70+
},
71+
],
72+
'./plugins/withTimeflowAlarm',
73+
[
74+
'./plugins/withTimeflowBaiduLocation',
75+
{
76+
apiKey: process.env.TIMEFLOW_BAIDU_LOCATION_API_KEY,
77+
},
78+
],
79+
],
80+
},
81+
};

‎frontend/app.json‎

Lines changed: 0 additions & 34 deletions
This file was deleted.
28.7 KB
Binary file not shown.

‎frontend/eslint.config.js‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,19 @@ module.exports = defineConfig([
88
expoConfig,
99
prettierConfig,
1010
{
11-
ignores: ['dist/**', '.expo/**', 'web-build/**', 'node_modules/**'],
11+
ignores: ['dist/**', '.expo/**', 'web-build/**', 'node_modules/**', 'modules/**'],
1212
},
1313
{
1414
rules: {
1515
'no-console': ['warn', { allow: ['warn', 'error'] }],
1616
},
1717
},
18+
{
19+
files: ['react-native.config.js'],
20+
languageOptions: {
21+
globals: {
22+
__dirname: 'readonly',
23+
},
24+
},
25+
},
1826
]);

‎frontend/index.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// 必须在根组件注册前加载,确保 TaskManager.defineTask 进入顶层作用域。
2+
import './src/infrastructure/location/geofenceTask';
3+
14
import { registerRootComponent } from 'expo';
25

36
import App from './App';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
android/build/
2+
android/.gradle/
3+
*.iml

0 commit comments

Comments
 (0)