19

Jan

I servizi di background audio in Windows Phone Mango: realizziamo un player multimediale – L’agent

Nel post precedente abbiamo realizzato l’applicazione con cui l’utente interagirà per utilizzare il nostro player multimediale. Abbiamo detto però che il cuore di un player in Mango non è l’applicazione (che si limita ad offrire la UI necessaria per l’interazione) bensì il background agent, che contiene tutta la logica di riproduzione.

Riprendiamo in mano perciò la soluzione realizzata nel progetto precedente e aggiungiamo un nuovo progetto, questa volta di tipo Windows Phone Audio Playback Agent. Esattamente come accadeva per i background agents tradizionali, verrà creato un nuovo progetto con un file (AudioPlayer.cs) che conterrà la classe AudioPlayer, la quale erediterà da AudioPlayerAgent.

Tale classe ci metterà a disposizione una serie di eventi da gestire. Nello specifico, quelli che rappresentano il cuore del nostro agent sono:

  • OnPlayStateChanged: questo evento viene scatenato ogni volta che lo stato della riproduzione cambia.
  • OnUserAction: questo evento viene scatenato ogni volta che l’utente interagisce con la riproduzione musicale, sia tramite l’applicazione che tramite la barra di controllo del volume.

Se ci ragionate, il primo evento è sempre una conseguenza del secondo. Dove sta la differenza perciò?

  • Nell’evento OnUserAction è bene gestire tutti i cambi di stato legati ad un diretto intervento dell’utente (la pressione del pulsante Pause o Next Track, ad esempio).
  • Nell’evento OnPlayStateChanged è bene gestire tutti i cambi di stato che invece non avvengono su esplicita richiesta dell’utente, ma in automatico (ad esempio, la traccia corrente è terminata e occorre riprodurre la successiva).

Qualche metodo di utilità

Prima di vedere la struttura vera e propria dell’agent, introduciamo alcun metodi di utilità che andremo ad usare.

LoadState e SaveState

Come spiegato nel post precedente, i background agent non vengono mantenuti in memoria ma vengono istanziati ad ogni esecuzione. Ecco perciò che abbiamo bisogno di salvare le informazioni necessarie per gestire lo stato della riproduzione nell’Isolated Storage (nel nostro caso, è sufficiente memorizzare il numero di traccia in riproduzione).

Eccone la definizione:

private void LoadState()
      {
      IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
      currentTrack = settings.Contains("CurrentTrack") ? Convert.ToInt32(settings["CurrentTrack"]) : -1;
      }

      private void SaveState()
      {
      IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
      if (settings.Contains("CurrentTrack"))
      settings["CurrentTrack"] = currentTrack;
      else
      settings.Add("CurrentTrack", currentTrack);
      }

Il metodo LoadState() non fa altro che recuperare dall’Isolated Storage il valore della proprietà identificata dalla chiave CurrentTrack. In caso non esista, questa viene impostata a –1, che è il valore che abbiamo deciso di usare come identificativo nel caso in cui non ci sono tracce correntemente in riproduzione.

Il metodo SaveSate() invece serve per salvare nella stessa proprietà CurrentTrack l’indice della traccia correntemente in riproduzione, che potrebbe essere cambiato rispetto all’inizio dell’esecuzione del metodo.

InitializeTracks

In questo metodo inizializziamo la playlist che conterrà le nostre tracce audio, sotto forma di collezione di tipo List. Andremo a chiamare questo metodo nel costruttore dell’agent. Per maggiori dettagli sulla classe AudioTrack, utilizzata per mantenere le informazioni sulle tracce audio, vi rimando alla prima parte di questo tutorial.

private void InitializeTracks()
      {
      tracks = new List
      {
      new AudioTrack(
      new Uri(
      "http://localhost/Mango/AlbertFarrington-ItsALongWayToTipperary1915.mp3",
      UriKind.Absolute),
      "It's a long way to Tipperary", "Albert Farrington", "Unknown album",
      null),
      new AudioTrack(
      new Uri(
      "http://localhost/Mango/BigBillBroonzy-BabyPleaseDontGo1.mp3",
      UriKind.Absolute),
      "Baby please don't go", "Big Bill Broonzy", "Unknown album", null),
      new AudioTrack(
      new Uri("http://localhost/Mango/Cocaine.mp3", UriKind.Absolute),
      "Cocaine",
      "Unknown artist", "Unknown album", null)
      };

      }

PlayNextTrack e PlayPreviousTrack

