--- id: "0160" title: "matrix-client-android mini-webapps: WebView + Widget API v2 bridge" status: pending priority: medium created: 2026-05-24 related_flows: ["0011"] related_issues: ["0159", "0161"] dependencies: ["0159"] tags: [matrix, android, webview, widgets, agents, sandbox] --- ## Objetivo Host de widgets en Android equivalente al cliente PC (issue 0152). Mismo contrato Widget API v2. WebView con sandbox estricto + bridge JS-Kotlin implementa capabilities API. Widgets de los rooms operados por agentes (`agents_and_robots`) se ven embebidos: dashboard, formulario, kanban inline, control del agente. ## Tareas 1. ViewModel: - `WidgetsViewModel(matrixClient, roomId)`: - `Flow>` desde state events `m.widget` del room. - `addWidget(widget)`, `removeWidget(widgetId)`. - `generateUrl(widget) -> String` — substituye placeholders Matrix Widget API. - `mintScopedToken(widgetId) -> String` — token efimero scope room+widget. 2. Compose: - `WidgetsPanel` (drawer lateral o bottom sheet en movil): - Tabs con widgets activos del room. - Cada tab = `WidgetView` que envuelve un `WebView`. - `WidgetView` composable: - `WebView` configurado: - `settings.javaScriptEnabled = true`. - `settings.allowFileAccess = false`. - `settings.allowContentAccess = false`. - `settings.allowFileAccessFromFileURLs = false`. - `settings.allowUniversalAccessFromFileURLs = false`. - `settings.mixedContentMode = MIXED_CONTENT_NEVER_ALLOW`. - `webViewClient` con CSP injection + URL allowlist. - `addJavascriptInterface(WidgetBridge, "MatrixWidgetBridge")` — bridge expone Widget API v2. - `CapabilityConsentDialog` Compose — pide consentimiento usuario para capabilities. 3. WidgetBridge (Kotlin): - Implementa capabilities handshake postMessage (igual contrato que cliente PC): - `read_events`, `send_event`, `send_to_device`, `get_openid`, `m.always_on_screen`. - Audit log mensajes JS<->Kotlin en local DB. - Whitelist estricta de capabilities concedidas. 4. Widgets internos primer batch (compartidos con cliente PC): - `widget-agent-panel` — control del agente. - `widget-kanban` — kanban inline. - `widget-issue-tracker`. 5. Tests: - Instrumented `WidgetCapabilitiesTest` — dialog aparece + accept/decline funciona. - Instrumented `WidgetSandboxTest` — widget malicioso (intenta `window.location='file:///etc/passwd'`) bloqueado. - Instrumented `WidgetSendEventTest` — widget con capability envia msg. ## Funciones del registry a crear - `WidgetView_kotlin_ui` — Compose WebView wrapper sandboxed. - `widget_bridge_kotlin_infra` — JavascriptInterface implementando Widget API v2. - `widget_url_template_kotlin_core` — substituyente placeholders (puede compartirse logica con la Go version del PC, contrato identico). - `CapabilityConsentDialog_kotlin_ui` — Compose dialog. - `widget_audit_log_kotlin_infra` — append-only audit log en Room DB. ## Acceptance - [ ] Widget publicado desde cliente PC se ve embebido en Android (mismo room). - [ ] Capability handshake: widget pide `send_event` -> dialog Compose -> accept -> widget envia msg. - [ ] Sandbox: widget intenta `XMLHttpRequest` a `file:///` -> bloqueado. - [ ] Widget agent-panel funcional: muestra logs en vivo del agente + boton restart. - [ ] Audit log persiste en Room DB con timestamp + capability + accept/deny. ## Notas **Critico:** - Mismo contrato Widget API v2 que cliente PC. Widget HTML escrito una vez funciona en ambos. - WebView Android moderno (Chromium 100+) soporta WebRTC + WebGL + service workers. Suficiente para widgets ricos. **Gotcha:** - `WebView.addJavascriptInterface` solo seguro en Android 4.2+ (API 17+, ya minSdk=28). Pero validar todo input desde JS — nunca confiar. - `setAllowFileAccessFromFileURLs(false)` solo aplica si la URL del widget es `file://`. Nuestros widgets son `https://` -> hardcode CSP estricta. - Memory: WebView por tab + 5 widgets activos = ~200MB facil. Limitar a max 3 widgets simultaneos activos. **Roadmap post-DoD:** - Widget marketplace catalog accesible via menu. - "Add to home screen" PWA mode para widgets favoritos (Android shortcut + launcher icon dedicado).