# Integración Videollamadas Juphoon — Resumen del progreso ## Contexto SaveFamily S.L (Bizkaia) está integrando videollamadas y chat entre su app móvil Flutter y sus smartwatches infantiles (RTOS/Android), usando el SDK de Juphoon (`jc_sdk`). **Actores:** - Grupo SaveFamily S.L — Cliente, dueño de la app y backend - Shenzhen i365-Tech Co., Limited (Jane Zhang, Carmen) — Fabricante hardware, intermediario comercial - Juphoon/JUQU (Allen) — Proveedor del SDK de videollamadas - SeTracker — Proveedor del firmware del reloj y servidores auxiliares **Cotización aprobada: $8,835** (Integración $2,200 + Chat $2,950 + Cloud Photo Album $735 + Encryption $2,950) --- ## Lo que se hizo ### 1. Análisis de documentación (3 rondas) - **Ronda 1** (31-03-2026): 50 preguntas técnicas → 27/50 respondidas (54%) - **Ronda 2** (01-04-2026): 17 preguntas generales → 17/17 respondidas (calidad desigual) - **Ronda 3** (09-04-2026): Documentación oficial SDK recibida — Quickstart V1.1 Flutter (13 páginas), sequence diagrams, protocolo TCP, connection/mutual dialing process - Documentos generados: análisis completo, conclusiones, preguntas bilingües ES/EN, análisis cruzado de respuestas ### 2. Cuenta Juphoon Cloud creada (16-04-2026) - Consola: juphoon.com (+34) - App creada: "SaveFamily" (tipo IoT, escenario Smartwatch) - **AppKey:** `9efcf2d889dc8a0320925096` - **AppSecret:** `ui7pr73ggl5rr0gf01np` (solo backend) - **AES_KEY IoT:** `8e3637pG7E9144E0` (solo backend) - Token auth activado en consola ### 3. Paquete `packages/videocall_sdk/` creado Wrapper 100% del `jc_sdk` v2.16.5 con arquitectura sólida (patrón `sca_treezor` del monorepo): - **7 servicios** cubriendo toda la API pública del SDK: - `VideocallClient` → JCClient (auth, login, logout, messaging) - `VideocallCallService` → JCCall (llamadas 1-to-1) - `VideocallDeviceService` → JCMediaDevice (cámara, mic, speaker) - `VideocallChannelService` → JCMediaChannel (llamadas grupales) - `VideocallPushService` → JCPush (push notifications) - `VideocallNetService` → JCNet (estado de red) - `VideocallLogService` → JCLog (logging) - **Constructor injection** (no singletons estáticos) - **GetIt module** (`videocallSdkModule(config)`) - **`VideocallSdkManager`** orquestador de inicialización (Client → Device → Call/Channel/Push) - **`VideocallSdkConfig`** abstracto para config por entorno - **Riverpod providers** + StreamProviders para UI reactiva - **Callbacks del SDK → Dart Streams** ### 4. Permisos nativos configurados - **Android:** RECORD_AUDIO, ACCESS_WIFI_STATE, MODIFY_AUDIO_SETTINGS, BLUETOOTH + uses-feature (camera, bluetooth) + ProGuard rules (juphoon, justalk) - **iOS:** NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription, NSCameraUsageDescription actualizado + Podfile GCC_PREPROCESSOR_DEFINITIONS (PERMISSION_CAMERA, PHOTOS, MICROPHONE) ### 5. AppKey configurado por entorno - `juphoonAppKey` en development.json, staging.json, production.json - `Environment.juphoonAppKey` via `String.fromEnvironment()` - `SaveFamilyVideocallConfig` implementa `VideocallSdkConfig` - `videocallSdkModule(config)` integrado en `init_app.dart` ### 6. Feature `videocall/` creada en device_management Feature completa siguiendo el patrón del monorepo (builder + domain + data + presentation): **Domain:** - `videocall_error.dart` — enums de error/success/screenMode - `videocall_participant.dart` — entidad Freezed para participantes grupales - `videocall_signaling_repository.dart` — interface señalización backend **Data:** - `videocall_signaling_datasource.dart` — interface - `videocall_signaling_datasource_impl.dart` — placeholder (TODO cuando backend dé spec) - `videocall_signaling_repository_impl.dart` — impl **State:** - `videocall_view_state.dart` — Freezed state 1-to-1 (screenMode, sdk ready, mic/speaker/camera, canvas, error/success events) - `videocall_view_model.dart` — Notifier 1-to-1 (init, login, call, answer, hangup, mute, speaker, camera, streams del SDK) - `group_call_view_state.dart` — Freezed state grupal - `group_call_view_model.dart` — Notifier grupal (join, leave, participants, streams) **Widgets:** - `video_view_widget.dart` — renderiza JCMediaDeviceVideoCanvas (iOS/Android) - `call_controls_widget.dart` — mic, speaker, camera, hangup (botones circulares) - `call_status_indicator.dart` — "Llamando...", "Conectando..." - `incoming_call_overlay.dart` — aceptar/rechazar llamada entrante (fullscreen) - `participant_tile_widget.dart` — tile individual con video + nombre - `participant_grid_widget.dart` — grid responsivo de participantes **Screen:** - `videocall_screen.dart` — 4 modos: idle (input userID + botón llamar), outgoing (llamando...), incoming (overlay aceptar/rechazar), inCall (video fullscreen + PIP + controles) **Routing:** - `videocall_builder.dart` — GoRouter builder - Ruta: `/legacy/dashboard/device_management/videocall` ### 7. Code review realizado Score: **6/10 — Request changes** **Issues identificados (pendientes de corregir):** 1. Hardcoded test credentials (`p_test1/test123`) en UI de producción → guardar con `kDebugMode` 2. `_onCallItemRemove` llama async sin await → race condition 3. Todos los errores mapean a `I18n.errorGeneric` → sin diferenciación para el usuario 4. `videocall_screen.dart` (310 líneas) demasiado grande → extraer `_IdleView` y `_InCallView` a ficheros separados como `ConsumerWidget` 5. `group_call_view_model.dart` es dead code (no lo consume ninguna screen) 6. Signaling placeholder con `throw UnimplementedError` → cambiar a no-op 7. `VideocallParticipant` (domain) expone tipo SDK (`JCMediaDeviceVideoCanvas`) → mover al ViewModel --- ## Dónde quedamos - **Rama:** `feature/videocall-sdk-integration` - Los cambios del paquete `videocall_sdk` están **commiteados y pusheados** (3 commits) - Los cambios de la feature están en disco pero **sin commitear** (necesitan correcciones del code review) - `fusion-app` avanzó y revirtió algunos cambios compartidos (permisos, rutas) → hay que re-sincronizar --- ## Pendiente ### Correcciones del code review - [ ] Guardar test credentials con `kDebugMode` - [ ] Fix async race en `_onCallItemRemove` - [ ] Implementar mensajes de error diferenciados - [ ] Extraer `_IdleView` y `_InCallView` a ficheros separados - [ ] Integrar o excluir group call ViewModel - [ ] Cambiar signaling placeholder de throw a no-op - [ ] Remover SDK type de domain entity ### Pruebas APP↔APP (primera llamada real) - [ ] Login con 2 userIDs de prueba (`p_test1`, `p_test2`) - [ ] Videollamada entre dos teléfonos físicos - [ ] Probar incoming call, reject, hangup, mute, camera switch - [ ] Probar app cerrada en iOS (riesgo #1 — push/background) ### Integración con backend - [ ] Obtener API REST del backend SaveFamily para señalización - [ ] Definir formato userID con backend (`p_` + sanitización emails) - [ ] Implementar datasource de señalización ### Pruebas APP↔Reloj - [ ] Llamada APP → Reloj - [ ] Llamada Reloj → APP - [ ] Llamadas grupales ### Producción - [ ] Token auth (backend genera tokens con AppSecret) - [ ] AppKeys separadas por entorno - [ ] Push/background iOS (PushKit + CallKit si necesario) --- ## 3 riesgos abiertos antes del pago ($8,835) | # | Riesgo | Estado | |---|---|---| | 1 | **Push/background iOS** — la doc no menciona FCM/APNs, CallKit ni ConnectionService. App cerrada = no recibe llamadas. Posible deal-breaker | ❌ Sin respuesta | | 2 | **GDPR sin DPA** — servidores UE pero sin DPA, sin control routing, datos de menores | ❌ Email enviado 01-04, sin respuesta | | 3 | **Chat sin spec** — $2,950 sin lista de features, "mira SeTracker2" | ❌ Sin spec | | + | **Encryption** — $2,950 pagados, cero documentación del módulo | ❌ Sin spec | --- ## Arquitectura confirmada ``` APP (Flutter + jc_sdk) ←→ Juphoon Cloud (solo media) ↕ API REST Backend SaveFamily ←→ Backend i365/SeTracker ←→ Smartwatch (firmware + jrtc_* C API) ↕ TCP plano ``` - El "Server" del protocolo TCP es **i365**, NO Juphoon - Juphoon Cloud **solo rutea audio/video** (media plane) - La señalización (quién llama a quién) va por el backend ## Naming conventions (protocolo TCP) | Tipo | Formato | Ejemplo | |---|---|---| | Watch userID | `w_` + IMEI | `w_000078932675810` | | Mobile userID | `p_` + APP account | `p_abc10086` | | Group room | `did` + `_group` | `0245423235_group` | | Single room | `did` + `_` + APP account | `0245423235_abc10086` | `@` y `.` se reemplazan por `_` en room numbers y userIDs. ## Documentación de referencia - Quickstart V1.1: `~/Downloads/Video call API_ Juphoon Flutter SDK quickstart V1.1.pdf` - TCP Protocol: `~/Downloads/Juphoon Video Call TCP Protocol.docx` - Connection process: `~/Downloads/video call connection process Rev2.docx` - Mutual dialing: `~/Downloads/video call mutual dialing process.docx` - Schematics: `~/Downloads/schematics _2025.03.26 (2)/` - pub.dev: https://pub.dev/packages/jc_sdk - Consola Juphoon: https://developer.juphoon.com