196 lines
9.0 KiB
Markdown
196 lines
9.0 KiB
Markdown
|
|
# 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_<cuenta>` + 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
|