-
Notifications
You must be signed in to change notification settings - Fork 8
Codebase Map
一句话 TL;DR:Sleepy 主源码 177 个 Kotlin 文件怎么分组、每组干什么、测试放在哪。给想动手改代码的贡献者用,读完你知道该开哪个文件。
主源码在 app/src/main/java/com/lingion/sleepy/,共 177 个文件,分成四个包加根包。行数最多的两块是 data(61 文件)和 ui(48 文件),widget 39 个、util 19 个,根包只有 MainActivity 和 SleepyApp 两个文件。
com.lingion.sleepy/
├── MainActivity.kt (523 行)
├── SleepyApp.kt (102 行)
├── data/ 61 文件
│ ├── dao/ 2 entity/ 3 diff/ 3 undo/ 1
│ ├── repository/ 1 parser/ 5
│ └── jw/ 43 ← 最大子包,教务导入
├── ui/ 48 文件
│ ├── component/ 12 theme/ 4
│ └── screen/ 32 (edit/imports/manage/mine/schedule/today/widget)
├── util/ 19 文件
└── widget/ 39 文件 (含 notification/ 2 个)
两个文件撑起整个 App 骨架。SleepyApp 是 Application 类,只初始化 Room 数据库、课表仓库、课前通知调度和桌面组件定期刷新四样全局依赖(启动时另做节假日预取),没有 SDK 和广告(app/src/main/java/com/lingion/sleepy/SleepyApp.kt:16)。MainActivity 持有四个底部 tab:Schedule / Today / Manage / Mine(app/src/main/java/com/lingion/sleepy/MainActivity.kt:172),加课、编辑课程这类 overlay 也压在这一层,状态提升到 Activity 级才不会被 tab 切换杀掉(app/src/main/java/com/lingion/sleepy/MainActivity.kt:223)。
| 子包 | 文件数 | 职责 | 代表文件 |
|---|---|---|---|
data 直属 |
3 | 数据库定义、迁移、自定义主题存储 |
AppDatabase.kt、Migrations.kt、CustomThemeStore.kt
|
data/dao |
2 | Room DAO,只有课程和课表两张表 |
CourseDao.kt、TimeTableDao.kt
|
data/entity |
3 | 表实体 |
CourseEntity.kt、TimeTableEntity.kt、SmartPeriodConfig.kt
|
data/diff |
3 | 编辑器保存的行级 diff/patch | RowKeyDiffer.kt |
data/undo |
1 | 单级撤回 | UndoManager.kt |
data/repository |
1 | 业务数据访问唯一入口 | ScheduleRepository.kt |
data/jw |
43 | 教务系统导入(见下节) | JwParser.kt |
data/parser |
5 | 课表格式解析与导出 | ScheduleParser.kt |
数据库只有两张表:courses 和 time_tables,当前 version 6(5 → 6 加了课程别名字段,app/src/main/java/com/lingion/sleepy/data/AppDatabase.kt:14)。任何 schema 改动必须在 Migrations.kt 登记一条 Migration 并挂进 ALL_MIGRATIONS 数组(app/src/main/java/com/lingion/sleepy/data/Migrations.kt:7)。
CourseEntity 的字段名与 WakeUp 课表的 CourseBean schema 兼容(type / day / startNode / step / startWeek / endWeek / color / tableId),这是各类导入格式能无损进库的前提(app/src/main/java/com/lingion/sleepy/data/entity/CourseEntity.kt:10)。SmartPeriodConfig 承载智慧节次的自动模式配置(app/src/main/java/com/lingion/sleepy/data/entity/SmartPeriodConfig.kt:7)。
UI 层只调 ScheduleRepository,不直接碰 DAO(app/src/main/java/com/lingion/sleepy/data/repository/ScheduleRepository.kt:16)。写方法执行前先在 UndoManager 里存全库快照,用户点撤回时取走清空——单级撤回,进程内单例(app/src/main/java/com/lingion/sleepy/data/undo/UndoManager.kt:16)。编辑器保存走 RowKeyDiffer:按 groupId 分桶、用 RowKey 判定同一行,得出 update / insert / delete 三类动作,改颜色这类字段变化原地 update,不删了重插(app/src/main/java/com/lingion/sleepy/data/diff/RowKeyDiffer.kt:6)。
全仓库最大的子包,负责「选学校 → WebView 登录 → 抓 HTML → 解析出课」。文件分四类:
- 基建:
JwProtocol.kt定义协议类型常量,继承自 WakeupSchedule_BUPT(Apache-2.0)的协议枚举(app/src/main/java/com/lingion/sleepy/data/jw/JwProtocol.kt:4);JwParser.kt是全部解析器的抽象基类,同样注明了上游出处(app/src/main/java/com/lingion/sleepy/data/jw/JwParser.kt:21);JwParserRegistry持有「协议 type → parser 工厂」这张单一来源表,type 为空时跑全部候选、按 confidence 和课程数裁决(app/src/main/java/com/lingion/sleepy/data/jw/JwParserRegistry.kt:6);JwParseDiagnostics在 parser 之上做失败分类,区分会话过期、登录页、无课表容器(app/src/main/java/com/lingion/sleepy/data/jw/JwParseDiagnostics.kt:19)。 - 解析器:30 个具体 parser,一个文件一个,强智 QZ 系 7 个变体、正方 ZF 系 2 个、URP 2 个,再加 EAMS5、CQU、UCAS、BJTU、ZJU、USTC、SCU、SEU、NEU、WHUT、 PKU 等单校实现。单双周修正集中在
JwParity,五个同型端点的 parser 共用一份语义(app/src/main/java/com/lingion/sleepy/data/jw/JwParity.kt:10)。 - 学校元数据:
JwSchoolInfo定义学校条目结构,数据从assets/schools.json读进JwImportViewModel(app/src/main/java/com/lingion/sleepy/data/jw/JwImportViewModel.kt:54)。当前 schools.json 有 340 条条目。SchoolDomainMatch把用户手输的 typed URL 映射回目录条目,处理门户域名混进来的场景(app/src/main/java/com/lingion/sleepy/data/jw/SchoolDomainMatch.kt:18)。Eams5PathPrefix推断 supwisdom 新版平台的两种部署前缀(app/src/main/java/com/lingion/sleepy/data/jw/Eams5PathPrefix.kt:4)。 - WebView 内 fetch:
JwFetchProtocol按学校声明 fetch 模式,四种FetchKind(WISEDU / ZF_NEW / QZ / QZ_IEAS),pick 返回 null 就走原来的 outerHTML 路径(app/src/main/java/com/lingion/sleepy/data/jw/JwFetchProtocol.kt:9);JwFetchError定义桥协议错误分类。
给新学校加解析,动这里 + schools.json + 测试 fixture,三处缺一不可。
ScheduleParser 负责 WakeUp 分享文本、ics 等外部格式的导入;SleepyNativeFormat 是 sleepy-v1 原生交换格式的纯函数层(app/src/main/java/com/lingion/sleepy/data/parser/SleepyNativeFormat.kt:9),配套 SleepyNativeParser 和 SleepyNativeExporter;ScheduleExporter 负责导出,有 exportWakeUpJson / exportWakeUpShareText / exportIcs 三个出口(app/src/main/java/com/lingion/sleepy/data/parser/ScheduleExporter.kt:26)。
ui/component 12 个文件是跨屏复用件:课表网格 CourseTableView、冲突卡 ConflictCard、课程详情 CourseDetailSheet、药丸导航 PillNavigationBar、时间段编辑 SmartPeriodEditor 和 TimeSlotEditor、分享面板 ShareScheduleSheet 等。
ui/theme 4 个文件管配色。核心是 Theme.kt 里的 WakeUpColorScheme(沿用 WakeUp 原版语义的色板,app/src/main/java/com/lingion/sleepy/ui/theme/Theme.kt:39)和 SleepyTheme(app/src/main/java/com/lingion/sleepy/ui/theme/Theme.kt:303);CustomSchemeDeriver 从用户配的种子色按 M3 角色关系派生完整 scheme;ThemePresets 是静态预设。
ui/screen 按导航拆成七个子包:
| 子包 | 文件数 | 内容 |
|---|---|---|
screen/schedule |
2 | 主课表屏 ScheduleScreen + ScheduleViewModel(app/src/main/java/com/lingion/sleepy/ui/screen/schedule/ScheduleViewModel.kt:44) |
screen/today |
1 | 今日课程屏 TodayScreen
|
screen/edit |
1 | 加课/编辑课程 AddCourseScreen(含非常规节次逐卡编辑) |
screen/imports |
10 | 教务导入全流程:选校 SchoolSelectScreen、WebView 登录 JwWebViewLoginScreen、正方新版页内 fetch JwZfNewFetchJs、UCAS 的 XRW 头剥除拦截器 SepXrwRequestInterceptor
|
screen/manage |
1 | 课表管理页 ManagementPage
|
screen/mine |
12 | 我的页与设置子页:外观、通用设置、导出、节假日、提醒、关于、开源许可 LicenseScreen(app/src/main/java/com/lingion/sleepy/ui/screen/mine/LicenseScreen.kt:61)、多表管理、自定义主题编辑器 |
screen/widget |
5 | 桌面组件的应用内配置界面:WidgetEditScreen 及三个分区、管理页 WidgetManagementScreen
|
纯逻辑层,多数刻意不依赖 Android Context,方便 JVM 单测。几块大的:
- 冲突布局:
ConflictLayoutEngine把同一天里节次相连的课程算成冲突簇并排布图层(app/src/main/java/com/lingion/sleepy/util/ConflictLayoutEngine.kt:6);ConflictDetailReporter在保存时生成撞车明细文案。 - 时间与周次:
TimeTableUtils解析timeJson(每节课起止时间)、DateUtils完全不依赖 Android Context(app/src/main/java/com/lingion/sleepy/util/DateUtils.kt:11)、WeekRangeOverlap判两个周次区间含单双周是否相交、HolidayManager与HolidayRange管节假日。 - 展示辅助:
CourseColorUtil课程配色、CourseDisplayUtil课程别名的展示名解析(app/src/main/java/com/lingion/sleepy/util/CourseDisplayUtil.kt:6)、PinyinMatcher拼音首字母搜索(app/src/main/java/com/lingion/sleepy/util/PinyinMatcher.kt:6)、MarkdownBlocks解析更新日志。 - 更新链:
UpdateInfo(纯数据)、UpdateManager(下载安装)、UpdateNotifier、VersionUtils语义化版本比较。 - 其他:
AppPrefs全部 SharedPreferences 偏好的集中地、LocaleHelper多语言、HighRefreshRate高刷、FeedbackComposer拼反馈链接。
39 个文件(含 notification/ 2 个),全走 RemoteViews + Canvas bitmap,Glance 渲染层已删除,数据模型和配色集中在 WidgetContent(app/src/main/java/com/lingion/sleepy/widget/WidgetContent.kt:12)。
五种基础排版 × 常规/紧凑两档 = 10 个变体,登记在 ALL_WIDGET_VARIANTS 这张单一来源表里(app/src/main/java/com/lingion/sleepy/widget/WidgetVariantInfo.kt:19):WeekGrid、Today、WeekList、WeekView、TwoDay。每个变体一个 receiver 文件,small 档 receiver 继承常规档。
按职责分:
- 渲染:
WidgetBitmapRenderers画 bitmap;TodayRowGeometry是 Today 内容行几何的单一真值(app/src/main/java/com/lingion/sleepy/widget/TodayRowGeometry.kt:7);RemoteViewsWidgetHelper负责静态面和可滚动条带两条推送路径。 - 刷新:
WidgetUpdater从ALL_WIDGET_VARIANTS派生刷新广播的目标 receiver 列表(app/src/main/java/com/lingion/sleepy/widget/WidgetUpdater.kt:47);WidgetUpdateWorker用 WorkManager 每 15 分钟刷一次,绕开系统updatePeriodMillis最低 30 分钟的限制(app/src/main/java/com/lingion/sleepy/widget/WidgetUpdateWorker.kt:8)。 - 数据:
WidgetTableResolver定「当前展示哪张课表」(默认表优先,其次课程最多的表,app/src/main/java/com/lingion/sleepy/widget/WidgetTableResolver.kt:7);WidgetBindingStore按 appWidgetId 存绑定的 tableId。 - 交互:
WidgetRoutes集中桌面组件点回 App 的路由(app/src/main/java/com/lingion/sleepy/widget/WidgetRoutes.kt:5);PinWidgetRouting解析 pin 请求的widget_type字符串,路由表同样从ALL_WIDGET_VARIANTS派生,10 个变体全部可 pin(app/src/main/java/com/lingion/sleepy/widget/PinWidgetRouting.kt:4);Today 的日期翻页状态在纯 JVM 核心TodayDateNavCore(app/src/main/java/com/lingion/sleepy/widget/TodayDateNavCore.kt:4)。 - 配置:
WidgetConfigureActivity(launcher 添加时的免 UI 快速通道)、WidgetEditCore+ui/screen/widget/的编辑界面、WidgetSizeCore/WidgetResizeCore尺寸。 -
widget/notification/:上课前提醒CourseNotificationScheduler(app/src/main/java/com/lingion/sleepy/widget/notification/CourseNotificationScheduler.kt:40)和息屏通知进度同步的FluidCloudService(app/src/main/java/com/lingion/sleepy/widget/notification/FluidCloudService.kt:21)。
-
app/src/main/assets/schools.json:340 条学校教务入口,JwImportViewModel启动时读入。改学校数据必须同步改app/src/test/resources/jw/schools.json(测试读的就是这份)。 -
app/src/main/assets/PARSERS_NOTICE.json:WakeUpSchedule_BUPT(Apache-2.0)的署名与修改文件清单。 - 文案在
app/src/main/res/values*,六个目录:默认目录是简体中文,另有 en、es、ja、zh-rCN、zh-rTW 五个翻译目录。改用户可见字符串时六处都要过一遍。
纯 JVM 单测,和主源码同包名镜像分布:
| 包 | 文件数 | 测什么 |
|---|---|---|
data/jw |
52 | 每个 parser 一份,外加协议识别、179 校交叉验证回归 Schools179CrossValidationTest、schools.json 一致性 SchoolsJsonConsistencyTest
|
util |
22 | 冲突布局、时间表合并、节假日、版本比较、iCal 导入等 |
widget |
23 | 路由 WidgetRoutesTest / PinWidgetRoutingTest、Today 几何与翻页、变体渲染、绑定与刷新接线 |
data/parser |
19 | 各格式往返:ExportImportRoundTripTest、sleepy-v1 全家、NEU/ics 真实样本 |
ui/screen/imports |
10 | 合并闸 AppendNonConflictGateTest、WebView 契约、拦截器 |
ui/screen/edit |
4 | 非常规节次校验与分组 |
data/entity |
3 | 实体字段与智慧节次 |
data(直属)、data/undo、data/diff
|
6 | 撤回、diff、别名迁移、主题核心 |
ui/component、ui/theme
|
4 | 冲突卡几何、字号缩放、自定义配色派生 |
ui/screen/schedule |
2 | 空态与视图模式会话契约 |
| 根包 | 6 | License 页署名、strings 键位一致性、Manifest 外部打开过滤、AppPrefs 隔离等 |
测试夹具在 app/src/test/resources/,626 个文件:jw/fixtures/<校名>/ 按 school 存 HTML 原文与期望 JSON,jw_fixtures/ 收对抗样本(GBK 编码、图片表格、WebVPN 改写 URL、iframe 嵌套、登录过期等 10 类)。跑测试:./gradlew test。
| 想做的事 | 去这里 |
|---|---|
| 新增一所学校的教务解析 |
data/jw/ 写 parser,JwParserRegistry 挂表,assets/schools.json 加条目,app/src/test/resources/jw/fixtures/ 加 fixture |
| 课表网格样式、冲突排布 |
util/ConflictLayoutEngine.kt(排布算法)+ ui/component/CourseTableView.kt(绘制) |
| 加课 / 编辑课程行为 |
ui/screen/edit/AddCourseScreen.kt + 保存路径 data/diff/RowKeyDiffer.kt + 撤回 data/undo/UndoManager.kt
|
| 导入导出某种格式 |
data/parser/ + ui/screen/imports/
|
| 某个桌面组件显示不对 |
widget/ 里对应 Receiver,几何看 TodayRowGeometry.kt,配色看 WidgetContent.kt
|
| 上课前提醒 / 通知 | widget/notification/ |
| 主题、颜色、深色模式 |
ui/theme/ + data/CustomThemeStore.kt
|
| 应用内检查更新 |
util/UpdateManager.kt 及同族三件 |
| 设置项、偏好存储 |
util/AppPrefs.kt + ui/screen/mine/
|
| 课程别名显示 | util/CourseDisplayUtil.kt |
| 节假日显示 |
util/HolidayManager.kt / HolidayRange.kt
|
| 多语言文案 |
app/src/main/res/values* 六处同步 |
Sleepy Wiki
入门
界面与视图
课程管理
导入导出
小组件与提醒
数据与设置
项目
社区