Commit Graph
3 Commits
Author SHA1 Message Date
pj 5b5594ee05 Port sample-app to KMP with Android, iOS, Web, and SQLite (#16)
* chore(sample-app): hoist gradle wrapper to sample-app root

* chore(sample-app): add KMP root gradle config

* chore(sample-app): add composeApp KMP module build config

* chore(sample-app): add Android manifest for composeApp

* feat(sample-app): add shared domain models and auth constants

* feat(sample-app): add shared number and date formatting

* feat(sample-app): add cross-platform storage, clock, and id

* feat(sample-app): add shared repository with file-backed state

* feat(sample-app): add shared in-memory navigator

* feat(sample-app): add Compose theme and design tokens

* feat(sample-app): add shared UI components (icons, screen, widgets)

* feat(sample-app): add Login and Home pages

* feat(sample-app): add AddAccount, Ledger, AddTransaction pages

* feat(sample-app): add App root composable with routing

* feat(sample-app): add Android Application and Activity hosting Compose UI

* chore(sample-app): remove legacy android module (replaced by composeApp)

* chore(sample-app): bump to latest stable Kotlin/AGP/Compose deps

* fix(sample-app): make iOS compile (drop @Volatile, set bundleId)

* feat(sample-app): add xcodegen spec, SwiftUI host, and iOS Info.plist

* chore(sample-app): ignore build artifacts and generated xcodeproj

* chore(sample-app): update justfile for composeApp layout, add ios target

* docs(sample-app): rewrite README for KMP + iOS flow

* refactor(sample-app): drop in-app status bar and formatClock

* feat(sample-app): add SQLDelight schema and per-platform drivers

* refactor(sample-app): back Repository with SQLite, drop file serializer

* feat(sample-app): wire native back on Android and iOS edge swipe

* feat(sample-app): semantic roles, labels, and a11y descriptions

* fix(sample-app): link libsqlite3 for iOS target

SQLDelight's native driver needs libsqlite3.tbd on iOS; without it the
linker fails with undefined _sqlite3_bind_blob and friends.

* refactor(sample-app): abstract storage behind LedgerStore interface

Platform-specific createLedgerStore() returns a SqlLedgerStore backed
by SQLDelight on Android + iOS. Opens the door for a pure in-memory
web implementation that does not require a SQLite driver.

* feat(sample-app): add wasmJs target with in-memory LedgerStore

Wires a Compose Multiplatform browser canvas entry point. The web
implementation of LedgerStore is an in-memory model with localStorage
persistence, so it does not need a SQLite driver. Back navigation maps
the browser back button to the same BackHandler contract Android and
iOS use.

* chore(sample-app): settings + gitignore for wasmJs dev run

Registers the Node.js distributions repository and switches
repositoriesMode to PREFER_PROJECT so the Kotlin wasmJs plugin can
download its toolchain. Adds kotlin-js-store (lockfile) and ignores
runs/, web screenshots, playwright-mcp scratch output.

* fix(sample-app): singularize transaction count on Home

Shows "1 transaction" not "1 transactions" for accounts with a single
transaction; falls back to "$count transactions" otherwise.
2026-04-19 15:56:33 +07:00
pj d0578dbaaa fix(runner): warn on malformed screen snapshot (#13)
* fix(runner): warn on malformed screen snapshot

screenFromSnapshot swallowed json.Unmarshal errors, so a non-string
screen value silently became "" in the step log and trace while the
verifier still saw the raw JSON. Return the error and warn at the
call site, matching the hierarchy warning pattern.

* docs: clarify --avd is optional for uatu test

The CLI accepts --avd as an empty-string default (cmd/uatu/main.go:49)
and only requires it when no device is connected and multiple AVDs
exist (cmd/uatu/android_env.go:63). Docs and examples that showed it
as required or always-passed were misleading.
2026-04-18 17:14:31 +07:00
pj 6b1c17328c fix(sample-app): make it standalone, no repo_root assumptions (#8)
* fix(cli): fall back to node_modules for @uatu/spec resolution

Drop the hard failure when the uatu source tree is not reachable from
the spec file. Users integrating uatu in their own app have @uatu/spec
installed via npm; esbuild now resolves it from node_modules.

* build(gradle): drop :sample-app include from root settings

The sample now has its own Gradle project in examples/sample-app/android.

* build(sample-app): vendor gradle wrapper

Users running the sample build the APK via ./gradlew from inside the
sample-app's own android/ directory, no repo-root wrapper required.

* build(sample-app): make gradle project standalone

Drop the project(':sdk-android') dependency in favor of the Maven
Central coordinate io.github.priyanshujain:sdk-android. The sample now
owns its settings.gradle.kts and gradle.properties, so it builds
without any pieces of the uatu source tree.

* chore(sample-app): declare @uatu/spec npm dependency

Mirrors what a downstream user would put in their own package.json.
Uses file: for pre-release development; becomes a normal semver pin
once @uatu/spec ships to npm.

* docs(sample-app): rewrite justfile and add README

Justfile drops repo_root; all recipes run against the local gradle
wrapper and uatu from PATH. README is scoped to what a user needs to
run the sample against their own device.

* docs(manual): update sample install steps to standalone layout

./gradlew :sample-app:installDebug no longer exists; the sample owns
its own wrapper under android/.

* feat(sdk): log when Uatu.start succeeds

Silent SDK start makes the "SDK didn't connect" failure mode
impossible to debug. One INFO line at start time is enough.

* fix(sidecar): launch via am start -W instead of monkey

monkey -p <pkg> -c LAUNCHER 1 is unreliable on API 36+: it reports no
error but silently fails to start the activity, so the SDK never runs
and the CLI times out on the SDK-accept handshake.

Resolve the launcher activity via `cmd package resolve-activity
--brief` and launch it with `am start -W -n`. -W makes the call block
until the activity is up, which also makes the subsequent SDK
accept timing deterministic.

* feat(cli): auto-resolve Android device; boot AVD if none connected

--avd becomes optional. Resolution order:
 - use any already-connected adb device;
 - else if --avd names an existing AVD, boot it and wait for boot;
 - else if --avd is missing or names no AVD, error with a clear message.

Falls back to $ANDROID_HOME/emulator/emulator when the binary is not on
PATH, so a standard Android SDK install works without extra shell setup.

* docs(sample-app): AVD is optional; document both paths

just test runs against any connected device. If none, pass AVD=<name>
to have uatu boot the emulator for you.

* feat(cli): auto-discover Android SDK; auto-pick the lone AVD

adb and emulator are looked up via PATH, then $ANDROID_HOME,
$ANDROID_SDK_ROOT, ~/Library/Android/sdk, ~/Android/Sdk, and the
Homebrew cask path. The discovered platform-tools directory is
prepended to the sidecar's PATH so its adb subprocess calls work too.

When --avd isn't passed and no device is connected, the CLI picks the
sole local AVD and boots it. Multiple AVDs → error listing them.

* build(make): add `make install` that go-installs uatu onto PATH

Puts `uatu` into $GOBIN (or $GOPATH/bin) so the sample and any local
dev flow can call it without PATH= prefixes.

* docs(sample-app): zero-config just test; dotenv-load for persistence

Drop the expectation that users prefix commands with PATH=, ANDROID_HOME=,
or AVD=. `just test` now works as-is; optional knobs can be pinned in a
.env file alongside the justfile.

* fix(sample-app): auto-detect ANDROID_HOME for Gradle tasks

The Go CLI finds the SDK itself, but AGP still needs ANDROID_HOME to
resolve `sdk.dir`. The justfile now resolves it from env or canonical
install paths before invoking ./gradlew, so `just install` works out
of the box on a standard Android SDK setup.

* gitignore runs directory for sample app
2026-04-18 15:31:47 +07:00