-
Notifications
You must be signed in to change notification settings - Fork 8
TimeSlot System
一句话 TL;DR:Sleepy 里「第几节课」到「几点到几点」的映射规则——每张课表独立配置,支持逐节手填和智慧节次自动推导两种模式,课表显示、提醒、ICS 导出全部从这里取时间。
节次 = 节次编号 + 上课时间。课程只存编号(startNode 起始节 + step 连堂节数),具体时间由所在课表的节次表反查。
节次配置跟着课表走,每张课表一份,存在 TimeTableEntity 的两个字段里:
-
timeJson— 手动模式的节次表,JSON 数组,每项{node, start, end}。默认值是 12 节制的标准作息,第一节 08:00-08:45(app/src/main/java/com/lingion/sleepy/data/entity/TimeTableEntity.kt:30)。 -
smartConfigJson— 自动模式(智慧节次)的配置,JSON 序列化的SmartPeriodConfig。空串表示这张表走手动模式(app/src/main/java/com/lingion/sleepy/data/entity/TimeTableEntity.kt:41)。
另有 nodesPerDay 记录一天的节次数,默认 12(app/src/main/java/com/lingion/sleepy/data/entity/TimeTableEntity.kt:24),导入时按解析出的最大节次更新。
// timeJson 默认值片段(app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:27)
[
{"node":1,"start":"08:00","end":"08:45"},
{"node":2,"start":"08:55","end":"09:40"},
...
{"node":12,"start":"21:45","end":"22:30"}
]解析走 TimeTableUtils.parseTimeSlotRows,产出编辑用行模型 TimeSlotRow(node, start, end, edgeClass)(app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:291)。edgeClass 只在「非常规节次」出现,见下文。
逐节编辑起止时间。每行三个东西:节次编号、开始时间、结束时间,外加一个删除按钮(app/src/main/java/com/lingion/sleepy/ui/component/TimeSlotEditor.kt:166)。
- 点「添加节次」追加一行,时间为空,编号取现有最大值 +1(
app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:424)。 - 删除某节后,剩余节次重新编号为 1..N(
app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:340)。删第 3 节,原第 4 节变成第 3 节。 - 至少保留 1 节,只剩一行时删除按钮消失(
app/src/main/java/com/lingion/sleepy/ui/component/TimeSlotEditor.kt:149)。
保存时统一校验:每行必须是 HH:mm 格式,且开始时间早于结束时间(app/src/main/java/com/lingion/sleepy/ui/screen/mine/EditTableScreen.kt:275)。校验不过,保存按钮直接报错返回。
不想一节一节填,就告诉 Sleepy 三件事:几点开始、每节多长、一共几节,再描述课间怎么插。SmartPeriodConfig 负责算出整张节次表(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:23)。
| 参数 | 含义 | 默认值 |
|---|---|---|
startTime |
第一节开始时间 | "08:00" |
periodMinutes |
每节时长(分钟) | 45 |
totalPeriods |
总节数 | 12 |
breaks |
课间模板列表,每项一个 BreakOption(minutes, isLong, label)
|
空 |
transitionAssignments |
每个课间位置选用哪个模板的索引,null = 0 分钟连续 |
空 |
课间模板默认名是「小课间 X 分钟」「大课间 X 分钟」,自定义 label 非空时优先用自定义名(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:97)。isLong 只影响颜色和标签,大课间用主色、小课间用第三色(app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:209)。
公式(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:19):
第 i 节开始时间 = startTime + i × periodMinutes + Σ(第 i 节之前的所有课间分钟数)
derive() 从 startTime 出发逐节累加:每节先走完 periodMinutes,再补上该节之后的课间分钟,得出下一节起点(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:57)。第 i 个 transition 表示第 i 节与第 i+1 节之间的课间。
分配索引做了两层保护:超出 totalPeriods - 1 的尾部截掉,越界或非法的索引当作 null(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:35)。null 和越界统一落到 0 分钟,即两节连上(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:47)。
一个真实作息的例子,来自单元测试(app/src/test/java/com/lingion/sleepy/data/entity/SmartPeriodConfigTest.kt:47):5 节、每节 45 分钟、08:00 开始,课间模板 10 / 15 / 65 分钟,分配 [0, 1, 0, 1, null],推出:
第1节 08:00 ~ 08:45 ↓ 10 分钟小课间
第2节 08:55 ~ 09:40 ↓ 15 分钟大课间
第3节 09:55 ~ 10:40 ↓ 10 分钟小课间
第4节 10:50 ~ 11:35 ↓ 15 分钟大课间
第5节 11:50 ~ 12:35
自动模式的编辑器分三块(app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:59):
- 输入区:每节时长、总节数、第一节开始时间。时长和节数最小钳到 1(
app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:92)。 - 课间分配区:点「添加小课间」(默认 10 分钟)或「添加大课间」(默认 30 分钟)建模板(
app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:122),每个模板下面铺一排位置卡片,编号1.5、2.5、…、(N-1).5,点卡片表示「第 i、i+1 节之间插这个课间」。同一模板内可多选位置,跨模板互斥——一个位置只能属于一个课间(app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:52)。 - 预览区:实时列出推导结果,每节一行,课间位置显示「↓ 0 分钟连续」或「↓ N 分钟 小课间/大课间」(
app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:194)。
两个容易踩的细节:
- 删除课间模板时,分配索引会重映射——被删组的位置清空,大于被删索引的组号全部减 1,否则后面所有课间会静默清空(
app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:169)。 - 总节数不足 2 时不显示位置卡片,提示「总节数至少 2 节才能分配课间」(
app/src/main/res/values/strings.xml:721)。
自动模式一旦有改动,derive() 的结果立即回写节次行,保存时 timeJson 与 smartConfigJson 一起落库(app/src/main/java/com/lingion/sleepy/ui/component/TimeSlotEditor.kt:62、app/src/main/java/com/lingion/sleepy/ui/screen/mine/EditTableScreen.kt:282)。切回手动模式能看到自动模式的成果,两模式共享同一份 timeJson。
TimeSlotEditor(手动/自动双模式,顶部 Tab 切换,app/src/main/java/com/lingion/sleepy/ui/component/TimeSlotEditor.kt:51)出现在三处:
- 课表管理 → 编辑课表 → 「节次时间表」折叠区,默认收起,展开后显示「N 节 · 展开」(
app/src/main/java/com/lingion/sleepy/ui/screen/mine/EditTableScreen.kt:206)。 - 文件导入的确认弹窗(
app/src/main/java/com/lingion/sleepy/ui/screen/imports/ImportSheet.kt:1173)。初始值从导入解析结果播种,避免切自动模式时被 08:00/45 分钟默认值覆盖(app/src/main/java/com/lingion/sleepy/ui/screen/imports/ImportSheet.kt:1122)。 - 教务导入的配置页(
app/src/main/java/com/lingion/sleepy/ui/screen/imports/JwImportActivity.kt:174)。
- 手动模式:节数没有硬上限,「添加节次」随便加;下限 1 节。
- 自动模式:总节数输入框只收数字、最多 4 位(
app/src/main/java/com/lingion/sleepy/ui/component/SmartPeriodEditor.kt:378),下限钳到 1;课间分配从 2 节起才可用。 - 默认 12 节。导入的课表有多少节就铺多少节,合并时「哪个大用哪个」(
app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:367)——导入课程实际到达第 13 节,课表就延到 13 节,不足的时间用内置默认铺底(app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:604)。
加课/编辑课页面上,每张课程卡选「起始节」和「节数」(连堂),取值范围 1..N,N 是当前课表的标准节次上界(app/src/main/java/com/lingion/sleepy/ui/screen/edit/AddCourseScreen.kt:1106)。改起始节时,节数上限自动收缩为 N - 起始节 + 1(app/src/main/java/com/lingion/sleepy/ui/screen/edit/AddCourseScreen.kt:1123)。
保存前再验一次:课程尾节越过标准上界直接拒绝,报「第 X 张卡:第 A-B 节超出当前课表的 N 节」(app/src/main/java/com/lingion/sleepy/ui/screen/edit/AddCourseScreen.kt:851)。
标准 1..N 之外还有「非常规节次」:课表可以挂编号为 0、-1、N+1 这类边缘槽位(EdgeClass.Before/After,app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:443),给早自习、晚自习这类表外时间用。勾选「非常规节次」的课程绑定单个槽位、节数锁 1(app/src/main/java/com/lingion/sleepy/ui/screen/edit/AddCourseScreen.kt:174)。另有「非常规时间」:课程自带起止钟点,完全不走节次表。
课表显示。周视图/网格把节次表转成每节一行的 TimeSlot(编号、起止、显示文本),课程按 startNode..startNode+step-1 占格(app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:63)。
提醒与 fluid cloud。课前提醒调度时用 parseNodes 拿节次表,再按课程的 startNode 反查具体上课钟点来排闹钟(app/src/main/java/com/lingion/sleepy/widget/notification/CourseNotificationScheduler.kt:183)。改了节次时间,提醒跟着变。
ICS 导出。导出日历时,事件起止时间从节次表反查:首节查 startNode - 1 行的开始时间,末节查 startNode + step - 2 行的结束时间(app/src/main/java/com/lingion/sleepy/data/parser/ScheduleExporter.kt:128)。「非常规时间」课程直接用自带钟点(app/src/main/java/com/lingion/sleepy/data/parser/ScheduleExporter.kt:124),事件 DESCRIPTION 里仍写「第X - Y节」(app/src/main/java/com/lingion/sleepy/data/parser/ScheduleExporter.kt:161)。
作息变更自适应。保存课表时如果节次表变了,课程节次会按旧表时间窗在新表上重新映射——首节找「结束时间晚于课程起点的第一节」,末节找「开始时间早于课程终点的最后一节」;旧表缺行或无重叠时保持原值不猜(app/src/main/java/com/lingion/sleepy/util/TimeTableUtils.kt:400)。16 节课表改 12 节后,原第 13-16 节的课程不会悬空。
测试锚点。SmartPeriodConfigTest 锁推导语义(连续、插课间、越界回退、最少 1 节);Ics28SmartPeriodBreakTest 锁「加课间只平移之后的节次」和 rows↔timeJson 往返无损(app/src/test/java/com/lingion/sleepy/data/entity/Ics28SmartPeriodBreakTest.kt:31)。改动推导逻辑先跑这两个文件。
Sleepy Wiki
入门
界面与视图
课程管理
导入导出
小组件与提醒
数据与设置
项目
社区