Open-source Android novel reader with an APK-based extension system.
Built with Kotlin and Jetpack Compose.
Tachiyomi/Mihon-inspired — but not a fork.
Grimoire is written from scratch to read novels, so it isn't shoehorned
into a manga app's page/image data model — the library, sources, and reader are
built around prose from the ground up.
| Library | Browse a source | Novel detail | Locked chapters | Reader |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
- Library — categories (incl. PIN-locked hidden categories), per-category background refresh, custom sort + filter + search, EPUB import, swipeable tabs throughout
- Personal ratings — rate any novel 1–10; sort, filter, and badge your library by your own score (separate from the source's rating)
- Cover & metadata overrides — replace a novel's cover, title, author, description, status or genres without losing them on refresh
- Reader — adjustable font, spacing, line height, colour theme and orientation; in-line image privacy mode; optional Text-to-Speech (on-device + ElevenLabs voices)
- History & Incognito — reading and browsing history, with an incognito mode that pauses history while you explore
- Sources via extensions — every source is a separate APK the app discovers at runtime via
PackageManager, so the catalogue grows without re-shipping the app. Ships with the official extension repository enabled out of the box; add your own repos too (including private GitHub repos via OAuth) - Downloads — foreground download service with a per-chapter image store for offline reading
- NovelUpdates integration — cross-source metadata, rankings, latest releases, saved series, and "read this with one of your sources" matching
- Library updates — background refresh via
WorkManager, per-novel notification rules, and an update-issues log - Source migration — move a novel from one source to another, keeping its history
- Share — generate a shareable reading-progress card (cover + stats over a cover-derived gradient) or copy a novel's link
- Backup & restore — manual and scheduled local backups
- Localization — English and Simplified Chinese, with the rest of the UI ready for more translations
- Statistics & tasks — reading statistics and a background-task log
- Self-updating — in-app updater pulls signed APKs from GitHub releases with changelogs; uncaught crashes are captured on the next launch and pre-fill a GitHub issue
io.grimoire.app/
├── ui/ Compose UI (Scaffold, NavHost, screens, ViewModels)
│ ├── AppNavigation.kt Top-level Scaffold + bottom nav + NavHost
│ ├── AppNavGraphs.kt Per-feature NavGraphBuilder extensions
│ ├── AppRoutes.kt Route constants + TopLevelDestination
│ ├── component/ Shared composables (chapter item, fast scroller, …)
│ └── screen/ One package per surface (library, browse, reader, …)
├── data/
│ ├── local/ Room database + DAOs + entities
│ ├── preferences/ DataStore-backed preference classes
│ ├── source/ ChapterListFetcher + paginated-source helpers
│ ├── download/ Foreground download service + chapter image store
│ ├── libraryupdate/ WorkManager scheduler + worker + updater
│ ├── backup/ Backup manager + worker + JSON models
│ ├── tts/ Device + ElevenLabs TTS engines + playback service
│ ├── novelupdates/ NU.com scraper + matcher
│ ├── update/ In-app GitHub release updater
│ ├── epub/ EPUB importer + parser + local source
│ └── cache/ Cover preloader
├── domain/ Auth / migration / NU info repositories
├── extension/ ExtensionLoader + ExtensionManager + repo browsing
├── auth/github/ OAuth device flow for GitHub
├── di/ Hilt modules (Database, Preferences, GitHubAuth)
└── util/ Content-language helpers
Tech: Kotlin 2.2, Compose BOM 2026.02.01, Material 3, Hilt 2.59, Room 2.7, Navigation-Compose 2.8, DataStore, WorkManager, OkHttp 4.12, Coil, kotlinx-serialization. minSdk 26, targetSdk 36, Java 11.
Grimoire is split into three repos that ship independently:
| Repo | Purpose |
|---|---|
grimoire (this) |
Main Android app |
grimoire-extensions-api |
Shared contract library (Source, Novel, Chapter, Filter…) — published to GitHub Packages and consumed as io.grimoire:extensions-api |
grimoire-extensions |
First-party extension APKs (one per source) |
The app does not dynamically load extension classes — each extension is a separate APK that the OS installs, discovered via PackageManager + a manifest metadata flag. See app/src/main/java/io/grimoire/app/extension/.
Requires Android Studio with the Android Gradle Plugin 9.2.1 toolchain (Kotlin 2.2.10, KSP 2.2.10-2.0.2). Java 11 source / target.
# Debug install
./gradlew :app:installDebug
# Run unit tests
./gradlew :app:testDebugUnitTest
# Release APK
APP_VERSION_TAG=v0.1.0 ./gradlew :app:assembleReleaseThe release versionCode is derived from the latest git tag (vMAJOR.MINOR.PATCH) plus an optional APP_BETA_NUMBER env var. GitHub OAuth client id is read from gradle.properties (GITHUB_OAUTH_CLIENT_ID) or the same-named env var — forks can drop in their own without patching code.
- Bug reports / feature requests: open an issue
- Extension contributions: see
grimoire-extensions - App contributions: see
CLAUDE.mdfor an architecture / convention overview before touching the screen / VM layout





