aves_mio0.31/ARCHITETTURA_PLUGIN_google_cast_pratica.md
2026-07-18 13:39:22 +02:00

624 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Plugin `plugins/google_cast` Guida pratica / Debug operativo
Questa guida è la versione **pratica** del documento di architettura del plugin `google_cast`.
Lobiettivo è aiutarti a capire **rapidamente**:
- quale parte del flusso è coinvolta
- quale file toccare
- dove guardare quando qualcosa non funziona
- come distinguere i problemi **Google Cast standard** dai problemi **Custom Receiver**
---
# 1. Mappa mentale rapida
## Flusso generale
```text
Flutter UI
cast_controller.dart
┌───────────────────────────────┬───────────────────────────────┐
│ GoogleCastController │ CustomCastController │
│ (Google Cast standard) │ (Custom Receiver CAF/web) │
└───────────────┬───────────────┴───────────────┬───────────────┘
↓ ↓
MethodChannel('google_cast') MethodChannel('google_cast')
↓ ↓
FlutterCastFrameworkPlugin.kt (Android)
┌──────────────┬───────────────┬──────────────┬──────────────┐
│ Context │ Session │ MediaClient │ QueueManager │
│ Manager │ Handler │ │ │
└──────────────┴───────────────┴──────────────┴──────────────┘
Google Cast framework / Receiver
```
---
# 2. File principali: a cosa servono in pratica
## Lato Flutter / App
### `lib/widgets/cast/cast_controller.dart`
**È la facciata unificata**.
Usalo per capire:
- quale backend è attivo (`google`, `custom`, `dlna`)
- dove finiscono i comandi UI
- come vengono sincronizzati i notifier condivisi:
- indice corrente
- stato play/pause
- posizione/durata
- volume/mute
### Se hai un bug tipo:
- il pulsante UI chiama il backend sbagliato
- i notifier non si aggiornano
- la cast bar chiama il backend sbagliato
👉 il primo file da guardare è **questo**.
---
### `lib/widgets/cast/google_cast_controller.dart`
**È il cervello Dart del Google Cast standard**.
Qui trovi:
- connessione del flusso Google Cast
- caricamento queue standard
- slideshow lato Flutter
- polling media/volume
- sync verso fullscreen viewer / controlli
### Se hai un bug tipo:
- slideshow Google si ferma
- foto/video si comportano male
- seek cambia sulla TV ma non sul telefono
- volume non si aggiorna nella UI
- `onCastReady` genera comportamenti strani
👉 guarda **questo file**.
---
### `lib/widgets/cast/custom_cast_controller.dart`
**È il cervello Dart del Custom Receiver**.
Qui trovi:
- avvio sessione/receiver custom
- invio messaggi JSON al receiver web
- polling custom (`MEDIA_STATUS`, `VOLUME_STATUS`)
- slideshow custom
- comandi `LOAD_QUEUE`, `NEXT`, `PLAY`, `PAUSE`, ecc.
### Se hai un bug tipo:
- Custom connect funziona ma la TV mostra male il contenuto
- overlay/progress duplicati
- volume custom non cambia davvero il video
- il receiver risponde ma Flutter non si aggiorna
👉 guarda **questo file** + il receiver web.
---
### `lib/widgets/collection/app_bar/cast/cast_icon_button.dart`
Gestisce il **tap sullicona Cast** nella app bar.
Qui si decide se il tap deve fare:
- solo **connessione device**
- oppure connessione + media
### Se hai un bug tipo:
- scegli Google ma sembra partire Custom
- il cast parte subito quando scegli il device
- il device si connette ma la UX è sbagliata
👉 guarda **questo file**.
---
### `lib/widgets/collection/app_bar/cast/cast_bar_inline.dart`
È la **seconda barra orizzontale** con:
- cast manuale
- fullscreen cast
- slideshow
- prev/next
- stop slideshow
- stop cast
### Se hai un bug tipo:
- parte con gli item sbagliati
- usa una selezione vecchia
- startIndex invalido
- overflow orizzontale della barra
👉 guarda **questo file**.
---
### `lib/widgets/collection/app_bar/cast/cast_controls_sheet.dart`
È il **bottom sheet dei controlli**.
Qui trovi:
- seek
- prev/play-pause/next
- volume/mute
- stop slideshow
- stop cast
### Se hai un bug tipo:
- il volume non compare
- il volume compare ma non si aggiorna
- i controlli interagiscono col backend sbagliato
- la UI chiama `controller.google` / `controller.custom` direttamente
👉 guarda **questo file**.
---
### `lib/widgets/viewer/fullscreen_cast_viewer.dart`
È il viewer fullscreen locale sincronizzato col Cast.
### Se hai un bug tipo:
- il video sulla TV cambia seek ma il telefono no
- il fullscreen non segue lindice Cast
- il video locale e il Cast non sono allineati
👉 guarda **questo file**.
---
## Lato plugin Android
### `plugins/google_cast/android/src/main/kotlin/com/aves/google_cast/FlutterCastFrameworkPlugin.kt`
**Router centrale del plugin**.
Riceve tutto dal `MethodChannel('google_cast')`.
### Se hai un bug tipo:
- Flutter chiama un metodo ma sembra non arrivare al native
- un comando non viene instradato bene
- un handler non viene più registrato
👉 guarda **questo file**.
---
### `CastContextManager.kt`
Gestisce:
- `CastContext`
- `Activity`
- sessione Cast corrente
- `RemoteMediaClient`
### Se hai un bug tipo:
- sessione non trovata
- device connesso ma client nullo
- callback non agganciata al client corretto
👉 guarda **questo file**.
---
### `CastSessionHandler.kt`
Gestisce gli eventi di sessione:
- `onSessionStarted`
- `onSessionResumed`
- `onSessionEnded`
- `onCastReady`
- `onCastStopped`
### Se hai un bug tipo:
- il device si connette ma Flutter non sa che è pronto
- la seconda barra non compare
- la sessione finisce ma Flutter resta “connected”
👉 guarda **questo file**.
---
### `CastDialogLauncher.kt`
Mostra il dialog nativo di selezione device.
### Se hai un bug tipo:
- licona Cast non apre il picker
- il picker si apre ma non seleziona device
👉 guarda **questo file**.
---
### `CastMediaClient.kt`
Gestisce:
- play / pause / seek
- get media status
- volume / mute
- callback `RemoteMediaClient.Callback`
### Se hai un bug tipo:
- `onQueueIndexChanged` non arriva più a Flutter
- `onVideoFinished` non arriva
- `Invalid Request`
- volume nativo Google non si aggiorna
👉 guarda **questo file**.
---
### `CastQueueManager.kt`
Gestisce:
- `loadQueue(items, startIndex)`
- `next()`
- `prev()`
- costruzione `MediaQueueItem`
- metadati (`index`, `isVideo`, mime, title)
### Se hai un bug tipo:
- `Invalid startIndex`
- queue rotta o item sbagliati
- Google Cast non va al prossimo item come dovrebbe
👉 guarda **questo file**.
---
### `CastCustomReceiver.kt`
Gestisce lavvio del custom receiver web/CAF.
### Se hai un bug tipo:
- il receiver custom non parte
- parte ma non riceve messaggi
- la sessione custom sembra connettersi ma non carica nulla
👉 guarda **questo file**.
---
# 3. Mappa dei comandi principali
## Comandi Flutter → plugin Android
### Connessione
- `showCastDialog`
- `startCustomReceiver`
### Queue / navigazione
- `loadQueue`
- `next`
- `prev`
### Media
- `play`
- `pause`
- `seekTo`
- `getMediaStatus`
### Volume
- `getVolume`
- `setVolume`
- `setMute`
### Stop
- `stopCast`
---
# 4. Mappa degli eventi di ritorno
## Google Cast standard → Flutter
- `onCastReady`
- `onCastStopped`
- `onQueueIndexChanged`
- `onVideoFinished`
- `onCastError`
## Custom Receiver → Flutter
Di solito via messaggi custom, per esempio:
- `CAST_READY`
- `CAST_STOPPED`
- `MEDIA_STATUS`
- `VOLUME_STATUS`
---
# 5. Troubleshooting rapido
## Problema: seleziono il device e subito compaiono
- `Invalid Request`
- `MediaQueue error 2001`
- `IDLE_REASON_ERROR`
### Causa probabile
Il polling media parte **troppo presto**, cioè già su `onCastReady` quando non esiste ancora una queue/media caricata.
### Dove guardare
- `google_cast_controller.dart`
- `CastMediaClient.kt`
### Fix tipico
- non avviare `startMediaPolling()` dentro `onCastReady`
- far partire il polling solo dopo `loadQueue`
- lato native, ignorare gli stati in cui `mediaInfo == null`
---
## Problema: slideshow Google fa prima e seconda foto, poi si ferma
### Causa probabile
Flutter non riceve più il cambio item queue, quindi il timer slideshow non viene riarmato.
### Dove guardare
- `google_cast_controller.dart`
- `CastMediaClient.kt`
- eventualmente `CastQueueManager.kt`
### Fix tipico
- ripristinare la callback `RemoteMediaClient.Callback`
- reinviare a Flutter `onQueueIndexChanged(index, isVideo)`
- in Dart riarmare il timer solo per le foto
---
## Problema: in slideshow un video viene saltato dopo 5 secondi
### Causa probabile
Il timer slideshow delle foto parte anche sui video.
### Dove guardare
- `google_cast_controller.dart`
- `custom_cast_controller.dart`
### Comportamento corretto
- **foto** → timer
- **video** → nessun timer, deve finire da solo e poi passare al successivo
---
## Problema: in presentazione manuale/fullscreen le foto avanzano da sole
### Causa probabile
La logica slideshow è rimasta attiva quando non dovrebbe.
### Dove guardare
- `google_cast_controller.dart`
- `custom_cast_controller.dart`
- `cast_bar_inline.dart`
### Comportamento corretto
- **manuale/fullscreen**: le foto restano ferme fino a un comando manuale
- i video invece vanno al successivo a fine riproduzione
---
## Problema: `Invalid startIndex: X`
### Causa probabile
Stai caricando una queue con un indice iniziale più grande della lunghezza della lista.
### Dove guardare
- `cast_bar_inline.dart`
- `google_cast_controller.dart`
- `custom_cast_controller.dart`
- `CastQueueManager.kt`
### Fix tipico
- usare `startIndex: 0` nei comandi manuali della cast bar
- oppure proteggere con una funzione tipo `_safeIndexOrKeep(...)`
---
## Problema: seek sul video cambia sulla TV ma non sul telefono
### Causa probabile
Il telefono usa un player locale non sincronizzato con `positionNotifier` / `isPlayingNotifier` del Cast.
### Dove guardare
- `google_cast_controller.dart`
- `fullscreen_cast_viewer.dart`
### Fix tipico
- in `seekCast()` aggiornare subito `positionNotifier`
- nel `VideoWidget` locale ascoltare `positionNotifier` e `isPlayingNotifier`
---
## Problema: il volume compare ma la barra non si aggiorna
### Causa probabile
Il comando volume parte, ma il notifier locale non viene riallineato con lo stato reale del device.
### Dove guardare
- `google_cast_controller.dart`
- `cast_controls_sheet.dart`
- `fullscreen_cast_viewer.dart`
- `CastMediaClient.kt`
### Fix tipico
- dopo `setVolume` e `setMute`, chiamare `refreshVolumeStatus()`
- lato UI, separare `onChanged` e `onChangeEnd` dello slider
---
## Problema: scegli Google ma sembra partire Custom
### Causa probabile
Il backend attivo del controller unificato non è allineato al backend scelto nella UI.
### Dove guardare
- `cast_icon_button.dart`
- `cast_controller.dart`
- `custom_cast_controller.dart`
- `google_cast_controller.dart`
### Fix tipico
- rendere licona Cast “connect-only”
- impostare esplicitamente il backend attivo (`_backend`) nel controller unificato
- far partire la queue solo dai pulsanti della cast bar
---
## Problema: in selection mode la app bar sembra toccare anche le foto sotto
### Causa probabile
La barra non sta assorbendo correttamente i tocchi nellarea header.
### Dove guardare
- `collection_app_bar.dart`
- `aves_app_bar.dart`
- `collection_page.dart`
### Fix tipico
- ripristinare il wrapper che assorbe i tap nella barra (`AInkResponse`)
- verificare il comportamento `pinned`
- verificare luso corretto di `appBarHeightNotifier`
---
# 6. Regole pratiche di debug
## Regola 1
### Se il problema è “UI Flutter non aggiornata”
Guarda prima i controller Dart:
- `cast_controller.dart`
- `google_cast_controller.dart`
- `custom_cast_controller.dart`
- `fullscreen_cast_viewer.dart`
---
## Regola 2
### Se il problema è “la TV/device fa una cosa diversa da Flutter”
Guarda:
- `CastMediaClient.kt`
- `CastQueueManager.kt`
- receiver custom JS/HTML (se custom)
---
## Regola 3
### Se il problema avviene subito dopo la connessione device
Guarda:
- `CastSessionHandler.kt`
- `google_cast_controller.dart`
- `CastMediaClient.kt`
Il sospetto classico è la distinzione:
- connessione sessione
- queue realmente caricata
---
## Regola 4
### Se il problema riguarda slideshow foto/video
Guarda prima il controller Dart, poi il native.
Ordine consigliato:
1. `google_cast_controller.dart`
2. `custom_cast_controller.dart`
3. `CastMediaClient.kt`
4. `CastQueueManager.kt`
---
## Regola 5
### Se il problema riguarda il Custom Receiver
Non fermarti al controller Flutter.
Devi guardare anche:
- `CastCustomReceiver.kt`
- `receiver.html`
- eventuale JS del receiver
Perché molti bug Custom sono **receiver-side**, non plugin-side.
---
# 7. Differenza pratica Google vs Custom (da tenere a mente)
## Google Cast standard
### Più affidabile quando:
- usi queue nativa
- usi volume Cast nativo
- ti affidi a `RemoteMediaClient.Callback`
## Custom Receiver
### Più flessibile quando:
- vuoi UI custom sul TV
- vuoi logica media personalizzata
- vuoi controllare il player HTML5 direttamente
### Ma più fragile quando:
- volume/mute non sono implementati davvero
- il DOM del receiver non viene pulito bene
- i messaggi receiver/Flutter non sono perfettamente allineati
---
# 8. Checklist rapida: dove toccare per ogni categoria
## Connessione device
- `cast_icon_button.dart`
- `FlutterCastFrameworkPlugin.kt`
- `CastDialogLauncher.kt`
- `CastSessionHandler.kt`
## Queue / media non partono
- `cast_bar_inline.dart`
- `google_cast_controller.dart`
- `CastQueueManager.kt`
## Slideshow foto/video
- `google_cast_controller.dart`
- `custom_cast_controller.dart`
- `CastMediaClient.kt`
## Volume Google
- `google_cast_controller.dart`
- `cast_controls_sheet.dart`
- `fullscreen_cast_viewer.dart`
- `CastMediaClient.kt`
## Volume Custom
- `custom_cast_controller.dart`
- `CastCustomReceiver.kt`
- `receiver.html` / JS
## Bug UI selection / app bar
- `collection_app_bar.dart`
- `aves_app_bar.dart`
- `collection_page.dart`
---
# 9. Conclusione pratica
Se devi pensare al plugin in modo operativo, ricordati questa formula:
## **Flutter decide lUX → il plugin Android parla con Cast → il receiver riproduce davvero**
Quindi ogni bug va classificato subito in una di queste tre famiglie:
### A. Bug UX / Flutter
- notifier
- selection
- fullscreen viewer
- cast bar
- app bar
### B. Bug plugin Android
- sessione
- queue
- callback media
- volume nativo
### C. Bug receiver
- volume custom
- progress bar duplicate
- media status custom
- UI TV-side
Capire **subito** in quale famiglia rientra il bug fa risparmiare tantissimo tempo di debug.