Moved music playing controls from individual viewmodels to main view

This commit is contained in:
watsonb8 2019-12-04 20:42:23 -08:00
parent 187de97503
commit 3576a906e2
7 changed files with 145 additions and 148 deletions

View File

@ -12,20 +12,20 @@
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition <ColumnDefinition
Width="150"/> Width="200"/>
<ColumnDefinition <ColumnDefinition
Width="*"/> Width="*"/>
<ColumnDefinition <ColumnDefinition
Width="100"/> Width="100"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackLayout <StackLayout
Grid.Column="0"
Orientation="Horizontal"> Orientation="Horizontal">
<BoxView <BoxView
x:Name="AlbumArtBoxView"/> x:Name="AlbumArtBoxView"/>
<StackLayout <StackLayout
x:Name="MediaInfoContainer" x:Name="MediaInfoContainer"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand">
Grid.Column="0">
<Label <Label
x:Name="SongTitleLabel" x:Name="SongTitleLabel"
LineBreakMode="TailTruncation"/> LineBreakMode="TailTruncation"/>

View File

@ -11,7 +11,6 @@ namespace Aurora.Design.Views
public class BaseViewModel : INotifyPropertyChanged public class BaseViewModel : INotifyPropertyChanged
{ {
private BaseMedia _baseMedia;
public BaseViewModel() public BaseViewModel()
{ {
} }
@ -46,39 +45,33 @@ namespace Aurora.Design.Views
} }
/// <summary> /// <summary>
/// Model for the currently playing music. /// Delegate for interacting with main screen player control
/// </summary> /// </summary>
/// <value></value> /// <value></value>
public BaseMedia Media public SetPlayerDelegate ChangePlayerState { get; set; }
{
get
{
return this._baseMedia;
}
set
{
if (value != _baseMedia)
{
_baseMedia = value;
if (this.SetPlayerMetadata != null)
{
SetPlayerMetadata.Invoke(value);
}
}
}
}
public SetPlayerMetadataDelegate SetPlayerMetadata { get; set; } /// <summary>
/// Delegate for checking if main screen player control is currently playing
/// </summary>
/// <value></value>
public GetIsPlayingDelegate IsPlaying { get; set; }
public SetPlayerVisibleDelegate SetPlayerVisible { get; set; }
public SetIsPlayingDelegate SetIsPlaying { get; set; }
#endregion Player #endregion Player
#region Lifecycle #region Lifecycle
/// <summary>
/// Called by main screen on view appearing
/// </summary>
/// <typeparam name="object"></typeparam>
/// <returns></returns>
public virtual Task OnActive() { return Task.FromResult<object>(null); } public virtual Task OnActive() { return Task.FromResult<object>(null); }
/// <summary>
/// Called by main screen on view disappearing
/// </summary>
/// <typeparam name="object"></typeparam>
/// <returns></returns>
public virtual Task OnInactive() { return Task.FromResult<object>(null); } public virtual Task OnInactive() { return Task.FromResult<object>(null); }
#endregion #endregion

View File

