Build a modern, well-styled native Android app that connects to a Pinakes instance's Mobile API (
/api/v1) and lets a library user search the catalog, view book details, manage loans/reservations, wishlist, profile, and see their notification feed. Goal: a debug APK that installs and runs.This is the authoritative spec. Decisions are locked — implement them.
- JDK 21 at
$JAVA_HOME(/opt/homebrew/opt/openjdk@21/...).java/javacwork. - Android SDK at
ANDROID_HOME=/opt/homebrew/share/android-commandlinetools— already hasplatform-tools,build-tools,platforms/android-36, and acceptedlicenses. Usesdkmanagerto add anything missing;yes | sdkmanager --licensesto accept. - No system
gradleand nokotlinCLI — that's fine. Generate the Gradle wrapper and build with./gradlew. To create the wrapper without system gradle:brew install gradle(brew is available) thengradle wrapper --gradle-version 8.10.2, OR write the wrapper files directly. After that, ALWAYS use./gradlew. - 75 GB free disk.
adbis available for install/test.
- Kotlin + Jetpack Compose + Material 3 (Compose BOM). Single-module app.
- compileSdk/targetSdk 35 (install
platforms;android-35+build-tools;35.0.0if the build prefers them; android-36 is present but 35 is the safe stable target), minSdk 26. - AGP 8.7.x, Gradle 8.10.x, Kotlin 2.0.x, Compose compiler via the Kotlin Compose plugin (Kotlin 2.0). JDK 21 toolchain.
- Networking: Retrofit + OkHttp + Moshi (or kotlinx.serialization) — pick one and be
consistent. Bearer-token interceptor. Base URL is the user-entered instance URL +
/api/v1. - Navigation: Navigation-Compose. State: ViewModel + StateFlow. DI: Hilt OR manual (keep it simple; manual is fine to reduce build risk).
- Image loading: Coil (book covers — use the absolute cover URLs the API returns).
- Secure token storage: EncryptedSharedPreferences (androidx.security-crypto) or DataStore; store the instance URL + bearer token per the auth flow.
- Primary = brand magenta
#D70161(the Pinakes logo colour), Secondary = indigo#6366F1, neutrals slate/gray. Material 3 dynamic-free, brand-coloured scheme. - Typography: Inter (bundle the font, or use the closest system fallback if bundling is risky). Clean, generous spacing, rounded cards, soft shadows.
- Light AND dark theme (proper M3 color schemes for both).
- Bottom navigation (Search / My Library / Wishlist / Profile) + a top app bar.
- Book cards with cover thumbnails, availability chips, tasteful empty/loading/error states.
- Brand assets are in
_contract/brand/(logo.png, logo_small.png, pinakes.png) — use the logo on the onboarding/splash and the app icon (adaptive icon, magenta background). - Aim for bello, moderno, in linea con Pinakes — polished, not a wireframe.
- OpenAPI 3.1 snapshot:
_contract/openapi.json(the source of truth for every endpoint, request/response shape, and the{data, meta, error}envelope). Generate models + the API interface from it. - Endpoint manifest (one row per route, with the idempotency/ETag contract):
_contract/endpoint-manifest.spec.js. - Backend design doc:
_contract/MOBILE_API_SPEC.md. _contract/health-sample.jsonshows the discovery payload shape.- Auth flow: (1) user enters the instance URL → call
GET /api/v1/health→ show the library name/identity + checkapp_access_enabledand https; (2)POST /auth/login{email,password,device_name,device_id,platform}→ store the returned bearertoken; (3) sendAuthorization: Bearer <token>on every authed call; (4)POST /auth/logoutrevokes it. Envelope: success{data,meta,error:null}, error{data:null,meta,error:{code,message}}. - All dates are ISO-8601 UTC; format locally. Catalog GETs support ETag/304 (honour it via OkHttp cache if easy, otherwise ignore — not required for v1).
- Onboarding: instance URL entry →
/healthdiscovery (show library name, logo, https warning) → continue. - Login (email + password). Show clear API error messages. Persist token + URL.
- Catalog search: query + filters (text/author/publisher/genre/language/available), cursor pagination (infinite scroll), book cards with covers + availability chips.
- Book detail: full payload (cover, metadata, availability, copies, shelf), personal history (read/reserved/wishlisted), and actions: Reserve/Request loan, Wishlist toggle.
- My Library: my loans (active + history) and reservations; cancel a pending reservation.
- Wishlist: list + remove.
- Profile: view/edit profile, change password, logout, list devices.
- Notifications feed (
GET /me/notifications): loan due/overdue, reservation ready, book available — pull-to-refresh. - Contact/message: a simple "message the library" form (
POST /messages).
Push (UnifiedPush): only if the build is green and time remains — wire device
registration (POST /me/push/subscribe) + a UnifiedPush distributor receiver and the prefs
screen (/me/push/prefs). Otherwise stub the prefs screen and note it in STATUS.
- De-risk EARLY: scaffold a minimal Compose app and get
./gradlew assembleDebugGREEN before piling on features. Prove the APK pipeline first. - Then implement features, rebuilding frequently.
- Final:
./gradlew assembleDebug; the APK lands atapp/build/outputs/apk/debug/app-debug.apk. Copy it to the repo root aspinakes-debug.apkso it's trivial to find. - Run
./gradlew lintand./gradlew testDebugUnitTest(if any tests) — best-effort. - Write
STATUS.mdat the repo root: what builds, what's implemented vs partial vs TODO, the exact APK path, how to install (adb install -r pinakes-debug.apk), and how to point it at a Pinakes instance (the local dev API ishttp://<lan-ip>:8081, app access must be enabled). Be honest — no spin. - Write a short README.md (what it is, how to build, the tech stack).
- This folder is OUTSIDE the Pinakes git repo — do NOT touch the Pinakes repo.
git inithere is fine but optional; do NOT push anywhere. - Keep the build deterministic: pin all versions in
gradle/libs.versions.toml(version catalog). No+/dynamic versions. - Prefer fewer, well-chosen dependencies to keep the build converging.
- If the build loop can't fully converge, leave the project build-ready, the furthest-along APK if any, and a precise STATUS — never claim a green build that isn't.