# Plugin `plugins/google_cast` – Come funziona

Questo documento descrive **l’architettura** 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`
È l’entry 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 l’accesso alla sessione corrente.

#### In pratica gestisce:
- l’`Activity` attuale
- l’inizializzazione 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 all’utente 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 l’host 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 l’icona Cast
2. chiama `showCastDialog`
3. `FlutterCastFrameworkPlugin.kt` inoltra a `CastDialogLauncher.kt`
4. l’utente sceglie il device
5. `CastSessionHandler.kt` riceve `onSessionStarted`
6. il plugin manda a Flutter `onCastReady`

### B. Caricamento media
Quando l’utente 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 dell’architettura è la distinzione tra:
- **sessione Cast connessa**
- **media realmente caricato sulla queue**

Capire bene questa differenza evita gran parte dei bug visti in debug.