@ -8,25 +8,32 @@ using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Design.Components.MediaPlayer; using Aurora.Design.Components.MediaPlayer;
using Aurora.Services.PlayerService;
namespace Aurora.Design.Views.Main namespace Aurora.Design.Views.Main
{ {
public enum PlayAction
{
Play,
Pause,
Resume,
Stop
}
/// <summary> /// <summary>
/// Delegate for updating player metadata /// Delegate for updating player metadata
/// </summary> /// </summary>
/// <param name="media"></param> /// <param name="media"></param>
public delegate void SetPlayerMetadataDelegate(BaseMedia media); public delegate void SetPlayerDelegate(BaseMedia media, PlayAction action);
public delegate bool GetIsPlayingDelegate();
public delegate void SetPlayerVisibleDelegate(bool visible);
public delegate void SetIsPlayingDelegate(bool playing);
[XamlCompilation(XamlCompilationOptions.Compile)] [XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainView : ContentPage//, IDisposable public partial class MainView : ContentPage//, IDisposable
{ {
private Dictionary<int, BaseViewModel> _viewModels; private Dictionary<int, BaseViewModel> _viewModels;
private BaseViewModel _lastViewModel; private BaseViewModel _lastViewModel;
private Player _player; private Player _playerComponent;
private PlayerService _playerService;
private ContentPresenter _viewContent; private ContentPresenter _viewContent;
public MainView() public MainView()
@ -35,8 +42,9 @@ namespace Aurora.Design.Views.Main
BindingContext = new MainViewModel(); BindingContext = new MainViewModel();
_viewModels = new Dictionary<int, BaseViewModel>(); _viewModels = new Dictionary<int, BaseViewModel>();
_player = Player; _playerComponent = Player;
_viewContent = (ContentPresenter)ContentPage.Content.FindByName("ViewContent"); _viewContent = (ContentPresenter)ContentPage.Content.FindByName("ViewContent");
_playerService = PlayerService.Instance;
MasterPage.ListView.ItemSelected += OnNavItemSelected; MasterPage.ListView.ItemSelected += OnNavItemSelected;
@ -139,9 +147,8 @@ namespace Aurora.Design.Views.Main
/// <param name="vm"></param> /// <param name="vm"></param>
private void UnassignPlayerControls(BaseViewModel vm) private void UnassignPlayerControls(BaseViewModel vm)
{ {
vm.SetPlayerMetadata = null; vm.ChangePlayerState = null;
vm.SetPlayerVisible = null; vm.IsPlaying = null;
vm.SetIsPlaying = null;
} }
/// <summary> /// <summary>
@ -150,44 +157,72 @@ namespace Aurora.Design.Views.Main
/// <param name="vm">BaseViewModel to assign controls to</param> /// <param name="vm">BaseViewModel to assign controls to</param>
private void AssignPlayerControls(BaseViewModel vm) private void AssignPlayerControls(BaseViewModel vm)
{ {
_player.PlayButtonCommand = new Command(vm.OnPlayButtonExecute, vm.CanPlayButtonExecute); _playerComponent.PlayButtonCommand = new Command(vm.OnPlayButtonExecute, vm.CanPlayButtonExecute);
_player.NextButtonCommand = new Command(vm.OnNextButtonExecute, vm.CanNextButtonExecute); _playerComponent.NextButtonCommand = new Command(vm.OnNextButtonExecute, vm.CanNextButtonExecute);
_player.PreviousButtonCommand = new Command(vm.OnPreviousButtonExecute, vm.CanPreviousButtonExecute); _playerComponent.PreviousButtonCommand = new Command(vm.OnPreviousButtonExecute, vm.CanPreviousButtonExecute);
//Assign SetPlayer delegate //Assign SetPlayer delegate
vm.SetPlayerMetadata = SetPlayer; vm.ChangePlayerState = ChangePlayerState;
vm.SetPlayerVisible = SetPlayerVisible; vm.IsPlaying = () =>
vm.SetIsPlaying = SetIsPlaying; {
return _playerService.PlaybackState == PlaybackState.Playing;
};
} }
/// <summary> /// <summary>
/// SetPlayerDelegate implementation /// Delegate handler for a view model controling music playback
/// </summary> /// </summary>
/// <param name="media"></param> /// <param name="media"></param>
private void SetPlayer(BaseMedia media) /// <param name="action"></param>
/// <returns></returns>
private async void ChangePlayerState(BaseMedia media, PlayAction action)
{ {
if (media.Metadata is AudioMetadata) if (media != null && media.Metadata is AudioMetadata)
{ {
AudioMetadata meta = (AudioMetadata)media.Metadata; AudioMetadata meta = (AudioMetadata)media.Metadata;
_player.ArtistName = meta.Artist; _playerComponent.ArtistName = meta.Artist;
_player.SongTitle = meta.Title; _playerComponent.SongTitle = meta.Title;
} }
}
/// <summary> switch (action)
/// SetPlayerVisibleDelegate implementation {
/// </summary> case PlayAction.Pause:
/// <param name="visible"></param> {
private void SetPlayerVisible(Boolean visible) _playerService.Pause();
{ _playerComponent.IsPlaying = false;
_player.IsVisible = visible; break;
} }
case PlayAction.Play:
{
if (media == null)
{
break;
}
if (!_playerService.IsMediaLoaded(media))
{
await _playerService.LoadMedia(media).ConfigureAwait(true);
}
_playerService.Play();
_playerComponent.IsPlaying = true;
break;
}
case PlayAction.Resume:
{
_playerService.Play();
_playerComponent.IsPlaying = true;
break;
}
case PlayAction.Stop:
{
_playerService.Stop();
_playerComponent.IsPlaying = false;
break;
}
}
private void SetIsPlaying(bool playing)
{
_player.IsPlaying = playing;
} }
} }
} }