I metodi PlayNextTrack e PlayPreviousTrack contengono tutta la logica che gestisce la nostra playlist: sono a conoscenza del numero di tracce disponibili nella nostra applicazione e si occupano di gestire correttamente l’indice di riproduzione (facendo sì che, ad esempio, se l’utente preme il pulsante NextTrack quando è all’ultima traccia la playlist venga fatta ripartire dalla prima traccia).

private void PlayNextTrack(BackgroundAudioPlayer player)
      {
      currentTrack++;
      if (currentTrack > tracks.Count-1)
      currentTrack = 0;
      player.Track = tracks[currentTrack];
      player.Play();
      }

      private void PlayPreviousTrack(BackgroundAudioPlayer player)
      {
      currentTrack--;
      if (currentTrack < 0)
      currentTrack = tracks.Count-1;
      player.Track = tracks[currentTrack];
      player.Play();
      }

L’evento OnUserAction

Possiamo entrare ora nel vivo del backgound agent e lo facciamo analizzando la struttura dell’evento OnUserAction:

protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
      {
      LoadState();
      switch (action)
      {
      case UserAction.Pause:
      player.Pause();
      break;
      case UserAction.Play:
      {
      if (currentTrack == -1)
      PlayNextTrack(player);
      else
      player.Play();
      break;
      }
      case UserAction.SkipNext:
      {
      PlayNextTrack(player);
      break;
      }
      case UserAction.SkipPrevious:
      {
      PlayPreviousTrack(player);
      break;
      }
      }
      SaveState();
      NotifyComplete();
      }

L’evento ci restituisce le seguenti informazioni, alle quali possiamo accedere:

  • L’istanza corrente del player
  • La traccia correntemente in riproduzione
  • L’evento scatenato dall’utente
  • Un eventuale parametro opzionale che ci serve per identificare la richiesta

Notiamo innanzitutto i due metodi LoadState() e SaveState(), che vengono chiamati rispettivamente per primo e (quasi) per ultimo.  Dopodichè, con un statement switch andiamo a verificare il valore della proprietà di tipo UserAction, per capire che azione ha eseguito l’utente ed eseguire l’operazione corrispondente.

  • Se ha premuto Pausa, fermiamo la riproduzione.
  • Se ha premuto Play e non ci sono tracce in riproduzione, carichiamo la prima traccia; altrimenti, la facciamo semplicemente ripartire.
  • Se ha premuto NextTrack, carichiamo la traccia successiva.
  • Se ha premuto PreviousTrack, carichiamo la traccia precedente.

Esattamente come per i background agent tradizionali è presente il metodo NotifyComplete, che viene chiamato nel momento in cui l’agent ha terminato il suo compito. Come vedete, all’interno dello statement switch abbiamo gestito solamente i casi in cui lo stato della riproduzione è cambiato in seguito all’intervento dell’utente.

L’evento OnPlayStateChanged

La struttura di questo evento è la stessa vista in precedenza: l’unica differenza è che andremo a gestire l’unico caso previsto nella nostra applicazione in cui la riproduzione può cambiare senza intervento diretto dell’utente, ovvero quando termina la traccia corrente.

protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
      {
      LoadState();

      if (playState == PlayState.TrackEnded)
      PlayNextTrack(player);

      SaveState();

      NotifyComplete();
      }

Le informazioni alle quali possiamo accedere sono praticamente le stesse dell’evento OnUserAction, con la differenza che l’informazione sullo stato della riproduzione non è di tipo UserAction (dato che non c’è intervento dell’utente) ma è di tipo PlayState.

Qualche considerazione sul testing

Premendo F5 in Visual Studio lancerete l’applicazione Windows Phone e il relativo background agent: date avvio alla riproduzione, poi uscite dall’applicazione e, se avete implementato tutto correttamente, la musica continuerà ad essere riprodotta. Forse non tutti lo sanno, ma premendo F9 e F10 è possibile simulare nell’emulatore la pressione dei pulsanti per alzare o abbassare il volume: se lo facciamo durante la riproduzione, vedremo titolo e artista della traccia corrente visualizzati nella barra.

Per vedere il comportamento del background agent vi basta posizionare un breakpoint nell’evento OnUserAction: potrete notare come l’agent entrerà in azione indipendentemente da dove andrete a cambiare lo stato della riproduzione (applicazione vera e propria o barra di controllo del volume).

Come sempre trovate di seguito il link per scaricare il sorgente. Buon download!

by Il blog di Matteo Pagani on 1/19/2012
Post archive