一个极简、纯离线的安卓倒数日日历。在日历上点一天、起个名字,就能看到离那一天还有多久, 或者已经过去了多久。
约 2 MB · 零权限 · 纯离线 · 无广告 · 无账号 · 开源
- 底部三个按钮 —— 首页、日历、设置,一屏一个,随时能切;原来收在侧边栏里的通知与提示、 外观与显示、桌面小组件、数据管理、更新日志、项目介绍都挪进设置,语言也在那里。
- 倒数日列表 —— 一条一张卡片:左边是超大号的天数,右边是名称、标签和日期(填了时间就 一起显示)。已经过去的收在列表最下面,用「已过去」隔开;当天或第二天有日子时,列表顶上 会多一行提醒。
- 置顶与排序 —— 置顶的不受排序影响,始终在最前;排序可选用按日期近的在前、远的在前, 或按添加时间新的在前,选择会记住。它只管还没过去的那段。
- 搜索与筛选 —— 列表上方一个搜索框,右侧可选按名称(含标签)、日期或时间筛选;下面一行 胶囊可以只看「7 天内」,或按某个标签筛。标题下面那行还会说明今年还有几个。
- 精确到分钟 —— 可以是「全天」,也可以指定到某时某分。
- 多标签、自定义颜色 —— 标签不限个数;颜色在编辑页用色相条调,不指定就用主题色。
- 倒数日详情 —— 点卡片进入:年月日时分秒的完整拆分,以及置顶开关、日期、时间和标签。
- 日历 —— 手写月历,一天有多个倒数日时最多显示三个圆点;点一天,那天的倒数日列在下方, 同样能搜索和排序。
- 快速翻月 —— 点月份标题弹出年月选择窗,可一步回到本月,也能 ±1 年、±10 年翻页。
- 长按卡片 —— 直接置顶、编辑或删除,不必先进详情页;删除前会先确认。
- 一键添加多个 —— 新建时填上间隔天数和个数,一次添加一串同名、同标签、同颜色、同时间的 倒数日;添加前会告诉你有几条、最后一条落在哪天。
- 桌面小组件 —— 两种:单个把一条倒数日放到桌面、时分秒实时跳动;清单列出接下来 要数的几条。都随尺寸调整,面板可选浅色 / 深色 / 跟随系统,清单的字号也能调;都不需要任何权限。
- 通知与提示 —— 设置里单独一页:提醒时间、全局提前量,还能给某一条倒数日单独设提前量; 页面上列出接下来的几条和它们各自的提醒时刻,也能发一条测试通知。提醒用非精确闹钟, 不申请精确闹钟权限,重启后自动恢复。
- 农历 —— 新建 / 编辑时可以按农历选日子(1900–2100,含闰月),卡片和详情页会在公历 后面注明对应的农历;只是那一天,不做每年重复。
- 外观 —— 大圆角卡片配 1px 细描边、超大的天数层级。主题有白、黑、青绿、雾蓝、暖橙、玫瑰, 外加自定义色相;「白」是纯白底配接近黑色的强调色,「青绿」保持暖纸底,其余几套的页面、 面板和描边都跟着主题色明显变化。配色固定,不跟随系统深色开关,也不从壁纸取色。
- 中英双语 —— 跟随系统语言,也可以在设置里手动选;日期格式一并切换。
- 备份与恢复 —— 把全部倒数日,以及外观(含小组件面板与字号)、排序和语言设置导出成一个文件, 也能读回来。导出可加密码(AES-256-GCM 加密);导入先验密码,再选「覆盖」或「合并」, 并会先告诉你新增、覆盖或丢弃几条。另有一个「导出日历文件」,写成 .ics 交给系统日历。
- 更新日志 / 项目介绍 —— 设置里按版本列出历次改动,当前版本会标出来;项目介绍就在 应用内,含可复制、可跳转的 GitHub 地址。
- 大字体 —— 标题栏、底部栏和卡片都避开状态栏,并跟随系统字体缩放长高,不挤压文字。
- 不需要任何权限、不联网、无统计。 单模块,运行时不依赖任何第三方库,
minSdk27。 只有可选的「当天提醒」会用到通知权限和开机自启,打开开关时才申请,关掉即撤销。
名字、标签、颜色和时间只填一遍。新建时打开「一键添加多个」,再填两个数:
- 间隔天数 —— 相邻两条之间差几天,1~3650 天。
- 个数 —— 除了上面选的日期,还要再添加几条相同的,1~100 条。
上面选的日期本身也会添加一条,之后每条都比前一条多间隔那么多天。所以 10 月 14 日新建 「打副本」、间隔填 2、个数填 3,列表里就会出现 10 月 14、16、18、20 日共 4 条。
时间那一栏选的值,这一串里每一条都跟着走;全天就是全天,09:30 就是 09:30。这些条目算同一批, 所以「按添加时间」排序时它们挨在一起,里面的先后仍按日期排。每一条都是独立的倒数日,之后各自 编辑或删除,互不影响。
目标是所选日期当地时间的 00:00;这条倒数日如果指定了某时某分,就以那个时刻为准。 这个时刻过去之后,同一套算法反向运行,标签从「还有」变成「已过去」;目标日当天显示的是 「今天已过」。
从最高位起,还没有开始计数的字段用短横线占位,所以一眼就能看出还剩多久:
| 剩余时间 | 显示 |
|---|---|
| 4 个月 11 天 6 时 30 分 15 秒 | ----年04月11日 06时30分15秒 |
| 2 天 30 秒 | ----年--月02日 00时00分30秒 |
| 3 年 4 个月 11 天 6 时 30 分 15 秒 | 0003年04月11日 06时30分15秒 |
年、月、天按日历算,小时、分、秒按流逝时间算,所以夏令时切换的那一天仍然算一整天, 哪怕它实际只有 23 或 25 小时。
长按桌面空白处 → 添加小组件,里面有两个可选:
倒数日 · 单个 —— 再挑一个倒数日(列表里带时间,同名的两条不会挑混)。默认 4×2,拖大拖小 会自己换排版:拖扁了是「名字 + 天数 + 时钟」一行,拖大了再加上目标日期,并把天数拆成 「1年1个月4天」。点它直接打开这条倒数日。填了具体时间的倒数日,它也按那个时刻算,并在日期 那行写出时间(窄排版写在天数后面),和详情页一致。秒针由系统自己走,不额外唤醒应用。
倒数日 · 清单 —— 不用挑,它自己列出接下来要数的几条,从近到远,一行一条;点哪一行就 打开哪一条。填了具体时间的会写成「今天 09:30」。默认 4×2 放得下三行,拖高了最多四行, 行会撑满整个组件;没有要数的日子时显示一行提示。
两个组件的面板都在设置里的「桌面小组件」选:浅色、深色,或跟随系统。清单组件还能换字号 (小 / 标准 / 大);单个组件不跟着变,因为它的文字本来就撑满格子,用的是放得下的最大字号。
两个都不需要任何权限。
APK 发布在 Releases,也可以从 Actions 标签页里
任意一次成功的构建中下载产物。每个版本改了什么见 更新日志.txt。
需要:
- JDK 17
- Android SDK Platform 37 和 Build Tools 36.0.0
- 在
local.properties里写好 SDK 路径,或导出ANDROID_HOME
./gradlew testDebugUnitTest # 单元测试
./gradlew assembleRelease # APK -> app/build/outputs/apk/release/Windows 下使用 gradlew.bat。
| 语言 | Kotlin 2.4.20 |
| UI | Jetpack Compose + Material 3(Compose BOM 2026.09.00) |
| 构建 | AGP 9.4.1、Gradle 9.6.0,版本集中在 gradle/libs.versions.toml |
| SDK | compileSdk 37、targetSdk 36、minSdk 27 |
| 存储 | SharedPreferences 里存一个小 JSON 文档 |
| 依赖 | 运行时仅 AndroidX 与 Compose |
minSdk 为 27。java.time 在 API 26 就有了,主题里的 windowLightNavigationBar
在 27 也有了,所以既不需要 coreLibraryDesugaring,也不需要任何兼容垫片,
更不必把主题按版本拆成两份。
app/src/main/java/io/github/zzpby/tickcount/
├── MainActivity.kt 边到边宿主 + 应用主题(含状态栏图标配色)
├── data/ 模型与持久化
│ ├── CountdownEvent.kt 倒数日模型:id / 日期 / 时间 / 标签 / 颜色 / 置顶
│ ├── EventCodec.kt 倒数日记录的 JSON 读写,含旧格式迁移
│ ├── EventStore.kt 把上面那份文档存进 SharedPreferences
│ ├── SettingsStore.kt 主题、排序、语言与小组件外观的存储
│ ├── Backup.kt 备份文件的格式,以及导入的结果
│ └── BackupCrypto.kt 备份的密码加密(PBKDF2 + AES-GCM)
├── domain/ 纯逻辑,有单元测试
│ ├── Countdown.kt 年/月/天/时/分/秒 的拆解与占位格式化
│ ├── BatchAdd.kt 一键添加多个:按间隔天数铺开一串副本
│ ├── CalendarMath.kt 月历网格构造
│ ├── CountdownList.kt 列表排序、分段与天数距离
│ ├── Search.kt 搜索范围与匹配规则
│ ├── Changelog.kt 把更新日志拆成一个个版本
│ ├── Readme.kt 从 README 里抽取项目介绍的章节
│ ├── Ics.kt 导出 .ics:把倒数日写成日历文件
│ ├── Lunar.kt 农历表与公历/农历互转(1900–2100)
│ └── WidgetCountdown.kt 小组件的天数拆分、形态判定与清单挑选
└── ui/
├── MainViewModel.kt 状态 + 全进程唯一的那个时钟
├── TickCountApp.kt 根画面:三个屏幕的切换、返回键、各弹窗
├── AppDestinations.kt 底部三个按钮,以及应用能到的每一个屏幕
├── AppBottomBar.kt 底部栏:浮起的圆角卡片,里面三个按钮
├── SettingsScreen.kt 设置:分组的条目,语言和备份都在这里
├── AppTitleBar.kt 标题栏:返回 + 名字 + 动作槽
├── HomeScreen.kt 倒数日列表
├── CalendarScreen.kt 月历 + 选中那天的倒数日
├── EventCard.kt 倒数日卡片,主页与日历共用
├── SortButton.kt 排序胶囊:选择列表的排序方式
├── SearchField.kt 搜索框与范围选择
├── EventDetailScreen.kt 单个倒数日的详细画面
├── EventEditorScreen.kt 新建 / 编辑(整屏):名字、标签、日期、时间、颜色、批量
├── DeleteConfirmDialog.kt 删除前的确认弹窗
├── AppearanceScreen.kt 外观与显示:六个主题预设 + 自定义色相
├── WidgetSettingsScreen.kt 桌面小组件:面板与清单字号
├── NotificationsScreen.kt 通知与提示:提醒设置、每条倒数日的提前量与测试通知
├── DataScreen.kt 数据管理:导出、导入、可选加密、导出日历文件
├── ChangelogScreen.kt 更新日志,正文取自本仓库的同名文件
├── ProjectIntroScreen.kt 项目介绍,正文抽自本文件
├── calendar/ 手写月历 + 年月选择窗
├── components/ 选择胶囊、Canvas 图标(含搜索)、色相条、Markdown 渲染、本地化格式
├── notify/ 提醒:非精确闹钟的排期、通知接收器、开机后恢复
├── widget/ 两个桌面小组件:单条与清单的渲染、接收器、配置界面
└── theme/ 配色、字体,以及形状与间距的共享 token
倒计时算法、列表排序、搜索匹配、小组件的天数拆分与清单挑选、更新日志解析、备份编解码与加密、
README 抽取与 Markdown 解析都由 app/src/test/ 下的单元测试覆盖,包括月长、闰日、
跨年、两种夏令时切换,以及「今天不算已过去」这个容易差一天的边界。
矢量 XML 无法绘制汉字,因此启动器文字由
.tools/图标生成/IconGen.java 栅格化:
它加载微软雅黑,把文字缩放到正好落在 Android 自适应图标的 66dp 安全圆内,
再输出 res/drawable-xxxhdpi/ 中的 PNG。
java .tools/图标生成/IconGen.java app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.pngTickCount 只在自己私有的 SharedPreferences 中保存一份倒数日列表——日期、名称、时间、
标签、颜色、置顶和添加时间——此外没有别的东西。颜色是每个倒数日自己的颜色,用来在日历
和列表里区分它们。桌面小组件另有一份 tickcount_widgets,只记录「哪个小组件显示哪一条
倒数日」;主题、排序、语言和小组件外观存在 tickcount_settings。三份都在应用私有目录里,都不会自己
离开设备。
「数据管理」里导出的备份文件写到哪里由你选,加不加密也由你定,应用没有任何办法把它送出去。
它默认不声明任何权限,也不发起网络请求、不含统计与广告;只有可选的「当天提醒」会用到
POST_NOTIFICATIONS(Android 13 起)和 RECEIVE_BOOT_COMPLETED,前者在你打开提醒时才申请,
关掉即撤销。项目介绍里有一个指向本仓库的链接,点击后在浏览器中打开——TickCount 自己没有
联网能力,也没有 INTERNET 权限。
这些数据仅在 Android 自身的云备份开启时才会离开设备,而这由 res/xml/backup_rules.xml 决定。
MIT。