View File

@ -31,7 +31,6 @@ namespace Aurora.Design.Views.Party
private ObservableCollection<PartyMember> _members; private ObservableCollection<PartyMember> _members;
private ObservableCollection<BaseMedia> _queue; private ObservableCollection<BaseMedia> _queue;
private BaseMedia _selectedMedia; private BaseMedia _selectedMedia;
private PlayerService _player;
private ClientService _client; private ClientService _client;
public PartyViewModel() public PartyViewModel()
@ -43,7 +42,6 @@ namespace Aurora.Design.Views.Party
_queue = new ObservableCollection<BaseMedia>(); _queue = new ObservableCollection<BaseMedia>();
SetState(PartyState.SelectingHost); SetState(PartyState.SelectingHost);
this._player = PlayerService.Instance;
PlayCommand = new Command(OnDoubleClickExecute, CanDoubleClickExecute); PlayCommand = new Command(OnDoubleClickExecute, CanDoubleClickExecute);
@ -296,40 +294,33 @@ namespace Aurora.Design.Views.Party
public override void OnPlayButtonExecute() public override void OnPlayButtonExecute()
{ {
switch (_player.PlaybackState) if (base.IsPlaying())
{ {
case PlaybackState.Buffering: //Fire play stopped event
{ AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
//Fire play resume event MediaPausedEvent mediaPaused = new MediaPausedEvent();
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
MediaResumedEvent mediaResumed = new MediaResumedEvent();
EventManager.Instance.FireEvent(new BaseEvent() EventManager.Instance.FireEvent(new BaseEvent()
{ {
MediaResumedEvent = mediaResumed MediaPausedEvent = mediaPaused
}); });
}
else
{
//Fire play resume event
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
MediaResumedEvent mediaResumed = new MediaResumedEvent();
break; EventManager.Instance.FireEvent(new BaseEvent()
} {
case PlaybackState.Playing: MediaResumedEvent = mediaResumed
{ });
//Fire play stopped event
AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata;
MediaPausedEvent mediaPaused = new MediaPausedEvent();
EventManager.Instance.FireEvent(new BaseEvent()
{
MediaPausedEvent = mediaPaused
});
break;
}
} }
} }
public override bool CanPlayButtonExecute() public override bool CanPlayButtonExecute()
{ {
return this._state == PartyState.Hosting && this._player.IsLoaded; return this._state == PartyState.Hosting;
} }
public override bool CanNextButtonExecute() public override bool CanNextButtonExecute()
@ -491,21 +482,19 @@ namespace Aurora.Design.Views.Party
return null; return null;
} }
} }
private async void PlayFromBeginning(BaseMedia args) private void PlayFromBeginning(BaseMedia args)
{ {
base.Media = args; base.ChangePlayerState(args, Main.PlayAction.Play);
await _player.LoadMedia(base.Media).ConfigureAwait(true);
_player.Play();
} }
private void PlayResume() private void PlayResume()
{ {
_player.Play(); base.ChangePlayerState(null, Main.PlayAction.Resume);
} }
private void StopPlaying() private void StopPlaying()
{ {
_player.Pause(); base.ChangePlayerState(null, Main.PlayAction.Pause);
} }
#endregion Private Methods #endregion Private Methods

View File

@ -6,8 +6,8 @@
x:Class="Aurora.Design.Views.Songs.SongsView"> x:Class="Aurora.Design.Views.Songs.SongsView">
<ContentView.Content> <ContentView.Content>
<library:Library <library:Library
ItemsSource="{Binding SongsList}" ItemsSource="{Binding SongsList}"
SelectedItem="{Binding SelectedSong}" SelectedItem="{Binding SelectedSong}"
ItemDoubleClicked="{Binding PlayCommand}"/> ItemDoubleClicked="{Binding DoubleClickCommand}"/>
</ContentView.Content> </ContentView.Content>
</ContentView> </ContentView>

