Android 离线 Markdown 阅读器:Kotlin + Jetpack Compose,零权限/零网络/零收集,支持 Mermaid 离线渲染、系统 TTS 朗读与 MP3 导出(不引入 NDK)
Access rate limit exceeded. Please log in to continue.
android-md-reader
Android 平台上的 离线 Markdown 阅读器,Kotlin + Jetpack Compose 编写,零权限、零网络、零数据收集。
一款"小而美"的纯本地 Markdown 阅读应用,是 harmonyos-md-reader 的 Android 移植版(由 HarmonyOS 1.1.4 版本逐项对齐实现)。打开 .md 文件即可阅读:完整 Markdown 语法、表格列宽自适应、Mermaid 图表离线渲染、系统 TTS 语音朗读与 MP3 导出。
✨ 核心特性
| 类别 | 能力 |
|---|---|
| 🔒 隐私 | 不申请任何权限(无存储、无网络、无位置、无通讯录);AndroidManifest.xml 中 uses-permission 数量为 0 |
| 🌐 离线 | Markdown 解析、Mermaid 渲染、语音朗读、MP3 编码 100% 在设备本地完成,无任何远端调用 |
| 📝 Markdown | 标题 / 段落 / 列表 / 引用 / 代码块 / 分割线 / 图片 / 链接 / 表格 |
| 📊 表格 | 列宽自适应(与鸿蒙端、WEB 端共享同一套列宽常量),同行等高、超宽横向滚动 |
| 🧜 图形 | ```mermaid 围栏块离线渲染为 SVG;内置 SVG 净化器(SvgSanitizer)剔除脚本与事件属性 |
| 🔍 图形全屏 | 点击图形进入全屏查看,支持缩放与拖动 |
| 🔊 语音朗读 | 调用系统 TTS(TextToSpeech)逐句合成播放,支持语速/音色设置、断点续读;朗读脚本由 ReadScript 从 Markdown 结构生成(跳过代码块与纯符号行) |
| 💾 MP3 导出 | 逐句 synthesizeToFile → 解析 RIFF/WAVE → 分块 PCM → WebView 内 lamejs 编码 → SAF 流式落盘;不引入 NDK、不新增权限,内存峰值恒定(单段上限 64KB) |
| 🎛 三态视图 | 预览 / 源码 / 分屏,源码编辑实时同步预览 |
| 🗂 打开本地文件 | 系统文件选择器(SAF)+ 系统"用其他应用打开"(VIEW intent,支持 text/markdown、text/plain) |
| 🎨 主题 | 浅色/深色跟随系统;字号、行距、页边距可调 |
| ℹ️ 关于页 | 品牌区 / 定位语 / 可点击署名 / 版本历史(从 CHANGELOG 提取) |
📁 项目结构
android-md-reader/
├── app/
│ ├── build.gradle.kts # 应用模块(compileSdk 35 / minSdk 26 / targetSdk 34)
│ ├── proguard-rules.pro # release 混淆规则入口
│ └── src/main/
│ ├── AndroidManifest.xml # 零 uses-permission;仅一个 Activity + VIEW intent
│ ├── assets/
│ │ ├── demo.md # 内置示例文档(首屏加载)
│ │ ├── graph.html # Mermaid 渲染宿主页(WebView)
│ │ ├── mermaid.min.js # Mermaid 离线渲染引擎(MIT)
│ │ ├── mp3enc.html # MP3 编码宿主页(WebView)
│ │ └── lamejs.js # LAME MP3 编码器(LGPL-3.0,见 NOTICE)
│ ├── java/com/bzm/mdreader/android/
│ │ ├── MainActivity.kt # 入口 Activity + 状态编排
│ │ ├── core/
│ │ │ ├── MarkdownParser.kt # Markdown → 块结构(与鸿蒙端同源逻辑)
│ │ │ ├── SvgSanitizer.kt # SVG 净化(去 script / on* 事件属性)
│ │ │ ├── ReadScript.kt # 朗读脚本生成与时长估算
│ │ │ ├── TtsPlayer.kt # 系统 TTS 编排(逐句合成 / 播放 / 流控)
│ │ │ ├── AudioExporter.kt # WAV → MP3 流式导出编排
│ │ │ └── Mp3EncoderBridge.kt # WebView + lamejs 编码桥
│ │ └── ui/
│ │ ├── ReaderScreen.kt # 阅读主界面与手势
│ │ ├── ReadCard.kt # 朗读卡片(进度 / 控制)
│ │ ├── MarkdownRender.kt # 块级渲染
│ │ ├── GraphView.kt # 图形内联渲染
│ │ ├── GraphFullScreen.kt # 图形全屏查看
│ │ ├── SettingsDialog.kt # 设置弹窗
│ │ └── AboutDialog.kt # 关于页
│ └── res/ # 图标 / 主题 / 字符串
├── build.gradle.kts # 根构建脚本(AGP 8.7.3 / Kotlin 2.0.21)
├── settings.gradle.kts
└── gradle.properties
🚀 快速开始
环境要求
| 组件 | 版本 |
|---|---|
| JDK | 17(compileOptions / jvmTarget 均为 17) |
| Gradle | 8.9+ |
| Android Gradle Plugin | 8.7.3 |
| Kotlin | 2.0.21 |
| Android SDK | compileSdk 35、build-tools 35,minSdk 26(Android 8.0) |
构建
# 1. 指定 Android SDK 位置(该文件已被 .gitignore 排除)
echo "sdk.dir=/path/to/Android/sdk" > local.properties
# 2. Debug 包(首次执行会按 gradle-wrapper.properties 下载 Gradle 8.9)
./gradlew :app:assembleDebug # Windows: gradlew.bat :app:assembleDebug
# 产物:app/build/outputs/apk/debug/app-debug.apk
# 3. Release 包(需自行配置签名,见下)
./gradlew :app:assembleRelease
签名(Release)
仓库不包含任何签名材料。请自行生成 keystore 并在 app/build.gradle.kts 的 android { signingConfigs { ... } } 中配置,或使用 ~/.gradle/gradle.properties 注入口令:
MDREADER_STORE_FILE=/absolute/path/to/your.jks
MDREADER_STORE_PASSWORD=******
MDREADER_KEY_ALIAS=your_alias
MDREADER_KEY_PASSWORD=******
⚠️ 切勿把
.jks/.keystore/ 口令写入仓库——.gitignore已排除常见签名文件与local.properties。
🔐 隐私说明
- 应用不申请任何系统权限(
AndroidManifest.xml无uses-permission节点)。 - 打开文件使用系统文件选择器 /
ACTION_VIEW,由系统(SAF)授权单次读取,应用自身无存储权限。 - 不访问网络:代码中不存在任何 HTTP/Socket 客户端;
mermaid.min.js与lamejs.js均为随包内置。 - 不采集、不上报任何数据;无第三方统计 SDK、无广告 SDK。
- 导出的 MP3 / 另存的 Markdown 全部经系统文件选择器由用户指定落盘位置。
🧩 与其它端的关系
| 端 | 仓库 | 形态 |
|---|---|---|
| HarmonyOS | harmonyos-md-reader | ArkTS / ArkUI |
| HarmonyOS(彻底本地化分支) | harmonyos-md-reader-lite | ArkTS / ArkUI |
| Android | 本仓库 | Kotlin + Jetpack Compose |
| WEB | md-reader-web | 原生 JS + HTML + CSS |
三端的解析规则、表格列宽常量、图形渲染策略保持一致,行为可交叉验证。
📄 许可证
本项目采用 Apache License 2.0,详见 LICENSE。
第三方组件:assets/mermaid.min.js(MIT)、assets/lamejs.js(LGPL-3.0)——详见 NOTICE。
assets/lamejs.js与assets/mp3enc.html仅服务于「导出 MP3」这一可选特性。若不需要该特性,删除这两个文件并移除ui/SettingsDialog.kt中对应入口即可,其余功能不受影响。
Introduction
Android 离线 Markdown 阅读器:Kotlin + Jetpack Compose,零权限/零网络/零收集,支持 Mermaid 离线渲染、系统 TTS 朗读与 MP3 导出(不引入 NDK)
Customize your domain