Files
Vojtěch Maršál d0b1529c3f Initial commit: BountyBoard Android client
Native Kotlin + Jetpack Compose (Material 3) client for the BountyBoard
platform. Cookie-session + CSRF auth with a configurable base URL, the full
role-based feature set (admin / consultant / developer), local + OIDC SSO
sign-in, and background push notifications (WorkManager poll) for alerts and
messages. Bundles the app's editorial "ledger" design system and the original
Fraunces / Schibsted Grotesk / Spline Sans Mono typefaces.

Includes a prebuilt debug APK (BountyBoard-debug.apk) for side-loading.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 14:31:31 +02:00

5.1 KiB
Raw Permalink Blame History

Bounty Board — Android

A native Android client for the Bounty Board consulting work-management platform. Built with Kotlin + Jetpack Compose (Material 3), it covers the full role-based feature set — admin, consultant, and developer — over the server's cookie-session + CSRF API.

The visual identity mirrors the web app's "ledger / bounty-poster" aesthetic: warm paper grounds, brown/amber ink, hard offset shadows (no blur), 2px corners, ◈ bounty chips, and the original Fraunces / Schibsted Grotesk / Spline Sans Mono typefaces (bundled, instanced from the web app's variable fonts).

First run

The app opens on a Login page. The default server is https://bountyboard.anypreta.com/. Expand Server on the login screen to point the app at any other Bounty Board instance — the base URL is persisted and every request is retargeted at runtime. Sign in with email/password, register a developer account, or use "Forgot password". A forced password change is handled automatically when the server requires it.

Features

  • Auth — login (with configurable base URL), self-registration, forgot password, forced password change. Session + CSRF are handled transparently by a persistent cookie jar and a double-submit interceptor.
  • Developer — bounty board with search / project / min-bounty / sort filters and claim flow; My Tasks kanban (Assigned · In progress · In review · Done); per-task start / submit / abandon / withdraw; comments; time logging; metrics dashboard; leaderboard.
  • Consultant — atomization board (grouped by project, status filters, quick publish); subdivide / extend / edit / publish; reviews queue with an acceptance- criteria checklist; approve / decline claims; assign-to-AI; developer pool management; metrics.
  • Admin — users (roles, disable, force-reset, delete); projects/customers wizard (per-system credential fields, test connection, sync now); runtime settings; audit log; live service-status panel.
  • Shared — task detail with full role-aware action bar and timeline; real-time-feel messaging (DMs, group/project conversations, image + file attachments, read receipts); notification center with deep links; profile (avatar upload, bio/contact, theme picker with all six themes, leaderboard opt-out, sign-out-everywhere); public profile cards.

Architecture

data/        AppContainer (manual DI) · Repository · SessionManager · SettingsStore (DataStore)
  net/       Retrofit ApiService · PersistentCookieJar · CSRF + dynamic-host interceptors · DTOs
  model/     wire data classes
ui/
  theme/     palette (6 themes) · typography · 2px shapes · hard-offset shadow modifier
  components/ BBButton · BBCard · BountyChip · StatusBadge · BBAvatar · BBTextField · dialogs · TaskCard
  nav/       AppRoot (bootstrap/auth gate) · MainShell (role drawer + NavHost) · Routes
  auth/ developer/ consultant/ admin/ task/ messaging/ shared/ profile/   feature screens + ViewModels
  • Networking: OkHttp + Retrofit (Gson). The base URL is dynamic — a host-selection interceptor rewrites scheme/host/port per request, so changing the server on the login screen takes effect immediately without rebuilding Retrofit. Coil shares the OkHttp stack so /files/{id} image loads carry the session cookie.
  • Auth: cookie-session only. bb_session (httpOnly) and bb_csrf are persisted in a PersistentCookieJar; mutating requests echo bb_csrf into the X-CSRF-Token header.
  • State: ViewModels expose Compose mutableStateOf snapshots; screens are stateless and driven by LocalAppContainer.

Building

Requirements: JDK 17 or 21 (AGP 8.5 does not support newer JDKs as the build JVM), the Android SDK with platform 34 and build-tools 34.0.0, and Gradle 8.9+.

The easiest path is to open the project in Android Studio (Koala / 2024.1+), let it sync, and Run.

From the CLI (the Gradle wrapper is included; point JAVA_HOME at a JDK 1721):

cd BountyBoard_android
JAVA_HOME=/path/to/jdk-21 ./gradlew assembleDebug   # APK → app/build/outputs/apk/debug/
JAVA_HOME=/path/to/jdk-21 ./gradlew installDebug    # install on a connected device/emulator

A prebuilt debug APK is committed at BountyBoard-debug.apk in the project root — adb install -r BountyBoard-debug.apk to side-load it directly.

local.properties already points sdk.dir at ~/Android/Sdk; adjust if your SDK lives elsewhere. The app allows cleartext traffic so you can point it at a local http:// dev server.

Sign-in

Local email/password and OIDC SSO both work. "Sign in with SSO" opens the server's OIDC redirect flow in an in-app browser; once the IdP redirects back, the session cookies are captured into the app's HTTP stack and you're signed in. If the server has no OIDC configured, the app reports that instead of failing silently.

Notes

  • minSdk 26, targetSdk 34. No proprietary services; the launcher icon, splash, and fonts are all bundled.
  • The server contract is documented in ../BountyBoard/api/openapi.yaml and ../BountyBoard/specification.md; this client implements the §6 application API.