ottershelf

Ottershelf

Ottershelf is an open-source Android app for reading, tracking and annotating the books on a self-hosted BookOrbit server. It opens EPUB and the other ebook formats, PDFs and comics from your server, keeps books on the phone for offline reading, syncs your reading position and sessions back to the server, and brings BookOrbit’s dashboard, statistics, highlights and achievements to a native Kotlin and Jetpack Compose interface. It requires your own BookOrbit server (or an account on one); it contains no server, no store and no books of its own.

Ottershelf is a modified version of BookOrbit and is not an official BookOrbit release. It is not affiliated with, approved, sponsored or endorsed by the BookOrbit project.

Screenshots

     
Dashboard Library grid Book page
Reading statistics Reading calendar Highlights and notes

Features

Reading

Library

Tracking

Appearance

Requirements

Install

The Play and GitHub builds use the same package ID (io.github.ottershelf) and the same signing key: the project’s own key is also the app signing key on Google Play (Play App Signing with an uploaded key), so either build can update the other without reinstalling.

Building from source

You need JDK 21 and the Android SDK (compile SDK 37). The Gradle wrapper downloads the matching Gradle version.

./gradlew assembleDebug          # debug APK: app/build/outputs/apk/debug/
./gradlew bundleRelease          # release App Bundle: app/build/outputs/bundle/release/
./gradlew assembleRelease        # release APK (unsigned unless a signing key is configured)
./gradlew testDebugUnitTest      # JVM and Robolectric unit tests
./gradlew verifyRoborazziDebug   # screenshot tests against recorded images
./gradlew recordRoborazziDebug   # record the screenshot images again

On Windows, use gradlew.bat in place of ./gradlew.

Signing. The repository contains no keys, passwords or other secrets. A release build is signed only when a properties file outside the repository provides the key: the file named by the Gradle property ottershelf.keystore (for example -Pottershelf.keystore=/path/to/keystore.properties), or else ~/.ottershelf/keystore.properties. It holds four values:

storeFile=release.jks        # relative to this file's folder, or an absolute path
storePassword=...
keyAlias=...
keyPassword=...

Without the file, or with a value missing (a build warning names it), assembleRelease produces an unsigned APK and everything else, including the debug build and the tests, works as usual. .gitignore excludes *.jks, *.keystore, *.p12 and keystore.properties.

Project structure

Path Contents
app/src/main/java/.../core/ Data layer: API client and models, session and token encryption, sync, downloads, tracking, settings, theme preferences
app/src/main/java/.../ui/ Navigation shell, theme, icons and shared Compose components
app/src/main/java/.../feature/ One package per feature: login, home, library, book, reader, pdf, comics, downloads, notes, quotes, scan, requests, bookedit, seriesnext, timer, calendar, history, stats, achievements, settings
app/src/main/assets/foliate/ foliate-js (from BookOrbit’s copy, modified) and its vendored libraries
app/src/main/assets/reader/ The ebook reader’s page and its bridge to the app
app/src/test/ Unit, Robolectric and Roborazzi screenshot tests
app/src/androidTest/ On-device tests (see CONTRIBUTING.md)
tools/ Icon generation, device-test runner, reader script tests
docs/brand/ Artwork and icon sources
fastlane/metadata/ Store listing texts and images
docs/play/ Google Play Console drafts

ARCHITECTURE.md describes the packages, patterns and conventions in detail.

Privacy

Ottershelf has no analytics, advertising, crash reporting or tracking of any kind, and contains no third-party SDK that collects data. It connects only to the server address you type and, only when you tap Look up on selected text, to Wiktionary and Wikipedia, through a separate connection that never carries your account’s token. The camera is used only when you choose to scan an ISBN, photograph a page for a quote or take a cover photo; images are processed on the phone, and a cover photo is sent only to your own server, only when you confirm it. Sign-in tokens are encrypted with an Android Keystore key, and the app’s data is excluded from backups. See PRIVACY.md for the full privacy policy.

Licence and attribution

Ottershelf is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3 only (AGPL-3.0-only), together with BookOrbit’s additional terms, as permitted by section 7 of that licence. It is distributed without any warranty. Copyright notices and the list of modifications are in NOTICE.

Ottershelf contains material from BookOrbit (its copy of foliate-js, and code ported from its web client and API types). As BookOrbit’s additional terms require:

Third-party components

Ottershelf is built with open-source components that keep their own licences, including foliate-js, zip.js, fflate, Lucide icons, Jetpack Compose and AndroidX, CameraX, OkHttp, Coil, Telephoto, zxing-cpp and Tesseract. See THIRD_PARTY_NOTICES.md.

Contributing

Bug reports, feature requests and pull requests are welcome. See CONTRIBUTING.md, the code of conduct, and SECURITY.md for reporting vulnerabilities.