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

432 lines
11 KiB
Markdown
Raw Permalink 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` Come funziona
Questo documento descrive **larchitettura** e il **flusso di funzionamento** del plugin `plugins/google_cast` del progetto, sulla base della struttura riportata e dei file analizzati durante il debug.
---
## 1. Obiettivo del plugin
Il plugin `google_cast` fa da ponte tra Flutter/Dart e il layer nativo Android per gestire:
- **Google Cast standard** (Chromecast / Android TV con receiver standard)
- **Custom Receiver** (receiver web / CAF)
- il dialog nativo di selezione device
- comandi media (play, pause, seek, queue, volume, mute)
- eventi di ritorno verso Flutter (cast connesso, cast fermato, cambio item queue, errori, ecc.)
In pratica:
- **Flutter** decide *quando* connettersi e *quale media* mandare
- **il plugin Android** parla con le API Google Cast
- **il receiver** (standard o custom) riproduce davvero i contenuti sul TV/device Cast
---
## 2. Struttura del plugin
```text
plugins/google_cast
|-- android
| |-- build.gradle.kts
| |-- settings.gradle.kts
| `-- src/main/kotlin/com/aves/google_cast
| |-- CastActivityProvider.kt
| |-- CastOptionsProvider.kt
| |-- FlutterCastFrameworkPlugin.kt
| `-- cast
| |-- CastContextManager.kt
| |-- CastCustomReceiver.kt
| |-- CastDialogLauncher.kt
| |-- CastMediaClient.kt
| |-- CastQueueManager.kt
| `-- CastSessionHandler.kt
|-- lib
| `-- google_cast.dart
`-- pubspec.yaml
```
---
## 3. Ruolo dei file principali
### `lib/google_cast.dart`
È lentry point Dart del package/plugin.
In molti plugin Flutter questo file fa una o più di queste cose:
- espone API pubbliche al codice Dart
- definisce wrapper leggeri verso il `MethodChannel`
- centralizza eventuali modelli / helper lato Flutter
Nel tuo progetto, il vero orchestratore lato app non è qui ma nei controller di app (`cast_controller.dart`, `google_cast_controller.dart`, `custom_cast_controller.dart`).
---
### `android/src/main/kotlin/com/aves/google_cast/FlutterCastFrameworkPlugin.kt`
È il file **principale del plugin Flutter lato Android**.
#### Responsabilità
- registra il `MethodChannel('google_cast')`
- riceve le chiamate da Flutter (`showCastDialog`, `loadQueue`, `play`, `pause`, `seekTo`, `getVolume`, ecc.)
- smista ogni metodo verso i componenti modulari del package `cast/`
- aggancia il plugin al ciclo di vita dell`Activity`
- inizializza i componenti principali del Cast:
- `CastContextManager`
- `CastSessionHandler`
- `CastMediaClient`
- `CastQueueManager`
- `CastDialogLauncher`
- `CastCustomReceiver`
È il **router centrale** del plugin.
---
### `CastContextManager.kt`
Gestisce il **CastContext** e laccesso alla sessione corrente.
#### In pratica gestisce:
- l`Activity` attuale
- linizializzazione di `CastContext`
- il recupero della sessione Cast attiva
- il recupero del `RemoteMediaClient`
#### Metodi tipici/attesi
- `attachActivity(activity)`
- `detachActivity()`
- `ensureCastContext()`
- `getSession()`
- `getClient()`
È il punto base da cui gli altri oggetti ottengono la sessione Cast corrente e il media client corrente.
---
### `CastSessionHandler.kt`
Gestisce gli **eventi di sessione Cast**.
#### Di solito si occupa di:
- `onSessionStarting`
- `onSessionStarted`
- `onSessionResumed`
- `onSessionEnded`
- invio eventi verso Flutter, ad esempio:
- `onCastReady`
- `onCastStopped`
Nel tuo flusso attuale, `onCastReady` viene usato per segnalare una **sessione connessa**, ma **non necessariamente** un media già caricato.
---
### `CastDialogLauncher.kt`
Si occupa di aprire il dialog nativo di scelta dispositivo Cast.
#### Tipicamente fa:
- crea/configura un `MediaRouteButton`
- invoca il bottone o mostra il picker Cast
- consente allutente di scegliere il device
Quando Flutter chiama `showCastDialog`, il plugin passa il comando a `CastDialogLauncher`, che mostra il picker dispositivo.
---
### `CastMediaClient.kt`
Gestisce i **comandi media** e il **callback del `RemoteMediaClient`**.
#### Responsabilità principali
- `play()`
- `pause()`
- `seek(position)`
- `getMediaStatus()`
- `getVolume()`
- `setVolume(v)`
- `setMute(m)`
- registrazione del callback sul `RemoteMediaClient`
#### Perché è cruciale
Fa due cose fondamentali:
1. **Comandi verso Google Cast**
- play / pause
- seek
- volume / mute
2. **Eventi da Google Cast verso Flutter**
- `onQueueIndexChanged`
- `onVideoFinished`
- `onCastError`
È fondamentale per slideshow e sincronizzazione UI.
---
### `CastQueueManager.kt`
Gestisce la **queue dei media** Cast.
#### Responsabilità tipiche
- `loadQueue(items, startIndex)`
- `next()`
- `prev()`
- costruzione di `MediaQueueItem`
- associazione metadati per item (titolo, mimeType, flags foto/video, customData)
È il file che determina come una lista Flutter di media diventa una vera queue Google Cast.
---
### `CastCustomReceiver.kt`
Gestisce il **Custom Receiver CAF / Web Receiver**.
#### Responsabilità
- avvio del receiver custom con `receiverUrl`
- invio messaggi JSON al receiver web
- ricezione/gestione del namespace custom (se implementato)
Nel tuo progetto, Flutter costruisce un URL tipo:
```text
https://.../cast_receiver/receiver.html?ws=...
```
e poi chiama `startCustomReceiver`.
---
### `CastActivityProvider.kt`
Aiuta a dare accesso all`Activity` giusta o a integrare il plugin con lhost Android.
---
### `CastOptionsProvider.kt`
È il provider di configurazione Google Cast lato Android.
#### Tipicamente definisce:
- receiver application ID di default
- eventuali opzioni del framework Cast
---
## 4. Flusso end-to-end: Google Cast standard
### A. Connessione del device
1. Flutter preme licona Cast
2. chiama `showCastDialog`
3. `FlutterCastFrameworkPlugin.kt` inoltra a `CastDialogLauncher.kt`
4. lutente sceglie il device
5. `CastSessionHandler.kt` riceve `onSessionStarted`
6. il plugin manda a Flutter `onCastReady`
### B. Caricamento media
Quando lutente preme:
- cast manuale
- fullscreen cast
- slideshow
Flutter costruisce una queue e chiama `loadQueue`.
Il plugin passa il tutto a `CastQueueManager.loadQueue(...)`, che crea i `MediaQueueItem` e li carica nel `RemoteMediaClient`.
### C. Controlli playback
Dai controller Flutter partono metodi come:
- `playCast()`
- `pauseCast()`
- `seekCast()`
- `nextManual()`
- `prevManual()`
- `setCastVolume()`
- `toggleMuteCast()`
Questi passano dal `MethodChannel`, arrivano a `FlutterCastFrameworkPlugin.kt` e poi vengono gestiti da:
- `CastMediaClient.kt`
- `CastQueueManager.kt`
### D. Eventi di ritorno verso Flutter
Dal native tornano eventi come:
- `onCastReady`
- `onCastStopped`
- `onQueueIndexChanged`
- `onVideoFinished`
- `onCastError`
Questi aggiornano in Flutter:
- icona Cast
- indice corrente
- stato play/pause
- posizione/durata
- slideshow timer foto/video
---
## 5. Flusso end-to-end: Custom Receiver
### A. Connessione/sessione
Flutter chiama:
- `showCastDialog`
- `startCustomReceiver(appId, receiverUrl)`
Il plugin passa a `CastCustomReceiver.kt`, che avvia il receiver web.
### B. Caricamento media
Flutter non usa `loadQueue` standard, ma manda messaggi custom JSON al receiver, ad esempio:
- `LOAD_QUEUE`
- `PLAY`
- `PAUSE`
- `SEEK`
- `NEXT`
- `PREV`
- `SET_VOLUME`
- `SET_MUTE`
- `GET_VOLUME`
- `GET_MEDIA_STATUS`
### C. Eventi verso Flutter
Il receiver web risponde con messaggi come:
- `MEDIA_STATUS`
- `VOLUME_STATUS`
- `CAST_READY`
- `CAST_STOPPED`
che il `CustomCastController` usa per aggiornare la UI Flutter.
---
## 6. Differenza tra Google Cast standard e Custom
### Google Cast standard
- il volume è nativo via `CastSession`
- la queue è gestita dal framework Cast
- il passaggio video → next è naturale se la queue è corretta
- `RemoteMediaClient.Callback` è fondamentale
### Custom Receiver
- il volume ha senso **solo se il receiver web lo implementa davvero**
- le foto/video vengono gestiti dalla tua logica JS/CAF
- gli eventi stato/volume dipendono dal receiver
- è più flessibile ma più facile da rompere
---
## 7. Punto delicato emerso nel debug
Durante il refactor si è visto che una distinzione è fondamentale:
## **Connettere il device != aver già caricato un media**
Quindi il comportamento corretto è:
- `onCastReady` = sessione pronta
- **NO polling immediato** se la queue è ancora vuota
- il polling deve partire solo dopo `loadQueue`
Altrimenti il plugin rischia di generare:
- `Invalid Request`
- `MediaQueue error`
- `IDLE_REASON_ERROR`
---
## 8. Responsabilità lato Flutter (fuori dal plugin ma essenziali)
Anche se non sono dentro `plugins/google_cast`, lato app ci sono controller importanti:
- `cast_controller.dart` → controller unificato backend
- `google_cast_controller.dart` → orchestrazione Google standard
- `custom_cast_controller.dart` → orchestrazione custom receiver
Questi file decidono:
- quando aprire il dialog Cast
- quando connettere il device
- quando mandare davvero una queue
- quando mostrare controlli / slideshow / fullscreen viewer
Il plugin Android da solo non basta: è il layer Dart che decide il flusso UX.
---
## 9. Mappa rapida responsabilità
### Flutter / UI
- pulsante Cast
- selection mode
- slideshow/manual/fullscreen
- bottom sheet controlli
### `cast_controller.dart`
- facade unificata Google / Custom / DLNA
### `google_cast_controller.dart`
- queue standard Cast
- slideshow foto/video lato Flutter
- polling media/volume
### `custom_cast_controller.dart`
- invio messaggi JSON al receiver custom
- stato custom receiver
### `FlutterCastFrameworkPlugin.kt`
- router MethodChannel lato Android
### `CastContextManager.kt`
- CastContext + session + media client
### `CastSessionHandler.kt`
- session lifecycle → eventi verso Flutter
### `CastQueueManager.kt`
- queue standard Google Cast
### `CastMediaClient.kt`
- play/pause/seek/volume + callback eventi media
### `CastCustomReceiver.kt`
- custom receiver CAF/web
### `CastDialogLauncher.kt`
- dialog selezione device
---
## 10. Se devi debuggare il plugin in futuro
Ordine consigliato per capire dove si rompe qualcosa:
### Se non si connette il device
- `FlutterCastFrameworkPlugin.kt`
- `CastDialogLauncher.kt`
- `CastSessionHandler.kt`
- `CastContextManager.kt`
### Se si connette ma non parte il media
- `CastQueueManager.kt`
- `CastMediaClient.kt`
- `google_cast_controller.dart`
### Se slideshow si ferma o salta male foto/video
- `google_cast_controller.dart`
- `CastMediaClient.kt` (callback `onQueueIndexChanged`)
- eventualmente `CastQueueManager.kt`
### Se il Custom receiver ha problemi UI / progress / volume
- `CastCustomReceiver.kt`
- receiver web (`receiver.html`, JS)
- `custom_cast_controller.dart`
### Se il volume non si aggiorna
- `CastMediaClient.kt`
- `google_cast_controller.dart`
- widget controlli Flutter (`cast_controls_sheet.dart`, fullscreen viewer)
---
## 11. Conclusione
Il plugin `google_cast` del progetto è organizzato in modo modulare e separa bene le responsabilità:
- il file principale `FlutterCastFrameworkPlugin.kt` riceve i comandi Flutter
- il package `cast/` contiene i moduli Android specializzati
- i controller Flutter coordinano UX, queue e sincronizzazione stato
Il punto più delicato dellarchitettura è la distinzione tra:
- **sessione Cast connessa**
- **media realmente caricato sulla queue**
Capire bene questa differenza evita gran parte dei bug visti in debug.