using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Linq; using Xamarin.Forms; using Aurora.Services; using Aurora.Proto.General; using Aurora.Proto.Party; using Aurora.Proto.Events; using Aurora.Services.ClientService; using Aurora.Services.ClientService.Events; using Aurora.Services.PlayerService; using Aurora.Services.EventManager; using Aurora.Models.Media; using Aurora.Design.Views.Party.NewPartyDialog; namespace Aurora.Design.Views.Party { //TODO refactor enum PartyState { SelectingHost, InParty, Hosting, Connecting, } public class PartyViewModel : BaseViewModel { private PartyState _state; private string _hostname = ""; private ObservableCollection _members; private ObservableCollection _queue; private BaseMedia _selectedMedia; private ClientService _client; public PartyViewModel() { _members = new ObservableCollection(); _queue = new ObservableCollection(); SetState(PartyState.SelectingHost); PlayCommand = new Command(OnDoubleClickExecute, CanDoubleClickExecute); _client = ClientService.Instance; _client.OnMediaPaused += this.OnMediaPaused; _client.OnMediaResumed += this.OnMediaResumed; _client.OnNewMediaPlaying += this.OnNewMediaPlaying; _client.OnPartyMemberJoined += this.OnPartyMemberJoined; _client.OnPartyMemberLeft += this.OnPartyMemberLeft; } ~PartyViewModel() { //Task.Run(ServerService.Instance.Stop); } #region Properties /// /// Publc property for the members list /// /// public ObservableCollection Members { get { return _members; } set { SetProperty(ref _members, value); } } /// /// Public property for queue item source /// /// public ObservableCollection Queue { get { return _queue; } set { if (value != _queue) { SetProperty(ref _queue, value); } } } /// /// Public property for the currently selected song. /// /// public BaseMedia SelectedSong { get { return _selectedMedia; } set { SetProperty(ref _selectedMedia, value); } } /// /// Public property for playing media /// /// public Command PlayCommand { get; private set; } #endregion Properties #region Events /// /// Called by framework when view becomes active /// /// public override async Task OnActive() { if (this._state == PartyState.Hosting || this._state == PartyState.InParty) { await _client.GetEvents().ConfigureAwait(false); } else { //Open host selection modal var modalResult = await this.ShowModal(typeof(NewPartyDialog.NewPartyDialog), typeof(NewPartyDialogViewModel)); if (modalResult is ConnectionDetails) { ConnectionDetails details = modalResult as ConnectionDetails; _hostname = details.HostName; switch (details.ConnectionType) { case ConnectionType.Host: { OnHostExecute(); break; } case ConnectionType.Join: { OnJoinExecute(); break; } } } } } /// /// Called by framework when view becomes inactive /// /// public override Task OnInactive() { _client.StopEvents(); return Task.FromResult(null); } /// /// Remote media paused event /// /// /// public void OnMediaPaused(object sender, MediaPausedEventArgs args) { StopPlaying(); } /// /// Remote playing new media event /// /// /// public void OnNewMediaPlaying(object sender, NewMediaPlayingEventArgs args) { PlayFromBeginning(GetMediaFromQueue(args.Event.Media.Id)); } /// /// Remote resumed playing event /// /// /// public void OnMediaResumed(object sender, MediaResumedEventArgs args) { PlayResume(); } /// /// Member joined party event /// /// /// public void OnPartyMemberJoined(object sender, PartyMemberJoinedEventArgs args) { PartyMember member = new PartyMember { UserName = args.Event.Member.UserName, Id = args.Event.Member.Id, IpAddress = args.Event.Member.IpAddress, Port = args.Event.Member.Port }; Members.Add(member); } /// /// Member left party event /// /// /// public void OnPartyMemberLeft(object sender, PartyMemberLeftEventArgs args) { var found = Members.Where(x => x.Id == args.Event.Member.Id); foreach (PartyMember member in found) { _members.Remove(member); } } #endregion Events #region Commands private async void OnJoinExecute() { SetState(PartyState.Connecting); _client.Start(_hostname, SettingsService.Instance.DefaultPort.ToString()); await JoinParty(false); //TODO add cancellation token try { SetState(PartyState.InParty); await _client.GetEvents().ConfigureAwait(true); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanJoinExecute() { return true; } private async void OnHostExecute() { //Change state SetState(PartyState.Connecting); ServerService.Instance.Start(); string localHost = ServerService.GetLocalIPAddress(); _client.IsHost = true; _client.Start(localHost, SettingsService.Instance.DefaultPort.ToString()); await JoinParty(true); //TODO add cancellation token try { SetState(PartyState.Hosting); await _client.GetEvents().ConfigureAwait(true); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Exception occurred while receiviing events: ", ex.Message); } } private bool CanHostExecute() { return true; } public override void OnPlayButtonExecute() { if (base.IsPlaying()) { //Fire play stopped event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaPausedEvent mediaPaused = new MediaPausedEvent(); EventManager.Instance.FireEvent(new BaseEvent() { MediaPausedEvent = mediaPaused }); } else { //Fire play resume event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaResumedEvent mediaResumed = new MediaResumedEvent(); EventManager.Instance.FireEvent(new BaseEvent() { MediaResumedEvent = mediaResumed }); } } public override bool CanPlayButtonExecute() { return this._state == PartyState.Hosting; } public override bool CanNextButtonExecute() { return this._state == PartyState.Hosting; } public override bool CanPreviousButtonExecute() { return this._state == PartyState.Hosting; } /// /// On double click execute, fire media playing event /// public void OnDoubleClickExecute() { //Fire Playing event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent() { Media = new RemoteMediaData() { Id = _selectedMedia.Id, Title = meta.Title, Artist = meta.Artist, Album = meta.Album, } }; EventManager.Instance.FireEvent(new BaseEvent() { NewMediaPlayingEvent = mediaPlaying }); } public bool CanDoubleClickExecute() { return this._state == PartyState.Hosting; } #endregion Commands #region Private Methods /// /// Join the remote party. /// /// private async Task JoinParty(bool asHost) { try { JoinPartyResponse resp = await _client.RemotePartyClient.JoinPartyAsync(new JoinPartyRequest { UserName = SettingsService.Instance.Username, }); SettingsService.Instance.ClientId = resp.ClientId; RefreshMembers(); //Subscribe to events await SubscribeToEvents(); Queue.Clear(); QueueResponse queueResponse = _client.RemotePartyClient.GetQueue(new Empty()); //Convert received data to remote audio models foreach (RemoteMediaData data in queueResponse.MediaList) { //Assign received metadata (since this can't be aquired from a file) AudioMetadata meta = new AudioMetadata(); meta.Title = data.Title; meta.Album = data.Album; meta.Artist = data.Artist; meta.Duration = data.Duration; RemoteAudio remote = new RemoteAudio(data.Id, asHost, meta, _client.RemotePlaybackClient, _client.RemoteSyncClient); Queue.Add(remote); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Error subscribing to events: " + ex.Message); } } private async Task LeaveParty() { //Stop receiving events _client.StopEvents(); //Unsubscribe await UnsubscribeFromEvents(); //Leave party LeavePartyRequest leaveReq = new LeavePartyRequest(); await _client.RemotePartyClient.LeavePartyAsync(leaveReq); } private async Task SubscribeToEvents() { SubscribeRequest req = new SubscribeRequest(); req.EventTypes.Add(EventType.PartyMemberJoined); req.EventTypes.Add(EventType.PartyMemberLeft); req.EventTypes.Add(EventType.MediaPlaying); req.EventTypes.Add(EventType.MediaStopped); if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) { req.ClientId = SettingsService.Instance.ClientId; } System.Diagnostics.Debug.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); await _client.RemoteEventClient.SubscribeToEventsAsync(req); } private async Task UnsubscribeFromEvents() { UnsubscribeAllRequest unsubscribeReq = new UnsubscribeAllRequest(); await _client.RemoteEventClient.UnsubscribeFromAllAsync(unsubscribeReq); } /// /// Refresh members list. /// private void RefreshMembers() { Members.Clear(); MembersResponse response = _client.RemotePartyClient.GetPartyMembers(new Empty()); //Add members foreach (PartyMember member in response.Members) { Members.Add(member); } } private void SetState(PartyState state) { _state = state; OnPropertyChanged("IsSelectingHost"); OnPropertyChanged("IsNotSelectingHost"); } private BaseMedia GetMediaFromQueue(string Id) { if (_queue.Any((BaseMedia media) => media.Id == Id)) { BaseMedia media = _queue.First((BaseMedia med) => med.Id == Id); return media; } else { return null; } } private void PlayFromBeginning(BaseMedia args) { base.ChangePlayerState(args, Main.PlayAction.Play); } private void PlayResume() { base.ChangePlayerState(null, Main.PlayAction.Resume); } private void StopPlaying() { base.ChangePlayerState(null, Main.PlayAction.Pause); } #endregion Private Methods } }