Files
john-okeefe 9e516b96cc docs(android): require reader settings parity with the web reader
The web reader's font roster (Literata default, plus seven self-hosted
variable fonts), typography controls, chrome/reading theme split,
fx brightness/contrast/invert stack, tap zones, and highlight palette
are the reference design for the Android reader - only the mobile
presentation differs. Document the mapping to the synced reader_settings
model so the app reuses it instead of inventing a parallel one.
2026-08-29 15:25:52 -04: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 the audiobookshelf app to an audiobookshelf server, or the Kindle app to Kindle cloud — 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 the Kindle 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