{"id":51542914,"url":"https://github.com/deranjer/bookorbit-android","last_synced_at":"2026-07-09T15:30:50.349Z","repository":{"id":369168918,"uuid":"1285144136","full_name":"deranjer/bookorbit-android","owner":"deranjer","description":"Native Kotlin / Jetpack Compose Android client for the self-hosted BookOrbit reading platform","archived":false,"fork":false,"pushed_at":"2026-07-03T22:14:15.000Z","size":3298,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-04T00:13:28.359Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deranjer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-30T14:17:47.000Z","updated_at":"2026-07-03T22:14:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/deranjer/bookorbit-android","commit_stats":null,"previous_names":["deranjer/bookorbit-android"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/deranjer/bookorbit-android","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deranjer%2Fbookorbit-android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deranjer%2Fbookorbit-android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deranjer%2Fbookorbit-android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deranjer%2Fbookorbit-android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deranjer","download_url":"https://codeload.github.com/deranjer/bookorbit-android/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deranjer%2Fbookorbit-android/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35304874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-09T15:30:49.680Z","updated_at":"2026-07-09T15:30:50.343Z","avatar_url":"https://github.com/deranjer.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BookOrbit — Android (native)\n\nNative Kotlin / Jetpack Compose client for a self-hosted\n[BookOrbit](https://github.com/bookorbit/bookorbit) server. This is a **standalone Gradle project** —\nopen this repository's root directly in Android Studio.\n\n\u003e User-facing docs: **https://deranjer.github.io/bookorbit-android/** (source in [`website/`](website/)).\n\u003e Privacy policy: **https://deranjer.github.io/bookorbit-android/privacy.html**.\n\n\u003e **Status:** this is the BookOrbit mobile client. It supports auth + OIDC, library browsing, search,\n\u003e smart scopes, collections, authors/series, book detail, the foliate.js ebook reader, the Media3\n\u003e audiobook player, and downloads/offline. See [Architecture \u0026 conventions](#architecture--conventions)\n\u003e below and the [Cross-repo couplings](#cross-repo-couplings) section.\n\n## Prerequisites\n\n| Tool               | Version                                | Notes                                                   |\n| ------------------ | -------------------------------------- | ------------------------------------------------------- |\n| **Android Studio** | Ladybug (2024.2) or newer              | Easiest path; bundles a compatible JDK + SDK manager.   |\n| **JDK**            | 17+ (21 recommended)                   | Android Studio's bundled JBR works. CI uses Temurin 21. |\n| **Android SDK**    | Platform **36**, Build-Tools 35+       | Installed via Android Studio's SDK Manager.             |\n| **A device**       | Emulator (API 26+) or a physical phone | Min SDK is **26**; compile/target SDK is **36**.        |\n\nGradle itself does **not** need to be installed — the committed wrapper (`./gradlew`) downloads the\nright version (8.11.1) on first run.\n\n## First-time setup\n\n1. **Install the SDK.** In Android Studio: _Settings → Languages \u0026 Frameworks → Android SDK_ → install\n   **Android API 36** (SDK Platform) and a recent **Build-Tools**. Accept the SDK licenses.\n2. **`local.properties`.** This machine-specific file points Gradle at your SDK and must **not** be\n   committed (it's git-ignored). Android Studio creates it automatically on first open. To create it\n   by hand, put one line in `android/local.properties` (use forward slashes, even on Windows, to\n   avoid Java-properties escaping issues):\n\n   ```properties\n   sdk.dir=C:/Users/\u003cyou\u003e/AppData/Local/Android/Sdk\n   ```\n\n   On macOS/Linux it's typically `~/Library/Android/sdk` / `~/Android/Sdk`.\n\n## Run it in Android Studio (recommended)\n\n1. **File → Open** and select this repository's **root** folder.\n2. Wait for the initial **Gradle sync** to finish (first time pulls dependencies).\n3. Pick a device in the toolbar's device dropdown (or create one in **Device Manager**).\n4. Click **Run ▶**. Studio builds, installs, and launches the app.\n\nThe debug variant installs as application id **`com.bookorbit.debug`** (the `.debug` suffix lets debug\nand release builds coexist on one device).\n\n## Run it from the command line\n\nFrom the repository root:\n\n```bash\n./gradlew :app:installDebug      # build + install on the connected device/emulator\n./gradlew :app:assembleDebug     # just build the APK -\u003e app/build/outputs/apk/debug/\n./gradlew :app:testDebugUnitTest # JVM unit tests\n./gradlew :app:lintDebug         # Android lint\n```\n\nLaunch after installing:\n\n```bash\nadb shell monkey -p com.bookorbit.debug -c android.intent.category.LAUNCHER 1\n```\n\n(`adb` lives in `\u003csdk\u003e/platform-tools/`.) To start an emulator headlessly:\n`\"$ANDROID_HOME/emulator/emulator\" -avd \u003cavd-name\u003e`.\n\n## Connecting to a BookOrbit server\n\nOn first launch the app asks for a server URL. To point at a **local dev server**\n(`pnpm run db:up \u0026\u0026 pnpm dev` from the repo root, listening on `:3000`):\n\n- **Emulator:** the host machine is reachable at `10.0.2.2`, so enter `http://10.0.2.2:3000`.\n- **Physical device:** use your machine's LAN IP, e.g. `http://192.168.1.50:3000`.\n\nCleartext HTTP is permitted (self-hosted servers are often plain HTTP on a LAN); prefer HTTPS when\nexposed. For **OIDC** login, the server must whitelist the mobile redirect URI\n`bookorbit://oauth2-callback` (`OIDC_MOBILE_REDIRECT_URIS`, the default).\n\n## Project layout\n\n```\napp/src/main/\n    assets/reader/            # foliate.js + bridge.js + index.html (foliate copied verbatim from the web client's build)\n    java/com/bookorbit/\n      app/                    # Application (Hilt), MainActivity, nav graph (BookOrbitApp / AuthenticatedApp)\n      core/\n        auth/                 # SessionManager, SecureStorage (EncryptedSharedPreferences)\n        network/              # Retrofit ApiService, interceptors, token refresh, ImageUrls\n        model/                # hand-written @Serializable DTOs (mirror the server's API types)\n        db/                   # Room: reader/audio progress + downloads\n        di/                   # Hilt modules (network, database, player)\n        paging/               # Paging 3 sources\n      feature/\n        auth/ library/ search/ scopes/ collections/ authors/ series/\n        dashboard/ bookdetail/ main/\n        reader/               # foliate WebView host + bridge + CFI progress\n        player/               # Media3 service, MediaController manager, player UI\n        downloads/            # WorkManager downloads + offline catalog\n      ui/                     # shared Compose components + theme\n```\n\n## Architecture \u0026 conventions\n\n- **Kotlin + Jetpack Compose** (Material 3), **MVVM** with `ViewModel` + `StateFlow`, **Hilt** DI,\n  **Coroutines/Flow**. Networking is Retrofit/OkHttp + kotlinx.serialization; images via Coil;\n  audiobooks via Media3/ExoPlayer; persistence via Room + DataStore; downloads via WorkManager.\n- **No emoji as icons** — use `androidx.compose.material.icons` (Material Symbols), matching the repo\n  convention.\n- **API models are hand-written** Kotlin `@Serializable` classes (there is no OpenAPI codegen), so\n  server DTO changes must be mirrored here by hand. See [Cross-repo couplings](#cross-repo-couplings).\n- **Reader theming is a three-way sync.** The foliate assets (`assets/reader/`) are a verbatim copy of\n  the web client's build; when the web foliate or its `bridge.js` `THEMES`/`generateCSS` changes,\n  re-copy into `assets/reader/` and keep `feature/reader/ReaderThemes.kt` aligned. See\n  [Cross-repo couplings](#cross-repo-couplings) for the full rule.\n\n## Testing\n\n```bash\n./gradlew :app:testDebugUnitTest    # unit tests (query builder, playback math, serialization)\n```\n\nCI (`.github/workflows/android-build.yml`) runs lint + unit tests + `assembleDebug` on every PR and\non pushes to `main`.\n\n## Cross-repo couplings\n\nThis app is developed independently of the [BookOrbit server](https://github.com/bookorbit/bookorbit),\nbut two things must be kept in sync **by hand** when the server/web client changes — there is no build\ndependency or codegen that enforces them:\n\n1. **API DTOs.** The models in `app/src/main/java/com/bookorbit/core/model/` are hand-written\n   `@Serializable` mirrors of the server's `/api/v1` response types. When a server endpoint's shape\n   changes, update the matching Kotlin model here. `BaseUrlInterceptor` rewrites every request onto the\n   configured server + `/api/v1`.\n2. **Foliate reader assets (three-way sync).** `app/src/main/assets/reader/` is a verbatim copy of the\n   web client's foliate.js build (`client/public/assets/foliate/` in the server repo) plus `bridge.js`,\n   `index.html`. When the web client updates foliate or the `bridge.js` `THEMES`/`generateCSS` block,\n   re-copy those files here and keep the settings-UI swatches in\n   `app/src/main/java/com/bookorbit/feature/reader/ReaderThemes.kt` aligned with the same theme\n   definitions. This preserves byte-identical EPUB CFI sync with the web client and Kobo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderanjer%2Fbookorbit-android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fderanjer%2Fbookorbit-android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderanjer%2Fbookorbit-android/lists"}