Owner rulings from the admin-docs planning session: - sync-guide: dropped 'Kindle and reMarkable are under consideration' — no such plans exist, and promising support for proprietary device interfaces we will never build sets false expectations. Device posture stays as the table states it: web, Android app, KOReader anywhere (including jailbroken Kindle hardware), native Kobo the sole exception. - calibre-integration: cut 'Custom Calibre Columns (Future)' + 'Stay tuned!' — unshipped-feature promises do not belong in docs; calibre integration is a by-product of the scanner's sidecar support, not a roadmap item. The features list describing Calibre itself stays (that is Calibre's promise, not ours). - android-app: de-branded the two Kindle references per owner's legal caution — 'the Kindle app to Kindle cloud' analogy and 'quality bar is the Kindle app' are now 'any dedicated reader app to its server' / 'a first-class native reader app'; engineering substance unchanged. Governance rule going forward (codified in writing-ui-docs.md in a follow-up): no future-improvement or coming-soon language anywhere without explicit owner approval; anything found gets reported, not silently kept or silently removed.
166 lines
9.7 KiB
Markdown
166 lines
9.7 KiB
Markdown
# Android App — Design & Roadmap
|
|
|
|
This document describes the planned native Android client for Bookhoard: a thin, offline-first reading app that treats the Bookhoard server as its backend. The relationship is the same as any dedicated reader app to its server: the server owns the library, sync, and conflict resolution; the app is a dedicated, mobile-first reading frontend with its own UI, designed independently of the web interface.
|
|
|
|
**Status**: Planning / pre-development
|
|
**Companion repo**: `bookhoard-app` (separate repository, AGPL-3.0)
|
|
|
|
---
|
|
|
|
## 🎯 Product Vision
|
|
|
|
- An **amazing ereader** first and foremost — rendering polish, latency, and reading UX are the product
|
|
- **Mobile-first UI** designed from scratch for phones; not a wrapper around the web app
|
|
- **Thin client**: the server remains authoritative for all sync, book matching, and conflict resolution
|
|
- **v1 formats**: EPUB (ebooks) and CBZ (comics/manga); PDF comes nearly free via the reader toolkit
|
|
- **Android first**. iOS is a real roadmap item but unscheduled — likely contributor-driven
|
|
|
|
---
|
|
|
|
## 🛠 Tech Stack
|
|
|
|
| Concern | Choice |
|
|
| -------------- | ------------------------------------------------- |
|
|
| Language | Kotlin |
|
|
| UI | Jetpack Compose + Material 3 |
|
|
| Reader engine | [Readium Kotlin toolkit](https://github.com/readium/kotlin-toolkit) |
|
|
| Local database | Room |
|
|
| Networking | OkHttp / Retrofit + WebSocket |
|
|
| Background | WorkManager |
|
|
| Images | Coil |
|
|
| Settings | DataStore |
|
|
|
|
### Why native Android
|
|
|
|
- The quality bar is a first-class native reader app. Page-turn latency, text layout fidelity, PDF rendering (`PdfRenderer`), and comic/manga image pipelines are platform-level strengths — and they are the *hard* parts in a WebView, not the easy parts.
|
|
- Android-first removes the "share one codebase across two platforms simultaneously" constraint that motivates hybrid stacks.
|
|
- Solo, AI-assisted development compresses the cost of native (code volume), while native's failure modes (well-documented platform APIs) are far easier to debug — alone or with AI — than cross-framework bridge/plugin bugs.
|
|
- The target audience is the self-hosted community, best reached via GitHub Releases and F-Droid rather than app-store optimization.
|
|
|
|
### Alternatives considered
|
|
|
|
- **Capacitor / WebView shell** (the audiobookshelf-app model): excellent when a self-contained SPA already exists; a poor fit here. Bookhoard's web UI is server-rendered HTMX and cannot be packaged, comics rendering in a WebView caps the polish target, and deep offline support fights the shell.
|
|
- **Flutter**: strong middle ground, but no Readium port and a weaker EPUB/PDF plugin ecosystem than the native toolkits.
|
|
- **Kotlin Multiplatform**: only pays off with a committed near-term iOS effort. Revisit if iOS becomes active; until then it would constrain v1 for a hypothetical.
|
|
|
|
---
|
|
|
|
## 🏗 Architecture
|
|
|
|
Thin, offline-first client. The server API is the contract (see [API Reference](api/api-reference.md) and [WebSocket API](websocket-api.md)).
|
|
|
|
### Module layout
|
|
|
|
```
|
|
:app Compose UI, navigation, dependency injection
|
|
:core:domain Pure Kotlin — models, sync logic, use cases (no Android deps)
|
|
:core:data Room, Retrofit/OkHttp, downloads and file storage
|
|
:feature:reader Readium navigator integration and reading UI
|
|
```
|
|
|
|
Keeping `:core:domain` free of Android dependencies preserves optionality: a future iOS client, a KMP extraction, or a desktop client can reuse or port the domain logic without touching the UI.
|
|
|
|
### Offline-first sync flow
|
|
|
|
1. UI writes go to the local Room mirror **first** (never blocked on network)
|
|
2. A WorkManager queue replays changes to the existing REST endpoints (`/api/progress`, `/api/media-items/:id/notes`, `/highlights`, etc.)
|
|
3. Conflicts are resolved by the server's existing mechanisms — the client never invents its own merge logic
|
|
4. While online, a WebSocket connection receives realtime updates pushed by other devices (web reader, KOReader)
|
|
5. Books are downloaded to app storage for fully offline reading, with storage management UI
|
|
|
|
### Authentication & device identity
|
|
|
|
- **Primary auth: username/password login** via the existing endpoints (`POST /api/auth/login` + refresh). The app is a full user client — browse, collections, ratings, and annotation management all live behind the user JWT, which device tokens cannot reach
|
|
- After login, the app registers itself as a **device** (`device_type: mobile`) and **self-approves** its registration using its own JWT — approval only requires a logged-in user. The phone then appears on the Devices page with sync attribution, per-device settings, and individually revocable access, with no QR ceremony
|
|
- Netflix-style QR pairing as a zero-typing sign-in option: post-v1 (see below)
|
|
|
|
---
|
|
|
|
## 📖 Reader Engine
|
|
|
|
[Readium](https://github.com/readium/kotlin-toolkit) provides EPUB, PDF, and CBZ through one publication model and navigator — production-hardened by real reading apps. This avoids building and maintaining three renderers.
|
|
|
|
Planned reading features:
|
|
|
|
- Custom fonts (including user-loaded), adjustable margins and line height
|
|
- Themes including OLED true-black for battery
|
|
- Paginated and scroll modes; gesture and volume-key page turns
|
|
- Keep-screen-awake while reading
|
|
- Highlights, notes, and bookmarks synced via existing APIs (including deleted-annotation restore)
|
|
- Resume to exact position using EPUB CFI, consistent with universal sync
|
|
|
|
### Comics & manga UX
|
|
|
|
- RTL reading direction and double-page spreads with correct cover/single-page handling
|
|
- Per-book reading-mode overrides (a manga library can default to RTL)
|
|
- Zoom and pan; aggressive preloading of adjacent pages
|
|
- Webtoon / continuous vertical mode: post-v1
|
|
|
|
### Reader settings parity with the web reader
|
|
|
|
The web reader (`web/src/reader/`) is the reference implementation for reading ergonomics — its font selection, reading themes, and highlight system are considered well-designed; only its desktop-oriented presentation is being replaced on mobile. The Android reader should reuse the same settings model (stored in the `reader_settings` table and synced via the settings endpoint) rather than inventing a parallel one:
|
|
|
|
- **Fonts**: the same roster of variable fonts, self-hosted under `/static/fonts/` — Literata (default), Crimson Pro, Source Serif 4, EB Garamond, Libertinus Serif, Noto Serif, Charis SIL, IBM Plex Serif (`FONT_MAP` in `web/src/reader/reader.ts`)
|
|
- **Typography**: `font_size` (default 18), `line_height` (1.6), `margin_width`, `double_page_spread`
|
|
- **Themes**: `chrome_theme` (default `tokyo-night`) for app chrome vs `reading_theme`/`reading_mode` for the page surface, plus the fx stack (`fx_brightness`, `fx_contrast`, `fx_invert`)
|
|
- **Navigation**: `tap_zones_enabled` + `tap_zone_size`, `reading_direction`, `progress_mode`
|
|
- **Highlights**: per-annotation color (default `#ffd54f`), matching the web palette
|
|
|
|
Settings chosen on one device should follow the user everywhere — mobile changes write back through the same sync.
|
|
|
|
---
|
|
|
|
## 🍎 iOS Posture
|
|
|
|
iOS is a real roadmap item but not near-term. The strategy is **not** to pre-pay for it with KMP or a cross-platform framework. Instead:
|
|
|
|
- The documented REST/WebSocket API is the sharing mechanism — a future iOS client is a *new client over the same contract*, never a rewrite of shared logic
|
|
- A contributor-driven Swift/SwiftUI client is welcome; the server needs no changes to support it
|
|
|
|
---
|
|
|
|
## 📦 Distribution & Licensing
|
|
|
|
- **License**: AGPL-3.0, matching the Bookhoard server
|
|
- **Channels**: GitHub Releases and F-Droid; Play Store optional later
|
|
|
|
---
|
|
|
|
## 🚧 Milestones
|
|
|
|
1. **Scaffold** — app shell, auth + QR device pairing, library browsing, book downloads
|
|
2. **EPUB reading** — Readium integration, CFI progress sync, offline-first reading
|
|
3. **Annotations** — highlights/notes/bookmarks sync with offline queue
|
|
4. **Comics** — CBZ navigator with manga modes (RTL, spreads, zoom)
|
|
5. **Polish** — OLED themes, gestures, background sync, storage management
|
|
|
|
---
|
|
|
|
## 🔭 Post-v1 Ideas
|
|
|
|
### QR pairing sign-in (Netflix-style)
|
|
|
|
"Add device" on the web (while logged in) displays a QR code; a fresh app install scans it and is **fully signed in** — no server URL, no password, nothing typed on the phone.
|
|
|
|
- **QR is a full login**: the claim endpoint returns JWT + refresh token (plus the device token for sync identity)
|
|
- **Typed-code fallback** (GitHub/Netflix device-flow style: app displays a short code, user enters it on the web) for phones with broken cameras or no camera
|
|
- **KOReader keeps its existing flow unchanged** — no typed-code pairing there; it is already as convenient as it can be
|
|
- **Use the configured `BASE_URL`, never a detected LAN IP** — if the server is published at `https://public.domain`, pairing must work identically from outside the LAN
|
|
- Requires small server additions: `pair`/`claim` endpoints backed by single-use pairing sessions with a short TTL (in-memory like `pendingRegistrations`)
|
|
|
|
### Other ideas
|
|
|
|
- Webtoon / continuous vertical reading mode
|
|
- Home-screen widgets and app shortcuts ("continue reading")
|
|
- Text-to-speech
|
|
- OPDS feed consumption from other servers
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- **[API Reference](api/api-reference.md)** - Complete REST API
|
|
- **[WebSocket API](websocket-api.md)** - Real-time sync events
|
|
- **[Sync Guide](../user/sync-guide.md)** - How universal sync works
|
|
- **[Devices API](api/devices/get_devices.md)** - Device registration and approval
|