View File

@ -1,7 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Services; using Aurora.Services;
using Aurora.Services.PlayerService;
using Xamarin.Forms; using Xamarin.Forms;
namespace Aurora.Design.Views.Songs namespace Aurora.Design.Views.Songs
@ -11,16 +10,14 @@ namespace Aurora.Design.Views.Songs
#region Fields #region Fields
private ObservableCollection<BaseMedia> _songsList; private ObservableCollection<BaseMedia> _songsList;
private BaseMedia _selectedSong; private BaseMedia _selectedSong;
private PlayerService _player;
#endregion Fields #endregion Fields
#region Constructor #region Constructor
public SongsViewModel() public SongsViewModel()
{ {
_player = PlayerService.Instance;
_songsList = new ObservableCollection<BaseMedia>(); _songsList = new ObservableCollection<BaseMedia>();
PlayCommand = new Command(OnPlayButtonExecute, CanPlayButtonExecute); DoubleClickCommand = new Command(OnDoubleClickExecute, OnDoubleClickCanExecute);
Initialize(); Initialize();
} }
@ -40,7 +37,7 @@ namespace Aurora.Design.Views.Songs
set { SetProperty(ref _selectedSong, value); } set { SetProperty(ref _selectedSong, value); }
} }
public Command PlayCommand { get; private set; } public Command DoubleClickCommand { get; private set; }
#endregion Properties #endregion Properties
@ -65,57 +62,24 @@ namespace Aurora.Design.Views.Songs
public override bool CanPlayButtonExecute() public override bool CanPlayButtonExecute()
{ {
switch (_player.PlaybackState) return true;
{
case PlaybackState.Buffering:
{
return true;
}
case PlaybackState.Playing:
{
return true;
}
case PlaybackState.Stopped:
{
return true;
}
}
return false;
} }
public async override void OnPlayButtonExecute() public override void OnPlayButtonExecute()
{ {
if (_selectedSong == null) if (_selectedSong == null)
{ {
return; return;
} }
base.Media = _selectedSong; if (base.IsPlaying())
if (!_player.IsMediaLoaded(base.Media))
{ {
await _player.LoadMedia(base.Media).ConfigureAwait(true); base.ChangePlayerState(_selectedSong, Main.PlayAction.Pause);
}
else
{
base.ChangePlayerState(_selectedSong, Main.PlayAction.Play);
} }
switch (_player.PlaybackState)
{
case PlaybackState.Buffering:
{
_player.Play();
SetIsPlaying(true);
break;
}
case PlaybackState.Playing:
{
_player.Pause();
SetIsPlaying(false);
break;
}
case PlaybackState.Stopped:
{
_player.Play();
SetIsPlaying(true);
break;
}
}
} }
public override bool CanNextButtonExecute() public override bool CanNextButtonExecute()
@ -128,6 +92,21 @@ namespace Aurora.Design.Views.Songs
} }
public void OnDoubleClickExecute()
{
if (_selectedSong == null)
{
return;
}
base.ChangePlayerState(_selectedSong, Main.PlayAction.Play);
}
public bool OnDoubleClickCanExecute()
{
return true;
}
#endregion Commands #endregion Commands
} }
} }

View File

@ -4,8 +4,6 @@ using System.Threading;
using Grpc.Core; using Grpc.Core;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Proto.Sync; using Aurora.Proto.Sync;
using Aurora.Proto.Events;
using Aurora.Proto.Party;
using LibVLCSharp.Shared; using LibVLCSharp.Shared;
namespace Aurora.Services.PlayerService namespace Aurora.Services.PlayerService
@ -234,7 +232,10 @@ namespace Aurora.Services.PlayerService
/// <param name="args"></param> /// <param name="args"></param>
private void OnStopped(object sender, EventArgs args) private void OnStopped(object sender, EventArgs args)
{ {
PlaybackStateChanged.Invoke(this, new PlaybackStateChangedEventArgs(_state, PlaybackState.Stopped)); if (PlaybackStateChanged != null)
{
PlaybackStateChanged.Invoke(this, new PlaybackStateChangedEventArgs(_state, PlaybackState.Stopped));
}
_state = PlaybackState.Stopped; _state = PlaybackState.Stopped;
this.Unload(); this.Unload();
} }