| 1 | # Android habitat |
| 2 | |
| 3 | This is a runnable Compose application, backed by the same committed world and |
| 4 | score bundle as the terminal and Apple hosts. The existing Kotlin particle |
| 5 | renderer receives that world's state and persistent pod slots. It owns no |
| 6 | telemetry bucketer or score scheduler. Shared mode instead attaches to the local companion and directly renders its immutable points. Internet permission is restricted by the client to authenticated loopback, with cleartext allowed only for 127.0.0.1. See [shared ownership](../SHARED.md). |
| 7 | |
| 8 | Use JDK 17 and an Android SDK with Platform 35 and Build Tools 35.0.0. Set |
| 9 | `ANDROID_HOME` to that SDK, or set `sdk.dir` in an untracked `local.properties`. |
| 10 | The Gradle wrapper pins and verifies Gradle 8.14.5. Dependencies are pinned in |
| 11 | `build.gradle.kts`; the first build needs Google Maven and Maven Central. |
| 12 | |
| 13 | ```sh |
| 14 | cd pet/android |
| 15 | ./gradlew --no-daemon assembleDebug lintDebug |
| 16 | # With an Android device/emulator connected: |
| 17 | ./gradlew --no-daemon connectedDebugAndroidTest |
| 18 | adb install -r build/outputs/apk/debug/CodewhalePet-debug.apk |
| 19 | ``` |
| 20 | |
| 21 | Minimum Android version is 8.0 (API 26). Local device verification uses an |
| 22 | Android 15/API 35 ARM64 emulator. This does not establish physical-device sound, |
| 23 | battery use, or acceptance on all supported Android versions. |
| 24 | |
| 25 | Wild is a simulated creature. Event demo is synthetic telemetry. More → Import |
| 26 | recording opens the same version 1 and 2 exports as the other hosts. |
| 27 | Checkpoint-bearing recordings resume their exact world; exports without a |
| 28 | checkpoint replay from the segment's start (or time zero for version 1). Missing expression versions retain v1. |
| 29 | Both kinds can be exported again. Autosave and native import are bounded to |
| 30 | 8 MiB. Export includes the current checkpoint and writes small chunks through a |
| 31 | private staging file, up to 64 MiB; larger-than-autosave files open in the browser. |
| 32 | |
| 33 | More → Follow file study selects a seekable document through Android's file |
| 34 | picker. A local producer must keep appending canonical PetBucket JSONL to it; |
| 35 | desktop recorder output is not automatically transferred to the device. The |
| 36 | first complete packet establishes a baseline. Only later sequence advancement |
| 37 | is observed, so selecting an old file cannot resurrect its last human request. |
| 38 | The shared cursor also handles sequence restarts, invalid input and duplicates. |
| 39 | Polling reads at most 256 KiB every 400 ms on an IO worker. Pause/background |
| 40 | closes the reader; resume establishes a fresh baseline and discards old sound. |
| 41 | Unavailable, non-seekable or delayed files leave the live world unobserved. |
| 42 | The selected document's read grant and URI are retained when its provider permits; |
| 43 | use Follow again if access expires. This isolated file path does not use the network. |
| 44 | |
| 45 | Sound starts off on each process launch. In isolated modes, one native AudioTrack receives the |
| 46 | core's stereo 48 kHz float PCM. A bounded queue drops late output. Pause, |
| 47 | backgrounding, audio-focus loss and headphone disconnection stop sound; an |
| 48 | audio failure leaves the world and saves running. Still also honors the system |
| 49 | animator-duration setting. Color is accompanied by semantic text and TalkBack |
| 50 | descriptions. Portrait and landscape share the same dots. |
| 51 | |
| 52 | Each isolated mode has a separate private, atomic recording, saved every five seconds |
| 53 | and on suspension. Revision checks reject competing writers. Invalid files are |
| 54 | retained. More → Start fresh habitat preserves the previous file as a recovery |
| 55 | copy; More → Export previous world makes that copy available outside the app. |
| 56 | Completed history rotates into immutable segments before the active file advances. |
| 57 | More → Earlier recordings exports those segments; each includes its own starting |
| 58 | checkpoint. Active memory stays bounded while archived history grows in storage. |
| 59 | |
| 60 | QuickJS is provided by `app.cash.zipline:zipline:1.27.0`. It runs on one worker, |
| 61 | with a 64 MiB heap and evaluation deadlines. Two standard ES2022 method shims |
| 62 | cover that binding's older runtime. There are no Java host bindings or remote |
| 63 | script loads. Gradle packages `../ios/Resources/pet-native.js` and its demo |
| 64 | directly; run `npm --prefix pet run sync` after changing the canonical core. |
| 65 | |
| 66 | Eleven instrumentation tests exercise the real embedded engine, 4,800 shared |
| 67 | world frames and Kotlin digests, 3,000 additional checkpoint continuation |
| 68 | frames, version/import boundaries, sample-exact PCM, atomic storage/recovery, |
| 69 | the Compose pause/still/audio/background lifecycle, and recovery export after |
| 70 | a store conflict or with 90,000 pending interactions beyond the autosave limit. |
| 71 | They also check segment publication, exact continuation, archive corruption and |
| 72 | native restoration of long particle clocks beyond the former 24-hour limit. |
| 73 | Live tests use an instrumentation-only document provider to exercise actual |
| 74 | ContentResolver/descriptor reads, the ViewModel and Compose lifecycle, producer |
| 75 | restart and malformed input. The native live-resume test checks Kotlin geometry |
| 76 | against the resumed shared checkpoint and verifies that old requests stay unknown. The separate |
| 77 | `verify.sh` retains all 380 pure Kotlin conformance checkpoints. |
| 78 |