# Handoff — PON Go Android

Context for the next session/developer. Pairs with [`README.md`](README.md) and
[`docs/MCMS_API.md`](docs/MCMS_API.md).

## Architecture

- **`core/`** — pure Kotlin, no Android UI deps. Mirrors the iOS networking core.
  - JSON: MCMS docs are heterogeneous Mongo documents, so we keep raw
    `kotlinx.serialization` `JsonElement` trees and read fields via the accessors
    in `Json.kt` (`el["key"]`, `.string/.int/.double/.array/.obj`) — the analogue
    of the iOS `JSONValue`. Models (`Models.kt`) are thin wrappers over a
    `JsonElement` with computed properties.
  - `ApiClient` (OkHttp): builds versioned requests, injects `Referer` +
    `X-CSRFToken` (writes), unwraps the `{status,data}` envelope, maps status
    codes to `ApiError`, paginates via the `next` cursor. `Call.await()` bridges
    OkHttp to coroutines with cancellation.
  - Repositories return model wrappers; `McmsConnection` wires it all; auth/cookies
    in `Networking.kt` (`SessionCookieJar` + `SessionStore`), TLS policy in
    `ServerTrust`, secrets in `KeychainStore` (EncryptedSharedPreferences).
- **`ui/`** — Jetpack Compose. Navigation is **state-based** in `MainActivity`
  (a `List<Screen>` back stack + `BackHandler`), not Navigation-Compose — so
  detail screens and the dashboard drill-downs can take in-memory objects
  directly. Each screen has a plain **state-holder class** (`…Model`) exposing
  `mutableStateOf` fields + `suspend load()/refresh()` — the analogue of the iOS
  `@Observable` view models. No androidx `ViewModel`/DI.

## Build / verify loop (this matters)

The dev machine had **no `kotlinc`/`gradle` on PATH**, but Android Studio provides
everything. Two loops:

- **Full build (authoritative):**
  ```
  export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
  ./gradlew :app:assembleDebug --no-daemon --console=plain
  ```
  Needs `local.properties` (`sdk.dir=…`, git-ignored). First run downloads deps
  to `~/.gradle`; afterwards it's ~20–60 s. This is what proves the whole app
  (UI + resources + manifest + icons) compiles. **`release` now enables R8
  minify+shrink** (keep rules in `proguard-rules.pro`) — smoke-test a `release` build
  before shipping, since R8 can strip serializers if the keep rules ever drift.
- **Quick core-only check (no Gradle), when iterating on `core/`:** the bundled
  compiler at `…/Android Studio.app/Contents/plugins/Kotlin/kotlinc/bin/kotlinc`
  with `-Xplugin=…/kotlinc/lib/kotlinx-serialization-compiler-plugin.jar` and a
  classpath of Maven jars (kotlinx-serialization-json/core, okhttp, okio-jvm,
  kotlinx-coroutines-core-jvm). Compiles every `core/*.kt` except
  `KeychainStore.kt` (androidx). Faster than a Gradle round-trip.

## API gotchas (all handled in code — don't regress)

- **Single-encode URLs.** Ids stay RAW in `McmsEndpoint`; `ApiClient` encodes the
  path once via `HttpUrl.addPathSegments`. Pre-encoding double-encoded MAC colons
  (`:` → `%3A` → `%253A`) → MCMS "invalid id".
- **Endpoint versions are fixed per endpoint (`v1`/`v3`), NOT global.** Lists/states are
  `v3`; single-doc/login/logs/firmware-files are `v1` (§4) — the two are not
  interchangeable, so there is no global API-version setting. Hardcoded in
  `McmsEndpoint.path()`; endpoints not in the §4 table are marked `// inferred`.
- **401 bounces to login; 403 does NOT** (permission/CSRF — `requiresReauthentication`
  is 401-only).
- **Writes return `{"status":"success"}` (or `"warning"`) with no `data`** — the unwrap
  tolerates both. `"warning"` means the write committed (schema mismatch only) and is
  treated as success, NOT an error (§3.5).
- **ONU registration lives on OLT-STATE buckets** (`OltRepository.registrationMap`),
  not ONU-STATE. Friendly names are `ONU.Name` / `OLT.Name` (NOT `NETCONF.Name`).
- **CPE DHCP** comes from the web helper `GET /api/cpe/onu/<id>/` — a versionless,
  bare `[code, cpe…]` array (use `ApiClient.getRaw`).
- **Firmware** = Procedure 7: GET ONU-CFG → write filename/version into the
  **inactive** bank → full-doc PUT (`FirmwareUpgrade.apply`). Inactive-bank only,
  so service stays up until the ONU reboots. `FirmwareFile.isCompatible` now fails
  CLOSED (unknown vendor/model ⇒ not compatible-by-default) and staging a
  non-compatible image needs an extra acknowledgment. Still worth testing on a spare ONU.
- Field paths were confirmed against live JSON; see `docs/MCMS_API.md` and the
  model accessors.

## Trimmed vs iOS (intentional, easy to restore)

- ONU detail tabs are **Overview / CPE / Logs / Config** — no separate Alarms tab.
  Current alarms live on ONU-STATE `"Alarm"` buckets if you want to add it.
- **Config** shows pretty-printed JSON text, not the collapsible tree the iOS app
  has.
- Dashboard **ONU-state rows aren't tappable** (the Health rows are). The iOS app
  drills ONU-state → filtered ONU list.
- Toggles use `rememberSaveable` (survive rotation, not process death). Persist in
  prefs if you want parity with iOS `@AppStorage`.
- **TLS is HTTPS-only with system-CA trust; self-signed/cleartext support was removed**
  (security — see `CHANGELOG.md`, not "easy to restore"). `res/xml/network_security_config.xml`
  forbids cleartext and user-installed CAs. To reach a private-cert box, install its CA in
  the device system store or pin its key via `ServerTrustPolicy.PinPublicKeySha256`. The
  trust policy lives in `ServerTrust`.

## Repo split

This folder is self-contained (has its own `docs/MCMS_API.md`). To make it a git
repo: `git init` here; the included `.gitignore` already excludes `build/`,
`.gradle/`, `local.properties`, `.idea/`. Commit the Gradle wrapper
(`gradlew`, `gradle/wrapper/*`). The full dev-guide PDFs (`323-1961-30x`) live in
the iOS repo if you ever need the deep source.

## Possible next steps

Restore the trimmed items above; add OLT firmware (Procedure 8 bulk task);
per-ONU live-alarms view; charts for PM history; pull-to-refresh on lists;
dark-theme polish; a proper app theme from the brand colours.
