Files
meatbag/research/sdui_and_haptics_research.md

80 lines
4.1 KiB
Markdown
Raw Normal View History

2026-06-30 15:12:58 -06:00
# Meatbag Research: SDUI Inflation, Wear OS Haptics, and Server Push
This document summarizes architectural research for the Meatbag platform, focusing on performance, latency, and platform-specific haptic patterns.
---
## 1. Server-Driven UI (SDUI) in Compose Multiplatform
### The Reflection-Free Pattern
To meet performance constraints and avoid runtime performance degradation on lightweight clients (such as Wear OS watches), Meatbag implements a strictly typed, reflection-free Component Registry.
```kotlin
@Composable
fun ComponentRegistry(primitive: UiPrimitive, onAction: (ApprovalResponse) -> Unit) {
when (primitive) {
is ApprovalRequest -> ApprovalRequestCard(primitive, onAction)
is ActionList -> ActionListContainer(primitive, onAction)
is TextInput -> TextInputField(primitive, onAction)
}
}
```
* **Advantage**: Compile-time safety. Polymorphic serialization (via `kotlinx.serialization` with custom class discriminators like `"type"`) ensures payloads map directly to Kotlin data types without dynamic inspection.
* **State Management**: Adheres to Unidirectional Data Flow (UDF). Primitives are read-only configuration blueprints; interactive widgets emit state updates upward through explicit event callbacks (`onAction`), keeping clients entirely stateless.
---
## 2. Wear OS Haptic Feedback APIs
To minimize human response times, we utilize precise physical notifications based on the resolved `AttentionTier`.
### Android / Wear OS API Mapping
For triggering haptics on physical Wear OS smartwatches, the system integrates with Android's hardware `Vibrator` API.
#### 1. Predefined Effects (Android SDK 29+)
Modern Wear OS devices support the `VibrationEffect` API, which yields crisp, hardware-optimized clicks rather than fuzzy motor rumbling.
* **`AttentionTier.SILENT_HAPTIC`**: Double short pulses to signal prompt urgency without making sound.
```kotlin
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
val effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1.0f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1.0f, 150) // 150ms delay
.compose()
vibrator.vibrate(effect)
```
* **`AttentionTier.BACKGROUND_NOTIFICATION`**: A light tick effect.
```kotlin
vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
```
* **`AttentionTier.OVERRIDE_SCREEN`**: Repetitive pulsing alert composition.
```kotlin
val timings = longArrayOf(0, 200, 100, 200, 100, 500) // delay, vibe, pause, vibe...
val amplitudes = intArrayOf(0, 255, 0, 255, 0, 255)
vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, -1))
```
#### 2. Legacy Fallbacks (Android SDK < 29)
* For older Wear OS versions, fallback to deprecated pattern arrays:
```kotlin
vibrator.vibrate(longArrayOf(0, 80, 100, 80), -1)
```
---
## 3. Server Push Architecture: WebSockets vs. SSE
Smartwatches are subject to battery constraints and intermittent connectivity. The push transport layer must be responsive yet lightweight.
| Feature | WebSockets | Server-Sent Events (SSE) |
| :--- | :--- | :--- |
| **Protocol** | Bi-directional, custom framing | Uni-directional (Server -> Client), HTTP/1.1 or HTTP/2 |
| **Connection Overhead** | Low (post-handshake) | Extremely low (standard HTTP connection) |
| **Battery Impact** | Higher (constant connection maintenance) | Lower (leverages HTTP client connection pooling) |
| **Firewall Traversal** | Can be blocked by strict proxy rules | Standard HTTP, easily passes through proxies |
| **Meatbag MVP Selection** | **WebSockets** (Allows immediate client confirmation frames and bi-directional pings) | **Candidate for mobile clients** (Fallback for phone targets via standard APNS / FCM) |
### Recommended Hybrid Strategy
1. **Wear OS Smartwatches**: Utilize persistent WebSockets with optimized ping-pong intervals (e.g. 30 seconds) when the app is active in the foreground.
2. **Mobile Phones (iOS & Android)**: Fall back to native push notification systems (FCM/APNS) to wake up the client, which then opens a short-lived WebSocket or POST connection to render the SDUI primitive.