Files
bookhoard/docs/developer/android-app.md
T
John O'Keefe 7e2e1c4c60 docs: remove speculative promise language, de-brand Android design analogy
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.
2026-11-02 13:34:21 -05:00

9.7 KiB

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
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 and WebSocket API).

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 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