feat: Android Compose client for the unibus bus
Thin Jetpack Compose UI over the real Go client (pkg/client) compiled to a gomobile .aar, so the phone speaks NATS and runs the same end-to-end crypto as any other peer. Connect, create room (nats/matrix), publish and live-receive.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
---
|
||||
name: unibus_android
|
||||
domain: tools
|
||||
version: 0.1.0
|
||||
description: "Cliente Android (Jetpack Compose) del bus unibus: conecta, crea salas y chatea con el mismo cliente Go (pkg/client) compilado a un .aar via gomobile, conservando el cifrado E2E."
|
||||
tags: [messaging, nats, e2e, android, compose, kotlin, gomobile, client]
|
||||
lang: kt
|
||||
framework: compose
|
||||
entry_point: "app/src/main/kotlin/com/fnregistry/unibus/MainActivity.kt"
|
||||
dir_path: "projects/message_bus/apps/unibus_android"
|
||||
repo_url: "https://gitea.organic-machine.com/dataforge/unibus_android"
|
||||
uses_functions:
|
||||
- fn_theme_kt_ui
|
||||
- fn_tokens_kt_ui
|
||||
- fn_stack_kt_ui
|
||||
- fn_group_kt_ui
|
||||
- fn_card_kt_ui
|
||||
- fn_badge_kt_ui
|
||||
- fn_button_kt_ui
|
||||
- fn_text_kt_ui
|
||||
- fn_title_kt_ui
|
||||
- fn_text_input_kt_ui
|
||||
- fn_switch_kt_ui
|
||||
uses_types: []
|
||||
e2e_checks:
|
||||
- id: bind
|
||||
cmd: "cd ../unibus && gomobile bind -target=android -androidapi 24 -o ../unibus_android/app/libs/unibus.aar github.com/enmanuel/unibus/mobile"
|
||||
timeout_s: 600
|
||||
- id: build
|
||||
cmd: "fn run gradle_assemble_debug_bash_infra projects/message_bus/apps/unibus_android"
|
||||
timeout_s: 360
|
||||
---
|
||||
|
||||
# unibus_android
|
||||
|
||||
Cliente Android del bus de mensajería `unibus`. La aplicación es una fina capa de
|
||||
interfaz en Jetpack Compose sobre el cliente Go real del bus: `pkg/client` se compila
|
||||
a una librería nativa Android (`unibus.aar`) con `gomobile bind`, de modo que el teléfono
|
||||
habla el protocolo NATS y ejecuta el mismo cifrado end-to-end (ChaCha20-Poly1305 +
|
||||
Ed25519 + X25519) que cualquier otro peer. No se reimplementa ni el protocolo ni la
|
||||
criptografía: hay una única fuente de verdad compartida con el resto del ecosistema.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
MainActivity.kt (Compose UI)
|
||||
└── mobile.Session (Kotlin, generado por gomobile)
|
||||
└── unibus.aar (libgojni.so: pkg/client + nats.go + crypto del registry)
|
||||
├── data plane → NATS TCP (nats://host:4250)
|
||||
└── control plane → HTTP REST (http://host:8470)
|
||||
```
|
||||
|
||||
El wrapper Go que define la API plana del binding vive en
|
||||
`projects/message_bus/apps/unibus/mobile/unibus.go` (paquete `mobile`): expone
|
||||
`NewSession`, `CreateRoom`, `Join`, `Publish`, `Subscribe`, `Request` y la interfaz
|
||||
`FrameListener` con tipos compatibles con gomobile (string, []byte, int, error,
|
||||
interface).
|
||||
|
||||
## Regenerar el binding (.aar)
|
||||
|
||||
Tras tocar `pkg/client` o el wrapper `mobile/`:
|
||||
|
||||
```bash
|
||||
cd projects/message_bus/apps/unibus
|
||||
export ANDROID_HOME=$HOME/android-sdk
|
||||
export ANDROID_NDK_HOME=$(ls -d $HOME/android-sdk/ndk/* | head -1)
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
gomobile bind -target=android -androidapi 24 \
|
||||
-o ../unibus_android/app/libs/unibus.aar \
|
||||
github.com/enmanuel/unibus/mobile
|
||||
```
|
||||
|
||||
## Build del APK
|
||||
|
||||
```bash
|
||||
fn run gradle_assemble_debug_bash_infra projects/message_bus/apps/unibus_android
|
||||
# o, directo:
|
||||
cd projects/message_bus/apps/unibus_android && ./gradlew :app:assembleDebug
|
||||
# salida: app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
## Probarlo contra el bus
|
||||
|
||||
1. Levantar el control plane real (NATS embebido + REST de membresía):
|
||||
```bash
|
||||
cd projects/message_bus/apps/unibus
|
||||
go run ./cmd/membershipd # HTTP :8470, NATS :4250
|
||||
```
|
||||
2. En el **emulador** (`Pixel_API34`), instalar el APK. La app usa `host = 10.0.2.2`,
|
||||
que el emulador mapea al loopback del PC, así que conecta sin más configuración.
|
||||
3. En la app: Conectar → Crear sala → escribir un mensaje. El mensaje viaja al bus por
|
||||
NATS y vuelve al propio suscriptor (eco del subject), confirmando el camino completo
|
||||
APK → bus → APK.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `cmd/membershipd` escucha en `127.0.0.1` (líneas `addr := "127.0.0.1:" + httpPort`).
|
||||
Desde el emulador funciona (10.0.2.2 → loopback del host). Para un **teléfono real** en
|
||||
la LAN o vía Tailscale hay que bindear el control plane y el NATS embebido a `0.0.0.0`
|
||||
y usar la IP de red del PC/VPS en los campos Host de la app.
|
||||
- El control plane v1 es HTTP sin TLS; el manifest declara `usesCleartextTraffic="true"`.
|
||||
Para producción, exponer el bus con TLS y quitar ese flag.
|
||||
- El `.aar` pesa ~24 MB (runtime Go embebido) y el APK debug ~54 MB. Es esperado: el
|
||||
cliente Go completo viaja dentro.
|
||||
- `onFrame` del `FrameListener` llega en una goroutine de entrega de NATS; la UI hace
|
||||
`Handler(mainLooper).post { ... }` antes de tocar el estado de Compose.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v0.1.0 (2026-06-05) — baseline: conectar, crear sala (modo nats/matrix), publicar y
|
||||
recibir mensajes en vivo. Binding gomobile del `pkg/client` con cifrado E2E intacto.
|
||||
Reference in New Issue
Block a user