From 1184a45c0acb58b7b8a8af21e80b980861c741be Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 12 Sep 2015 10:44:36 -0400 Subject: [PATCH 01/31] Not add bottom margin to shell content (mobile) --- Windows/Audiotica.Windows/App.xaml.cs | 2 +- Windows/Audiotica.Windows/Package.appxmanifest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/App.xaml.cs b/Windows/Audiotica.Windows/App.xaml.cs index 28a357c3..c3be1868 100644 --- a/Windows/Audiotica.Windows/App.xaml.cs +++ b/Windows/Audiotica.Windows/App.xaml.cs @@ -80,7 +80,7 @@ private void OnVisibleBoundsChanged(ApplicationView sender, object args) RootFrame.Margin = new Thickness(left, 0, right, bottom); Shell.HamburgerPadding = new Thickness(left, 0, 0, 0); Shell.NavBarMargin = new Thickness(0, 0, 0, bottom); - Shell.Padding = new Thickness(left, top, 0, bottom); + Shell.Padding = new Thickness(left, top, 0, 0); } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Package.appxmanifest b/Windows/Audiotica.Windows/Package.appxmanifest index c1f377ff..e6bec9cd 100644 --- a/Windows/Audiotica.Windows/Package.appxmanifest +++ b/Windows/Audiotica.Windows/Package.appxmanifest @@ -1,6 +1,6 @@  - + Audiotica From 84482bca54173426bb25a6b68cfe0c15a4170479 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 09:12:01 -0400 Subject: [PATCH 02/31] Make double click play track --- Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml | 2 +- Windows/Audiotica.Windows/Controls/TrackViewer.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml index b5d2be58..af3253ef 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml @@ -9,7 +9,7 @@ d:DesignHeight="300" d:DesignWidth="400"> - + diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml index 1bbca115..10a1e1ca 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml @@ -9,7 +9,7 @@ d:DesignHeight="300" d:DesignWidth="400"> - + From 842588b8d7a65a5465223fd687ed944f7a8736b8 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 09:40:14 -0400 Subject: [PATCH 03/31] Add shuffle all button to collection views --- .../Extensions/CollectionExtensions.cs | 55 +++++++++++++++++++ .../Controls/LibraryHeader.xaml | 6 ++ .../Controls/LibraryHeader.xaml.cs | 19 ++++++- .../DesignTime/DesignPlayerService.cs | 2 +- .../Services/Interfaces/IPlayerService.cs | 2 +- .../Services/RunTime/PlayerService.cs | 7 ++- .../ViewModels/AlbumPageViewModel.cs | 1 + .../ViewModels/AlbumsPageViewModel.cs | 17 ++++++ .../ViewModels/ArtistsPageViewModel.cs | 20 ++++++- .../ViewModels/SongsPageViewModel.cs | 19 ++++++- .../Audiotica.Windows/Views/AlbumPage.xaml | 2 +- .../Audiotica.Windows/Views/AlbumsPage.xaml | 1 + .../Audiotica.Windows/Views/ArtistsPage.xaml | 1 + .../Audiotica.Windows/Views/SongsPage.xaml | 1 + 14 files changed, 141 insertions(+), 12 deletions(-) diff --git a/Audiotica.Core/Extensions/CollectionExtensions.cs b/Audiotica.Core/Extensions/CollectionExtensions.cs index fb0d7e4c..0161e410 100644 --- a/Audiotica.Core/Extensions/CollectionExtensions.cs +++ b/Audiotica.Core/Extensions/CollectionExtensions.cs @@ -1,7 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + namespace Audiotica.Core.Extensions { public static class CollectionExtensions { + private static readonly Random Random = new Random(); + public static void Fill(this T[] array, T value) { for (var i = 0; i < array.Length; i++) @@ -9,5 +16,53 @@ public static void Fill(this T[] array, T value) array[i] = value; } } + + public static void Sort(this ObservableCollection observable, Comparison comparison) + { + var sorted = observable.ToList(); + sorted.Sort(comparison); + + var ptr = 0; + while (ptr < sorted.Count) + { + if (!observable[ptr].Equals(sorted[ptr])) + { + var t = observable[ptr]; + observable.RemoveAt(ptr); + observable.Insert(sorted.IndexOf(t), t); + } + else + { + ptr++; + } + } + } + + public static void AddRange(this IList collection, IEnumerable items) + { + foreach (var item in items) + collection.Add(item); + } + + public static IEnumerable Shuffle(this IEnumerable list) + { + var arr = list.ToArray(); + Shuffle(arr); + return arr; + } + + private static void Shuffle(IList array) + { + var n = array.Count; + for (var i = 0; i < n; i++) + { + // NextDouble returns a random number between 0 and 1. + // ... It is equivalent to Math.random() in Java. + var r = i + (int) (Random.NextDouble()*(n - i)); + var t = array[r]; + array[r] = array[i]; + array[i] = t; + } + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml index d324db2d..f8db7a56 100644 --- a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml +++ b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml @@ -15,6 +15,12 @@ + + + + + + diff --git a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs index b0adc3d8..487cf6cc 100644 --- a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs @@ -20,8 +20,10 @@ public sealed partial class LibraryHeader new PropertyMetadata(0)); public static readonly DependencyProperty CurrentSortChangedCommandProperty = - DependencyProperty.Register("CurrentSortChangedCommand", typeof (ICommand), typeof (LibraryHeader), - new PropertyMetadata(0)); + DependencyProperty.Register("CurrentSortChangedCommand", typeof (ICommand), typeof (LibraryHeader), null); + + public static readonly DependencyProperty ShuffleAllCommandProperty = + DependencyProperty.Register("ShuffleAllCommand", typeof (ICommand), typeof (LibraryHeader), null); public LibraryHeader() { @@ -52,7 +54,14 @@ public ICommand CurrentSortChangedCommand set { SetValue(CurrentSortChangedCommandProperty, value); } } + public ICommand ShuffleAllCommand + { + get { return (ICommand) GetValue(ShuffleAllCommandProperty); } + set { SetValue(ShuffleAllCommandProperty, value); } + } + public event EventHandler CurrentSortChanged; + public event EventHandler ShuffleAll; private static void SortItemsPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e) { @@ -71,5 +80,11 @@ private void ListBox_OnSelectionChanged(object sender, SelectionChangedEventArgs CurrentSortChangedCommand?.Execute(item); } } + + private void ShuffleAll_Click(object sender, RoutedEventArgs e) + { + ShuffleAll?.Invoke(this, EventArgs.Empty); + ShuffleAllCommand?.Execute(EventArgs.Empty); + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs index d68067af..d5318860 100644 --- a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs @@ -54,7 +54,7 @@ public Task AddUpNextAsync(WebSong webSong) throw new NotImplementedException(); } - public Task NewQueueAsync(List tracks) + public Task NewQueueAsync(IEnumerable tracks) { throw new NotImplementedException(); } diff --git a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs index 851088ce..ff4c0ea7 100644 --- a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs @@ -38,7 +38,7 @@ public interface IPlayerService Task AddAsync(WebSong webSong, int position = -1); Task AddUpNextAsync(Track track); Task AddUpNextAsync(WebSong webSong); - Task NewQueueAsync(List tracks); + Task NewQueueAsync(IEnumerable tracks); /// diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index b273464a..5808bb0b 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -101,11 +101,12 @@ public async Task AddUpNextAsync(WebSong webSong) return await AddUpNextAsync(track); } - public async Task NewQueueAsync(List tracks) + public async Task NewQueueAsync(IEnumerable tracks) { - foreach (var track in tracks) + var arr = tracks.ToArray(); + foreach (var track in arr) await PrepareTrackAsync(track); - var newQueue = tracks.Select(track => new QueueTrack(track)).ToList(); + var newQueue = arr.Select(track => new QueueTrack(track)).ToList(); MessageHelper.SendMessageToBackground(new UpdatePlaylistMessage(newQueue)); } diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs index 2941ed73..1dea8e79 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs @@ -86,6 +86,7 @@ public bool IsCatalogMode private void PlayAllExecute() { + if (Album.Tracks.Count == 0) return; var albumTracks = Album.Tracks.ToList(); _playerService.NewQueueAsync(albumTracks); } diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs index 2e67468c..f2ab0785 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs @@ -21,21 +21,25 @@ public class AlbumsPageViewModel : ViewModelBase { private readonly ILibraryCollectionService _libraryCollectionService; private readonly INavigationService _navigationService; + private readonly IPlayerService _playerService; private readonly ISettingsUtility _settingsUtility; private double _gridViewVerticalOffset; private double _listViewVerticalOffset; private CollectionViewSource _viewSource; public AlbumsPageViewModel(ILibraryService libraryService, ILibraryCollectionService libraryCollectionService, + IPlayerService playerService, ISettingsUtility settingsUtility, INavigationService navigationService) { _libraryCollectionService = libraryCollectionService; + _playerService = playerService; _settingsUtility = settingsUtility; _navigationService = navigationService; LibraryService = libraryService; AlbumClickCommand = new Command(AlbumClickExecute); SortChangedCommand = new Command(SortChangedExecute); + ShuffleAllCommand = new Command(ShuffleAllExecute); SortItems = Enum.GetValues(typeof (AlbumSort)) @@ -49,6 +53,8 @@ public AlbumsPageViewModel(ILibraryService libraryService, ILibraryCollectionSer ChangeSort(defaultSort); } + public Command ShuffleAllCommand { get; } + public Command SortChangedCommand { get; } public double GridViewVerticalOffset @@ -77,6 +83,17 @@ public CollectionViewSource ViewSource public ILibraryService LibraryService { get; } + private async void ShuffleAllExecute() + { + var playable = LibraryService.Tracks + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + if (!playable.Any()) return; + + var tracks = playable.Shuffle(); + await _playerService.NewQueueAsync(tracks); + } + private void SortChangedExecute(ListBoxItem item) { if (!(item?.Tag is AlbumSort)) return; diff --git a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs index f69646c2..c2bbd34a 100644 --- a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs @@ -4,14 +4,12 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Navigation; -using Audiotica.Core.Common; using Audiotica.Core.Extensions; using Audiotica.Database.Models; using Audiotica.Database.Services.Interfaces; using Audiotica.Windows.Enums; using Audiotica.Windows.Services.Interfaces; using Audiotica.Windows.Services.NavigationService; -using Audiotica.Windows.Tools; using Audiotica.Windows.Tools.Mvvm; using Audiotica.Windows.Views; @@ -21,20 +19,23 @@ public class ArtistsPageViewModel : ViewModelBase { private readonly ILibraryCollectionService _libraryCollectionService; private readonly INavigationService _navigationService; + private readonly IPlayerService _playerService; private double _gridViewVerticalOffset; private double _listViewVerticalOffset; private CollectionViewSource _viewSource; public ArtistsPageViewModel(ILibraryCollectionService libraryCollectionService, - ILibraryService libraryService, + ILibraryService libraryService, IPlayerService playerService, INavigationService navigationService) { LibraryService = libraryService; _libraryCollectionService = libraryCollectionService; + _playerService = playerService; _navigationService = navigationService; ArtistClickCommand = new Command(ArtistClickExecute); SortChangedCommand = new Command(SortChangedExecute); + ShuffleAllCommand = new Command(ShuffleAllExecute); SortItems = Enum.GetValues(typeof (ArtistSort)) @@ -44,6 +45,8 @@ public ArtistsPageViewModel(ILibraryCollectionService libraryCollectionService, ChangeSort(ArtistSort.AtoZ); } + public Command ShuffleAllCommand { get; } + public Command SortChangedCommand { get; } public ILibraryService LibraryService { get; set; } @@ -70,6 +73,17 @@ public double GridViewVerticalOffset set { Set(ref _gridViewVerticalOffset, value); } } + private async void ShuffleAllExecute() + { + var playable = LibraryService.Tracks + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + if (!playable.Any()) return; + + var tracks = playable.Shuffle(); + await _playerService.NewQueueAsync(tracks); + } + private void SortChangedExecute(ListBoxItem item) { if (!(item?.Tag is ArtistSort)) return; diff --git a/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs index 804169e0..2cd2c157 100644 --- a/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs @@ -7,6 +7,7 @@ using Audiotica.Core.Extensions; using Audiotica.Core.Utilities.Interfaces; using Audiotica.Core.Windows.Helpers; +using Audiotica.Database.Models; using Audiotica.Database.Services.Interfaces; using Audiotica.Windows.Enums; using Audiotica.Windows.Services.Interfaces; @@ -17,16 +18,18 @@ namespace Audiotica.Windows.ViewModels public class SongsPageViewModel : ViewModelBase { private readonly ILibraryCollectionService _libraryCollectionService; + private readonly IPlayerService _playerService; private readonly ISettingsUtility _settingsUtility; private int _selectedIndex; private double _verticalOffset; private CollectionViewSource _viewSource; public SongsPageViewModel(ILibraryCollectionService libraryCollectionService, ILibraryService libraryService, - ISettingsUtility settingsUtility) + ISettingsUtility settingsUtility, IPlayerService playerService) { _libraryCollectionService = libraryCollectionService; _settingsUtility = settingsUtility; + _playerService = playerService; LibraryService = libraryService; SortItems = @@ -35,6 +38,7 @@ public SongsPageViewModel(ILibraryCollectionService libraryCollectionService, IL .Select(sort => new ListBoxItem {Content = sort.GetEnumText(), Tag = sort}) .ToList(); SortChangedCommand = new Command(SortChangedExecute); + ShuffleAllCommand = new Command(ShuffleAllExecute); var defaultSort = _settingsUtility.Read(ApplicationSettingsConstants.SongSort, TrackSort.DateAdded, SettingsStrategy.Roam); @@ -42,6 +46,8 @@ public SongsPageViewModel(ILibraryCollectionService libraryCollectionService, IL ChangeSort(defaultSort); } + public Command ShuffleAllCommand { get; } + public Command SortChangedCommand { get; } public int DefaultSort { get; } @@ -68,6 +74,17 @@ public double VerticalOffset set { Set(ref _verticalOffset, value); } } + private async void ShuffleAllExecute() + { + var playable = LibraryService.Tracks + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + if (!playable.Any()) return; + + var tracks = playable.Shuffle(); + await _playerService.NewQueueAsync(tracks); + } + private void SortChangedExecute(ListBoxItem item) { if (!(item?.Tag is TrackSort)) return; diff --git a/Windows/Audiotica.Windows/Views/AlbumPage.xaml b/Windows/Audiotica.Windows/Views/AlbumPage.xaml index 55757367..ae75ef0f 100644 --- a/Windows/Audiotica.Windows/Views/AlbumPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumPage.xaml @@ -50,7 +50,7 @@ Visibility="{x:Bind ViewModel.IsCatalogMode, Mode=OneWay, Converter={StaticResource ReverseVisibilityConverter}}" Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}"> - + diff --git a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml index 20c50ebc..29d82687 100644 --- a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml @@ -49,6 +49,7 @@ diff --git a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml index 1261ad69..5fb4ffbf 100644 --- a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml +++ b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml @@ -49,6 +49,7 @@ diff --git a/Windows/Audiotica.Windows/Views/SongsPage.xaml b/Windows/Audiotica.Windows/Views/SongsPage.xaml index 93c9a7f9..b4b7e0c5 100644 --- a/Windows/Audiotica.Windows/Views/SongsPage.xaml +++ b/Windows/Audiotica.Windows/Views/SongsPage.xaml @@ -23,6 +23,7 @@ From 7fa4ba08c8d8a0b87dc48075d3f68c48dae14ecc Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 11:05:42 -0400 Subject: [PATCH 04/31] Add selection mode toggle --- Windows/Audiotica.Windows/App.xaml | 19 +++ .../Audiotica.Windows.csproj | 1 + .../Controls/LibraryHeader.xaml | 14 ++- .../Controls/LibraryHeader.xaml.cs | 9 ++ .../Styles/ControlStyles.xaml | 111 ++++++++++++++++++ .../Tools/Converters/ContentConverter.cs | 1 + .../Tools/Converters/NotConverter.cs | 19 +++ .../ViewModels/AlbumsPageViewModel.cs | 8 ++ .../ViewModels/ArtistsPageViewModel.cs | 8 ++ .../Audiotica.Windows/Views/AlbumsPage.xaml | 7 +- .../Audiotica.Windows/Views/ArtistsPage.xaml | 7 +- .../Audiotica.Windows/Views/SongsPage.xaml | 4 +- 12 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 Windows/Audiotica.Windows/Tools/Converters/NotConverter.cs diff --git a/Windows/Audiotica.Windows/App.xaml b/Windows/Audiotica.Windows/App.xaml index 5cb5e2aa..b6d6499a 100644 --- a/Windows/Audiotica.Windows/App.xaml +++ b/Windows/Audiotica.Windows/App.xaml @@ -35,6 +35,25 @@ + + + + + Multiple + + + Single + + + + + + Multiple + + + None + + diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index e6524465..f2ada4bd 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -196,6 +196,7 @@ + diff --git a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml index f8db7a56..a5beee6b 100644 --- a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml +++ b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml @@ -11,7 +11,7 @@ mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> - + @@ -21,9 +21,9 @@ - - + + @@ -41,6 +41,12 @@ + + + + + + diff --git a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs index 487cf6cc..800e75ba 100644 --- a/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/LibraryHeader.xaml.cs @@ -25,6 +25,9 @@ public sealed partial class LibraryHeader public static readonly DependencyProperty ShuffleAllCommandProperty = DependencyProperty.Register("ShuffleAllCommand", typeof (ICommand), typeof (LibraryHeader), null); + public static readonly DependencyProperty IsSelectModeProperty = + DependencyProperty.Register("IsSelectMode", typeof(bool?), typeof(LibraryHeader), new PropertyMetadata(false)); + public LibraryHeader() { InitializeComponent(); @@ -60,6 +63,12 @@ public ICommand ShuffleAllCommand set { SetValue(ShuffleAllCommandProperty, value); } } + public bool? IsSelectMode + { + get { return (bool?)GetValue(IsSelectModeProperty); } + set { SetValue(IsSelectModeProperty, value); } + } + public event EventHandler CurrentSortChanged; public event EventHandler ShuffleAll; diff --git a/Windows/Audiotica.Windows/Styles/ControlStyles.xaml b/Windows/Audiotica.Windows/Styles/ControlStyles.xaml index cbcced7f..6b0943bc 100644 --- a/Windows/Audiotica.Windows/Styles/ControlStyles.xaml +++ b/Windows/Audiotica.Windows/Styles/ControlStyles.xaml @@ -270,4 +270,115 @@ + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Tools/Converters/ContentConverter.cs b/Windows/Audiotica.Windows/Tools/Converters/ContentConverter.cs index 14166370..2640b8c2 100644 --- a/Windows/Audiotica.Windows/Tools/Converters/ContentConverter.cs +++ b/Windows/Audiotica.Windows/Tools/Converters/ContentConverter.cs @@ -10,6 +10,7 @@ public class ContentConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, string language) { + if (!(value is bool)) return FalseContent; return ((bool) value) ? TrueContent : FalseContent; } diff --git a/Windows/Audiotica.Windows/Tools/Converters/NotConverter.cs b/Windows/Audiotica.Windows/Tools/Converters/NotConverter.cs new file mode 100644 index 00000000..08ffb634 --- /dev/null +++ b/Windows/Audiotica.Windows/Tools/Converters/NotConverter.cs @@ -0,0 +1,19 @@ +using System; +using Windows.UI.Xaml.Data; + +namespace Audiotica.Windows.Tools.Converters +{ + public class NotConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (!(value is bool)) return false; + return !((bool) value); + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs index f2ab0785..eee80e46 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs @@ -24,6 +24,7 @@ public class AlbumsPageViewModel : ViewModelBase private readonly IPlayerService _playerService; private readonly ISettingsUtility _settingsUtility; private double _gridViewVerticalOffset; + private bool? _isSelectMode = false; private double _listViewVerticalOffset; private CollectionViewSource _viewSource; @@ -83,6 +84,12 @@ public CollectionViewSource ViewSource public ILibraryService LibraryService { get; } + public bool? IsSelectMode + { + get { return _isSelectMode; } + set { Set(ref _isSelectMode, value); } + } + private async void ShuffleAllExecute() { var playable = LibraryService.Tracks @@ -103,6 +110,7 @@ private void SortChangedExecute(ListBoxItem item) private void AlbumClickExecute(ItemClickEventArgs e) { + if (IsSelectMode == true) return; var album = (Album) e.ClickedItem; _navigationService.Navigate(typeof (AlbumPage), new AlbumPageViewModel.AlbumPageParameter(album.Title, album.Artist.Name)); diff --git a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs index c2bbd34a..473b0e11 100644 --- a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs @@ -21,6 +21,7 @@ public class ArtistsPageViewModel : ViewModelBase private readonly INavigationService _navigationService; private readonly IPlayerService _playerService; private double _gridViewVerticalOffset; + private bool? _isSelectMode = false; private double _listViewVerticalOffset; private CollectionViewSource _viewSource; @@ -73,6 +74,12 @@ public double GridViewVerticalOffset set { Set(ref _gridViewVerticalOffset, value); } } + public bool? IsSelectMode + { + get { return _isSelectMode; } + set { Set(ref _isSelectMode, value); } + } + private async void ShuffleAllExecute() { var playable = LibraryService.Tracks @@ -107,6 +114,7 @@ public void ChangeSort(ArtistSort sort) private void ArtistClickExecute(ItemClickEventArgs e) { + if (IsSelectMode == true) return; var artist = (Artist) e.ClickedItem; _navigationService.Navigate(typeof (ArtistPage), artist.Name); } diff --git a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml index 29d82687..c47c7eb1 100644 --- a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml @@ -51,7 +51,8 @@ SortItems="{x:Bind ViewModel.SortItems}" ShuffleAllCommand="{x:Bind ViewModel.ShuffleAllCommand}" CurrentSortChangedCommand="{x:Bind ViewModel.SortChangedCommand}" - Margin="{StaticResource PageTopSideThickness}" /> + Margin="{StaticResource PageTopSideThickness}" + IsSelectMode="{x:Bind ViewModel.IsSelectMode, Mode=TwoWay}"/> @@ -98,7 +99,7 @@ diff --git a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml index 5fb4ffbf..1a172997 100644 --- a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml +++ b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml @@ -51,7 +51,8 @@ + Margin="{StaticResource PageTopSideThickness}" + IsSelectMode="{x:Bind ViewModel.IsSelectMode, Mode=TwoWay}" /> @@ -98,7 +99,7 @@ diff --git a/Windows/Audiotica.Windows/Views/SongsPage.xaml b/Windows/Audiotica.Windows/Views/SongsPage.xaml index b4b7e0c5..9da5987c 100644 --- a/Windows/Audiotica.Windows/Views/SongsPage.xaml +++ b/Windows/Audiotica.Windows/Views/SongsPage.xaml @@ -34,7 +34,8 @@ - - From cff6d15e389639a7c60e2409d65d183c4a3ee82a Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 12:11:32 -0400 Subject: [PATCH 05/31] Add play, download, add and delete to selection bar --- .../Audiotica.Windows.csproj | 9 + .../Controls/SelectModeCommandBar.xaml | 21 ++ .../Controls/SelectModeCommandBar.xaml.cs | 135 +++++++++++++ .../ListViewBindableSelectionHandler.cs | 108 ++++++++++ .../Extensions/ListViewExtensions.cs | 186 ++++++++++++++++++ .../ViewModels/AlbumsPageViewModel.cs | 8 + .../ViewModels/ArtistsPageViewModel.cs | 8 + .../ViewModels/SongsPageViewModel.cs | 15 ++ .../Audiotica.Windows/Views/AlbumsPage.xaml | 6 + .../Audiotica.Windows/Views/ArtistsPage.xaml | 6 + .../Audiotica.Windows/Views/SongsPage.xaml | 12 +- 11 files changed, 511 insertions(+), 3 deletions(-) create mode 100644 Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml create mode 100644 Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs create mode 100644 Windows/Audiotica.Windows/Extensions/ListViewBindableSelectionHandler.cs create mode 100644 Windows/Audiotica.Windows/Extensions/ListViewExtensions.cs diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index f2ada4bd..09276510 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -136,6 +136,9 @@ LibraryHeader.xaml + + SelectModeCommandBar.xaml + LibraryDictionary.xaml @@ -157,6 +160,8 @@ + + @@ -295,6 +300,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml new file mode 100644 index 00000000..d30693fc --- /dev/null +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs new file mode 100644 index 00000000..78d5f669 --- /dev/null +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Windows.UI.Xaml; +using Audiotica.Core.Exceptions; +using Audiotica.Core.Extensions; +using Audiotica.Database.Models; +using Audiotica.Database.Services.Interfaces; +using Audiotica.Windows.Common; +using Audiotica.Windows.Services.Interfaces; +using Audiotica.Windows.Views; +using Autofac; + +namespace Audiotica.Windows.Controls +{ + public sealed partial class SelectModeCommandBar + { + public static readonly DependencyProperty SelectedItemsProperty = + DependencyProperty.Register("SelectedItems", typeof (ObservableCollection), + typeof (SelectModeCommandBar), null); + + public SelectModeCommandBar() + { + InitializeComponent(); + } + + public bool IsCatalog { get; set; } + + public ObservableCollection SelectedItems + { + get { return (ObservableCollection) GetValue(SelectedItemsProperty); } + set { SetValue(SelectedItemsProperty, value); } + } + + private List GetTracks() + { + List tracks; + + if (SelectedItems.FirstOrDefault() is Album) + tracks = SelectedItems.Cast().SelectMany(p => p.Tracks).ToList(); + else if (SelectedItems.FirstOrDefault() is Artist) + tracks = + SelectedItems.Cast() + .SelectMany(p => p.Tracks) + .Union(SelectedItems.Cast().SelectMany(p => p.TracksThatAppearsIn)) + .ToList(); + else + tracks = SelectedItems.Cast().ToList(); + return tracks; + } + + private async void Play_Click(object sender, RoutedEventArgs e) + { + using (var lifetimeScope = App.Current.Kernel.BeginScope()) + { + var playerService = lifetimeScope.Resolve(); + var tracks = GetTracks(); + await playerService.NewQueueAsync(tracks); + } + } + + private async void AddQueue_Click(object sender, RoutedEventArgs e) + { + using (var scope = App.Current.Kernel.BeginScope()) + { + var backgroundAudioService = scope.Resolve(); + try + { + var tracks = GetTracks(); + foreach (var track in tracks) + await backgroundAudioService.AddAsync(track); + CurtainPrompt.Show("Added to queue"); + } + catch (AppException ex) + { + CurtainPrompt.ShowError(ex.Message ?? "Something happened."); + } + } + } + + private async void AddUpNext_Click(object sender, RoutedEventArgs e) + { + using (var scope = App.Current.Kernel.BeginScope()) + { + var backgroundAudioService = scope.Resolve(); + try + { + var tracks = GetTracks(); + foreach (var track in tracks) + await backgroundAudioService.AddUpNextAsync(track); + CurtainPrompt.Show("Added up next"); + } + catch (AppException ex) + { + CurtainPrompt.ShowError(ex.Message ?? "Something happened."); + } + } + } + + private void Download_Click(object sender, RoutedEventArgs e) + { + using (var scope = App.Current.Kernel.BeginScope()) + { + var downloadService = scope.Resolve(); + var tracks = GetTracks().Where(p => p.IsDownloadable); + foreach (var track in tracks) + downloadService.StartDownloadAsync(track); + } + } + + private async void Delete_Click(object sender, RoutedEventArgs e) + { + using (var scope = App.Current.Kernel.BeginScope()) + { + var libraryService = scope.Resolve(); + var tracks = GetTracks(); + foreach (var track in tracks) + await libraryService.DeleteTrackAsync(track); + + // make sure to navigate away if album turns out empty + if (!IsCatalog && App.Current.NavigationService.CurrentPageType == typeof (AlbumPage)) + { + if ( + tracks.Select( + track => + libraryService.Albums.FirstOrDefault(p => p.Title.EqualsIgnoreCase(track.AlbumTitle))) + .Any(album => album == null)) + { + App.Current.NavigationService.GoBack(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Extensions/ListViewBindableSelectionHandler.cs b/Windows/Audiotica.Windows/Extensions/ListViewBindableSelectionHandler.cs new file mode 100644 index 00000000..e00c31bb --- /dev/null +++ b/Windows/Audiotica.Windows/Extensions/ListViewBindableSelectionHandler.cs @@ -0,0 +1,108 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using Windows.UI.Xaml.Controls; +using Audiotica.Core.Helpers; + +namespace Audiotica.Windows.Extensions +{ + /// + /// Handles synchronization of ListViewExtensions.BindableSelection to a ListView. + /// + public class ListViewBindableSelectionHandler + { + private readonly NotifyCollectionChangedEventHandler _handler; + private ObservableCollection _boundSelection; + private ListViewBase _listView; + + /// + /// Initializes a new instance of the class. + /// + /// The ListView. + /// The bound selection. + public ListViewBindableSelectionHandler( + ListViewBase listView, ObservableCollection boundSelection) + { + _handler = OnBoundSelectionChanged; + Attach(listView, boundSelection); + } + + private void Attach(ListViewBase listView, ObservableCollection boundSelection) + { + _listView = listView; + _listView.SelectionChanged += OnListViewSelectionChanged; + _boundSelection = boundSelection; + + foreach (var item in _boundSelection.Where(item => !_listView.SelectedItems.Contains(item))) + { + _listView.SelectedItems.Add(item); + } + + _boundSelection.CollectionChanged += OnBoundSelectionChanged; + } + + private void OnListViewSelectionChanged( + object sender, SelectionChangedEventArgs e) + { + foreach (var item in e.RemovedItems.Where(item => _boundSelection.Contains(item))) + { + _boundSelection.Remove(item); + } + + foreach (var item in e.AddedItems.Where(item => !_boundSelection.Contains(item))) + { + _boundSelection.Add(item); + } + } + + private void OnBoundSelectionChanged( + object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == + NotifyCollectionChangedAction.Reset) + { + _listView.SelectedItems.Clear(); + + foreach (var item in _boundSelection) + { + if (!_listView.SelectedItems.Contains(item)) + { + _listView.SelectedItems.Add(item); + } + } + + return; + } + + try + { + if (e.OldItems != null) + { + foreach (var item in e.OldItems.Cast().Where(item => _listView.SelectedItems.Contains(item)) + ) + { + _listView.SelectedItems.Remove(item); + } + } + + if (e.NewItems != null) + { + foreach ( + var item in e.NewItems.Cast().Where(item => !_listView.SelectedItems.Contains(item))) + { + _listView.SelectedItems.Add(item); + } + } + } + catch { } + } + + internal void Detach() + { + _listView.SelectionChanged -= OnListViewSelectionChanged; + _listView = null; + _boundSelection.CollectionChanged -= _handler; + _boundSelection = null; + } + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Extensions/ListViewExtensions.cs b/Windows/Audiotica.Windows/Extensions/ListViewExtensions.cs new file mode 100644 index 00000000..3a69ae04 --- /dev/null +++ b/Windows/Audiotica.Windows/Extensions/ListViewExtensions.cs @@ -0,0 +1,186 @@ +using System.Collections.ObjectModel; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Audiotica.Windows.Extensions +{ + /// + /// Extension methods and attached properties for the ListView class. + /// + public static class ListViewExtensions + { + /// + /// Scrolls a vertical ListView to the bottom. + /// + /// + public static void ScrollToBottom(this ListView listView) + { + var scrollViewer = listView.GetFirstDescendantOfType(); + scrollViewer.ChangeView(null, scrollViewer.ScrollableHeight, null); + } + + #region BindableSelection + + /// + /// BindableSelection Attached Dependency Property + /// + public static readonly DependencyProperty BindableSelectionProperty = + DependencyProperty.RegisterAttached( + "BindableSelection", + typeof (ObservableCollection), + typeof (ListViewExtensions), + new PropertyMetadata(null, OnBindableSelectionChanged)); + + /// + /// Gets the BindableSelection property. This dependency property + /// indicates the list of selected items that is synchronized + /// with the items selected in the ListView. + /// + public static ObservableCollection GetBindableSelection(DependencyObject d) + { + return (ObservableCollection) d.GetValue(BindableSelectionProperty); + } + + /// + /// Sets the BindableSelection property. This dependency property + /// indicates the list of selected items that is synchronized + /// with the items selected in the ListView. + /// + public static void SetBindableSelection( + DependencyObject d, + ObservableCollection value) + { + d.SetValue(BindableSelectionProperty, value); + } + + /// + /// Handles changes to the BindableSelection property. + /// + /// + /// The on which + /// the property has changed value. + /// + /// + /// Event data that is issued by any event that + /// tracks changes to the effective value of this property. + /// + private static void OnBindableSelectionChanged( + DependencyObject d, + DependencyPropertyChangedEventArgs e) + { + var oldBindableSelection = e.OldValue; + var newBindableSelection = GetBindableSelection(d); + + if (oldBindableSelection != null) + { + var handler = GetBindableSelectionHandler(d); + SetBindableSelectionHandler(d, null); + handler.Detach(); + } + + if (newBindableSelection != null) + { + var handler = new ListViewBindableSelectionHandler( + (ListViewBase) d, newBindableSelection); + SetBindableSelectionHandler(d, handler); + } + } + + #endregion + + #region BindableSelectionHandler + + /// + /// BindableSelectionHandler Attached Dependency Property + /// + public static readonly DependencyProperty BindableSelectionHandlerProperty = + DependencyProperty.RegisterAttached( + "BindableSelectionHandler", + typeof (ListViewBindableSelectionHandler), + typeof (ListViewExtensions), + new PropertyMetadata(null)); + + /// + /// Gets the BindableSelectionHandler property. This dependency property + /// indicates BindableSelectionHandler for a ListView - used + /// to manage synchronization of BindableSelection and SelectedItems. + /// + public static ListViewBindableSelectionHandler GetBindableSelectionHandler( + DependencyObject d) + { + return + (ListViewBindableSelectionHandler) + d.GetValue(BindableSelectionHandlerProperty); + } + + /// + /// Sets the BindableSelectionHandler property. This dependency property + /// indicates BindableSelectionHandler for a ListView - used to manage synchronization of BindableSelection and + /// SelectedItems. + /// + public static void SetBindableSelectionHandler( + DependencyObject d, + ListViewBindableSelectionHandler value) + { + d.SetValue(BindableSelectionHandlerProperty, value); + } + + #endregion + + #region ItemToBringIntoView + + /// + /// ItemToBringIntoView Attached Dependency Property + /// + public static readonly DependencyProperty ItemToBringIntoViewProperty = + DependencyProperty.RegisterAttached( + "ItemToBringIntoView", + typeof (object), + typeof (ListViewExtensions), + new PropertyMetadata(null, OnItemToBringIntoViewChanged)); + + /// + /// Gets the ItemToBringIntoView property. This dependency property + /// indicates the item that should be brought into view. + /// + public static object GetItemToBringIntoView(DependencyObject d) + { + return d.GetValue(ItemToBringIntoViewProperty); + } + + /// + /// Sets the ItemToBringIntoView property. This dependency property + /// indicates the item that should be brought into view when first set. + /// + public static void SetItemToBringIntoView(DependencyObject d, object value) + { + d.SetValue(ItemToBringIntoViewProperty, value); + } + + /// + /// Handles changes to the ItemToBringIntoView property. + /// + /// + /// The on which + /// the property has changed value. + /// + /// + /// Event data that is issued by any event that + /// tracks changes to the effective value of this property. + /// + private static void OnItemToBringIntoViewChanged( + DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var newItemToBringIntoView = + d.GetValue(ItemToBringIntoViewProperty); + + if (newItemToBringIntoView != null) + { + var listView = (ListView) d; + listView.ScrollIntoView(newItemToBringIntoView); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs index eee80e46..c1f2c85d 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumsPageViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; @@ -26,6 +27,7 @@ public class AlbumsPageViewModel : ViewModelBase private double _gridViewVerticalOffset; private bool? _isSelectMode = false; private double _listViewVerticalOffset; + private ObservableCollection _selectedItems = new ObservableCollection(); private CollectionViewSource _viewSource; public AlbumsPageViewModel(ILibraryService libraryService, ILibraryCollectionService libraryCollectionService, @@ -70,6 +72,12 @@ public double ListViewVerticalOffset set { Set(ref _listViewVerticalOffset, value); } } + public ObservableCollection SelectedItems + { + get { return _selectedItems; } + set { Set(ref _selectedItems, value); } + } + public CollectionViewSource ViewSource { get { return _viewSource; } diff --git a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs index 473b0e11..1d252bcc 100644 --- a/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/ArtistsPageViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; @@ -23,6 +24,7 @@ public class ArtistsPageViewModel : ViewModelBase private double _gridViewVerticalOffset; private bool? _isSelectMode = false; private double _listViewVerticalOffset; + private ObservableCollection _selectedItems = new ObservableCollection(); private CollectionViewSource _viewSource; public ArtistsPageViewModel(ILibraryCollectionService libraryCollectionService, @@ -80,6 +82,12 @@ public bool? IsSelectMode set { Set(ref _isSelectMode, value); } } + public ObservableCollection SelectedItems + { + get { return _selectedItems; } + set { Set(ref _selectedItems, value); } + } + private async void ShuffleAllExecute() { var playable = LibraryService.Tracks diff --git a/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs index 2cd2c157..cea79f36 100644 --- a/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/SongsPageViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; @@ -20,7 +21,9 @@ public class SongsPageViewModel : ViewModelBase private readonly ILibraryCollectionService _libraryCollectionService; private readonly IPlayerService _playerService; private readonly ISettingsUtility _settingsUtility; + private bool? _isSelectMode = false; private int _selectedIndex; + private ObservableCollection _selectedItems = new ObservableCollection(); private double _verticalOffset; private CollectionViewSource _viewSource; @@ -56,6 +59,12 @@ public SongsPageViewModel(ILibraryCollectionService libraryCollectionService, IL public ILibraryService LibraryService { get; set; } + public ObservableCollection SelectedItems + { + get { return _selectedItems; } + set { Set(ref _selectedItems, value); } + } + public CollectionViewSource ViewSource { get { return _viewSource; } @@ -74,6 +83,12 @@ public double VerticalOffset set { Set(ref _verticalOffset, value); } } + public bool? IsSelectMode + { + get { return _isSelectMode; } + set { Set(ref _isSelectMode, value); } + } + private async void ShuffleAllExecute() { var playable = LibraryService.Tracks diff --git a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml index c47c7eb1..ad7cdcf5 100644 --- a/Windows/Audiotica.Windows/Views/AlbumsPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumsPage.xaml @@ -12,6 +12,7 @@ xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:common="using:Audiotica.Windows.Common" + xmlns:extensions="using:Audiotica.Windows.Extensions" mc:Ignorable="d" DataContext="{Binding AlbumsPage, Source={StaticResource ViewModelLocator}}"> @@ -62,6 +63,7 @@ + + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml index 1a172997..ce62b95b 100644 --- a/Windows/Audiotica.Windows/Views/ArtistsPage.xaml +++ b/Windows/Audiotica.Windows/Views/ArtistsPage.xaml @@ -13,6 +13,7 @@ xmlns:tools="using:Audiotica.Windows.Tools" xmlns:converters="using:Audiotica.Windows.Tools.Converters" xmlns:common="using:Audiotica.Windows.Common" + xmlns:extensions="using:Audiotica.Windows.Extensions" mc:Ignorable="d" DataContext="{Binding ArtistsPage, Source={StaticResource ViewModelLocator}}"> @@ -62,6 +63,7 @@ + + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/SongsPage.xaml b/Windows/Audiotica.Windows/Views/SongsPage.xaml index 9da5987c..44e18912 100644 --- a/Windows/Audiotica.Windows/Views/SongsPage.xaml +++ b/Windows/Audiotica.Windows/Views/SongsPage.xaml @@ -12,6 +12,7 @@ xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:interactions="using:Audiotica.Windows.Interactions" + xmlns:extensions="using:Audiotica.Windows.Extensions" mc:Ignorable="d" DataContext="{Binding SongsPage, Source={StaticResource ViewModelLocator}}"> @@ -25,7 +26,8 @@ SortItems="{x:Bind ViewModel.SortItems}" ShuffleAllCommand="{x:Bind ViewModel.ShuffleAllCommand}" CurrentSortChangedCommand="{x:Bind ViewModel.SortChangedCommand}" - Margin="{StaticResource PageTopSideThickness}" /> + Margin="{StaticResource PageTopSideThickness}" + IsSelectMode="{x:Bind ViewModel.IsSelectMode, Mode=TwoWay}"/> - + + + \ No newline at end of file From 263e8b02301c5a7fb41a319b4adf4d280128a8f8 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 13:18:16 -0400 Subject: [PATCH 06/31] Add context menu to artists and albums --- .../Helpers/FlyoutEx.cs | 2 + .../Messages/AddToPlaylistMessage.cs | 19 +- .../ForegroundMessenger.cs | 4 +- .../Controls/SelectModeCommandBar.xaml.cs | 21 +- .../Controls/TrackViewer.xaml.cs | 6 +- .../DataTemplates/LibraryDictionary.xaml | 209 +++++++++++------- .../DataTemplates/LibraryDictionary.xaml.cs | 121 +++++++++- .../DesignTime/DesignPlayerService.cs | 10 + .../Services/Interfaces/IPlayerService.cs | 2 + .../Services/RunTime/PlayerService.cs | 31 ++- 10 files changed, 328 insertions(+), 97 deletions(-) diff --git a/Windows/Audiotica.Core.Windows/Helpers/FlyoutEx.cs b/Windows/Audiotica.Core.Windows/Helpers/FlyoutEx.cs index 42b703f9..fbc207f7 100644 --- a/Windows/Audiotica.Core.Windows/Helpers/FlyoutEx.cs +++ b/Windows/Audiotica.Core.Windows/Helpers/FlyoutEx.cs @@ -1,8 +1,10 @@ using System; using Windows.Foundation; +using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Media; namespace Audiotica.Core.Windows.Helpers { diff --git a/Windows/Audiotica.Core.Windows/Messages/AddToPlaylistMessage.cs b/Windows/Audiotica.Core.Windows/Messages/AddToPlaylistMessage.cs index 1871749c..e73b6b96 100644 --- a/Windows/Audiotica.Core.Windows/Messages/AddToPlaylistMessage.cs +++ b/Windows/Audiotica.Core.Windows/Messages/AddToPlaylistMessage.cs @@ -1,16 +1,29 @@ -using Audiotica.Database.Models; +using System.Collections.Generic; +using Audiotica.Database.Models; +using Newtonsoft.Json; namespace Audiotica.Core.Windows.Messages { public class AddToPlaylistMessage { + public AddToPlaylistMessage() + { + } + public AddToPlaylistMessage(QueueTrack track, int position) { - Track = track; + Tracks = new List {track}; + Position = position; + } + + public AddToPlaylistMessage(List tracks, int position) + { + Tracks = tracks; Position = position; } - public QueueTrack Track { get; set; } + public List Tracks { get; set; } + public int Position { get; set; } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows.Player/ForegroundMessenger.cs b/Windows/Audiotica.Windows.Player/ForegroundMessenger.cs index c17ef4f8..104f0a91 100644 --- a/Windows/Audiotica.Windows.Player/ForegroundMessenger.cs +++ b/Windows/Audiotica.Windows.Player/ForegroundMessenger.cs @@ -28,7 +28,7 @@ public void Dispose() public event EventHandler SkipToPrev; public event EventHandler TrackChanged; public event EventHandler> UpdatePlaylist; - public event TypedEventHandler AddToPlaylist; + public event TypedEventHandler, int> AddToPlaylist; /// /// Raised when a message is recieved from the foreground app @@ -66,7 +66,7 @@ private void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, { var addMessage = message as AddToPlaylistMessage; if (addMessage != null) - AddToPlaylist?.Invoke(addMessage.Track, addMessage.Position); + AddToPlaylist?.Invoke(addMessage.Tracks, addMessage.Position); } } } diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs index 78d5f669..0038d07f 100644 --- a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs @@ -32,7 +32,7 @@ public ObservableCollection SelectedItems set { SetValue(SelectedItemsProperty, value); } } - private List GetTracks() + private IEnumerable GetTracks() { List tracks; @@ -54,7 +54,8 @@ private async void Play_Click(object sender, RoutedEventArgs e) using (var lifetimeScope = App.Current.Kernel.BeginScope()) { var playerService = lifetimeScope.Resolve(); - var tracks = GetTracks(); + var tracks = GetTracks().Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); await playerService.NewQueueAsync(tracks); } } @@ -66,9 +67,10 @@ private async void AddQueue_Click(object sender, RoutedEventArgs e) var backgroundAudioService = scope.Resolve(); try { - var tracks = GetTracks(); - foreach (var track in tracks) - await backgroundAudioService.AddAsync(track); + var tracks = GetTracks() + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + await backgroundAudioService.AddAsync(tracks); CurtainPrompt.Show("Added to queue"); } catch (AppException ex) @@ -85,9 +87,10 @@ private async void AddUpNext_Click(object sender, RoutedEventArgs e) var backgroundAudioService = scope.Resolve(); try { - var tracks = GetTracks(); - foreach (var track in tracks) - await backgroundAudioService.AddUpNextAsync(track); + var tracks = GetTracks() + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + await backgroundAudioService.AddUpNextAsync(tracks); CurtainPrompt.Show("Added up next"); } catch (AppException ex) @@ -113,7 +116,7 @@ private async void Delete_Click(object sender, RoutedEventArgs e) using (var scope = App.Current.Kernel.BeginScope()) { var libraryService = scope.Resolve(); - var tracks = GetTracks(); + var tracks = GetTracks().ToList(); foreach (var track in tracks) await libraryService.DeleteTrackAsync(track); diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs index ed3ed140..07f63e67 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs @@ -149,11 +149,7 @@ private void Viewer_RightTapped(object sender, RightTappedRoutedEventArgs e) private void ExploreArtist_Click(object sender, RoutedEventArgs e) { - using (var scope = App.Current.Kernel.BeginScope()) - { - var navigationService = scope.Resolve(); - navigationService.Navigate(typeof (ArtistPage), Track.DisplayArtist); - } + App.Current.NavigationService.Navigate(typeof (ArtistPage), Track.DisplayArtist); } private void Download_Click(object sender, RoutedEventArgs e) diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml index 7100e9ce..cb9bb4cd 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml @@ -5,7 +5,7 @@ xmlns:controls="using:Audiotica.Windows.Controls" xmlns:databaseModels="using:Audiotica.Database.Models" xmlns:webModels="using:Audiotica.Web.Models"> - + @@ -28,93 +28,151 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -153,8 +211,9 @@ - + @@ -167,12 +226,12 @@ + ImageSource="{x:Bind Artwork, Converter={StaticResource ImageSourceConverter}, ConverterParameter=150}" /> + VerticalAlignment="Center" TextAlignment="Center" + TextTrimming="CharacterEllipsis" /> \ No newline at end of file diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml.cs b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml.cs index 83bba4b4..fb319bce 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml.cs +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml.cs @@ -1,4 +1,19 @@ -namespace Audiotica.Windows.DataTemplates +using System.Collections.Generic; +using System.Linq; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Audiotica.Core.Exceptions; +using Audiotica.Core.Extensions; +using Audiotica.Core.Windows.Helpers; +using Audiotica.Database.Models; +using Audiotica.Database.Services.Interfaces; +using Audiotica.Windows.Common; +using Audiotica.Windows.Services.Interfaces; +using Audiotica.Windows.Views; +using Autofac; + +namespace Audiotica.Windows.DataTemplates { public sealed partial class LibraryDictionary { @@ -6,5 +21,109 @@ public LibraryDictionary() { InitializeComponent(); } + + private void Panel_RightTapped(object sender, RightTappedRoutedEventArgs e) + { + var panel = (Grid) sender; + FlyoutEx.ShowAttachedFlyoutAtPointer(panel); + } + + private static IEnumerable GetTracks(object item) + { + List tracks; + + if (item is Album) + tracks = item.As().Tracks.ToList(); + else + tracks = + item.As().Tracks.Union(item.As().TracksThatAppearsIn) + .ToList(); + return tracks; + } + + private async void PlayButton_Click(object sender, RoutedEventArgs e) + { + var item = ((FrameworkElement) sender).DataContext; + using (var lifetimeScope = App.Current.Kernel.BeginScope()) + { + var playerService = lifetimeScope.Resolve(); + var tracks = GetTracks(item) + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + await playerService.NewQueueAsync(tracks); + } + } + + private async void AddQueue_Click(object sender, RoutedEventArgs e) + { + var item = ((FrameworkElement) sender).DataContext; + using (var scope = App.Current.Kernel.BeginScope()) + { + var backgroundAudioService = scope.Resolve(); + try + { + var tracks = GetTracks(item) + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + await backgroundAudioService.AddAsync(tracks); + CurtainPrompt.Show("Added to queue"); + } + catch (AppException ex) + { + CurtainPrompt.ShowError(ex.Message ?? "Something happened."); + } + } + } + + private async void AddUpNext_Click(object sender, RoutedEventArgs e) + { + var item = ((FrameworkElement) sender).DataContext; + using (var scope = App.Current.Kernel.BeginScope()) + { + var backgroundAudioService = scope.Resolve(); + try + { + var tracks = GetTracks(item) + .Where(p => p.Status == TrackStatus.None || p.Status == TrackStatus.Downloading) + .ToList(); + await backgroundAudioService.AddUpNextAsync(tracks); + CurtainPrompt.Show("Added up next"); + } + catch (AppException ex) + { + CurtainPrompt.ShowError(ex.Message ?? "Something happened."); + } + } + } + + private void Download_Click(object sender, RoutedEventArgs e) + { + var item = ((FrameworkElement) sender).DataContext; + using (var scope = App.Current.Kernel.BeginScope()) + { + var downloadService = scope.Resolve(); + var tracks = GetTracks(item).Where(p => p.IsDownloadable); + foreach (var track in tracks) + downloadService.StartDownloadAsync(track); + } + } + + private async void Delete_Click(object sender, RoutedEventArgs e) + { + var item = ((FrameworkElement) sender).DataContext; + using (var scope = App.Current.Kernel.BeginScope()) + { + var libraryService = scope.Resolve(); + var tracks = GetTracks(item); + foreach (var track in tracks) + await libraryService.DeleteTrackAsync(track); + } + } + + private void ExploreArtist_Click(object sender, RoutedEventArgs e) + { + var item = (Album) ((FrameworkElement) sender).DataContext; + App.Current.NavigationService.Navigate(typeof (ArtistPage), item.Artist.Name); + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs index d5318860..1ab8601e 100644 --- a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs @@ -39,6 +39,11 @@ public Task AddAsync(Track track, int position = -1) throw new NotImplementedException(); } + public Task AddAsync(IEnumerable tracks, int position = -1) + { + throw new NotImplementedException(); + } + public Task AddAsync(WebSong webSong, int position = -1) { throw new NotImplementedException(); @@ -49,6 +54,11 @@ public Task AddUpNextAsync(Track track) throw new NotImplementedException(); } + public Task AddUpNextAsync(IEnumerable tracks) + { + throw new NotImplementedException(); + } + public Task AddUpNextAsync(WebSong webSong) { throw new NotImplementedException(); diff --git a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs index ff4c0ea7..2b48bad6 100644 --- a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs @@ -34,9 +34,11 @@ public interface IPlayerService /// The position. /// Task AddAsync(Track track, int position = -1); + Task AddAsync(IEnumerable tracks, int position = -1); Task AddAsync(WebSong webSong, int position = -1); Task AddUpNextAsync(Track track); + Task AddUpNextAsync(IEnumerable tracks); Task AddUpNextAsync(WebSong webSong); Task NewQueueAsync(IEnumerable tracks); diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index 5808bb0b..79848d5d 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -83,16 +83,30 @@ public async Task AddAsync(Track track, int position = -1) return Add(track, position); } + public async Task AddAsync(IEnumerable tracks, int position = -1) + { + var arr = tracks.ToArray(); + foreach (var track in arr) + await PrepareTrackAsync(track); + Add(arr, position); + } + public async Task AddAsync(WebSong webSong, int position = -1) { var track = await ConvertToTrackAsync(webSong); return await AddAsync(track, position); } - public async Task AddUpNextAsync(Track track) + public Task AddUpNextAsync(Track track) + { + var currentPosition = PlaybackQueue.IndexOf(PlaybackQueue.FirstOrDefault(p => p.Id == CurrentQueueId)); + return AddAsync(track, currentPosition + 1); + } + + public async Task AddUpNextAsync(IEnumerable tracks) { var currentPosition = PlaybackQueue.IndexOf(PlaybackQueue.FirstOrDefault(p => p.Id == CurrentQueueId)); - return await AddAsync(track, currentPosition + 1); + await AddAsync(tracks, currentPosition + 1); } public async Task AddUpNextAsync(WebSong webSong) @@ -250,6 +264,19 @@ private QueueTrack Add(Track track, int position = -1) return queue; } + private void Add(IEnumerable tracks, int position = -1) + { + var queue = tracks.Select(track => new QueueTrack(track)).ToList(); + MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); + if (position > -1 && position < PlaybackQueue.Count) + foreach (var item in queue) + { + PlaybackQueue.Insert(position++, item); + } + else + PlaybackQueue.AddRange(queue); + } + private async Task PrepareTrackAsync(Track track) { switch (track.Status) From 2c895607e2c7d4869a2d3ed489d093302ccb33d1 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 13:18:28 -0400 Subject: [PATCH 07/31] Fix queue bug --- .../Audiotica.Windows.Player/PlayerWrapper.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs index 51aca699..f2a78b7e 100644 --- a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs +++ b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs @@ -156,23 +156,32 @@ public async void CreatePlaybackList(IEnumerable queues) _mediaPlaybackList.CurrentItemChanged += MediaPlaybackListOnCurrentItemChanged; } - public void AddToPlaybackList(QueueTrack queue, int position) + public async void AddToPlaybackList(List queue, int position) { if (_mediaPlaybackList == null || BackgroundMediaPlayer.Current.Source != _mediaPlaybackList) - CreatePlaybackList(new[] {queue}); + CreatePlaybackList(queue); else { - var source = MediaSource.CreateFromUri(new Uri(queue.Track.AudioWebUri)); - source.Queue(queue); - - if (position > -1 && position < _mediaPlaybackList.Items.Count) + foreach (var item in queue) { - _mediaPlaybackList.Items.Insert(position, new MediaPlaybackItem(source)); + MediaSource source; + if (item.Track.Type == TrackType.Stream) + source = MediaSource.CreateFromUri(new Uri(item.Track.AudioWebUri)); + else + { + source = MediaSource.CreateFromStorageFile( + await StorageHelper.GetFileFromPathAsync(item.Track.AudioLocalUri)); + } + source.Queue(item); + var playbackItem = new MediaPlaybackItem(source); + + if (position > -1 && position < _mediaPlaybackList.Items.Count) + _mediaPlaybackList.Items.Insert(position++, playbackItem); + else + _mediaPlaybackList.Items.Add(playbackItem); } - else - _mediaPlaybackList.Items.Add(new MediaPlaybackItem(source)); } } @@ -338,7 +347,7 @@ private void UnsubscribeFromMessenger() _foregroundMessenger.UpdatePlaylist -= ForegroundMessengerOnUpdatePlaylist; } - private void ForegroundMessengerOnAddToPlaylist(QueueTrack queueTrack, int position) + private void ForegroundMessengerOnAddToPlaylist(List queueTrack, int position) { AddToPlaybackList(queueTrack, position); } From 60aa1b088add8f59ed717b2434280117bb059275 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 13:38:15 -0400 Subject: [PATCH 08/31] Prev restarts playback if secs > 5 --- Windows/Audiotica.Windows.Player/PlayerWrapper.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs index f2a78b7e..9e45e7cd 100644 --- a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs +++ b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs @@ -114,8 +114,14 @@ public void SkipToNext() /// public void SkipToPrev() { - _smtcWrapper.PlaybackStatus = MediaPlaybackStatus.Changing; - _mediaPlaybackList.MovePrevious(); + if (BackgroundMediaPlayer.Current.Position.TotalSeconds > 5) + BackgroundMediaPlayer.Current.Position = TimeSpan.Zero; + + else + { + _smtcWrapper.PlaybackStatus = MediaPlaybackStatus.Changing; + _mediaPlaybackList.MovePrevious(); + } // TODO: Work around playlist bug that doesn't continue playing after a switch; remove later BackgroundMediaPlayer.Current.Play(); From 6cc6d75986b07297f4bc4ebc695b626bdd2cc8c1 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Tue, 15 Sep 2015 14:16:43 -0400 Subject: [PATCH 09/31] Add Save button on album page (catalog) --- Audiotica.Converters/WebToAlbumConverter.cs | 32 +++++- .../Controls/TrackViewer.xaml.cs | 2 +- .../ViewModels/AlbumPageViewModel.cs | 102 ++++++++++++------ .../Audiotica.Windows/Views/AlbumPage.xaml | 8 ++ 4 files changed, 107 insertions(+), 37 deletions(-) diff --git a/Audiotica.Converters/WebToAlbumConverter.cs b/Audiotica.Converters/WebToAlbumConverter.cs index 30c4f982..7f62c589 100644 --- a/Audiotica.Converters/WebToAlbumConverter.cs +++ b/Audiotica.Converters/WebToAlbumConverter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Audiotica.Core.Common; using Audiotica.Core.Extensions; @@ -50,19 +51,40 @@ public async Task ConvertAsync(WebAlbum other, bool ignoreLibrary = false { await FillPartialAsync(other); + // fill the album partial for each track + foreach (var webSong in other.Tracks) + webSong.Album = other; + var album = new Album { Title = other.Title, ArtworkUri = other.Artwork.ToString(), Artist = await _webArtistConverter.ConvertAsync(other.Artist), Year = other.ReleaseDate?.Year, - Tracks = - other.Tracks != null - ? new OptimizedObservableCollection( - await Task.WhenAll(other.Tracks.Select(p => _webTrackConverter.ConvertAsync(p)))) - : null }; + if (other.Tracks != null) + { + // only let 10 concurrent conversions + using (var semaphoreSlim = new SemaphoreSlim(10, 10)) + { + // ReSharper disable AccessToDisposedClosure + var trackTasks = other.Tracks.Select(async p => + { + await semaphoreSlim.WaitAsync(); + var track = await _webTrackConverter.ConvertAsync(p); + semaphoreSlim.Release(); + return track; + }); + album.Tracks = + other.Tracks != null + ? new OptimizedObservableCollection( + await Task.WhenAll(trackTasks)) + : null; + // ReSharper restore AccessToDisposedClosure + } + } + var libraryAlbum = _libraryService.Albums.FirstOrDefault(p => p.Title.EqualsIgnoreCase(album.Title)); other.PreviousConversion = libraryAlbum ?? album; diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs index 07f63e67..9f76395a 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs @@ -97,7 +97,7 @@ private async void AddCollection_Click(object sender, RoutedEventArgs e) catch (AppException ex) { Track.Status = TrackStatus.None; - CurtainPrompt.ShowError(ex.Message ?? "Problem saving song."); + CurtainPrompt.ShowError(ex.Message ?? "Problem saving: " + Track); } finally { diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs index 1dea8e79..f3654b82 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs @@ -6,6 +6,7 @@ using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; using Audiotica.Core.Common; +using Audiotica.Core.Exceptions; using Audiotica.Core.Extensions; using Audiotica.Core.Utilities.Interfaces; using Audiotica.Core.Windows.Extensions; @@ -29,8 +30,9 @@ public class AlbumPageViewModel : ViewModelBase private readonly ILibraryService _libraryService; private readonly List _metadataProviders; private readonly INavigationService _navigationService; - private readonly ISettingsUtility _settingsUtility; private readonly IPlayerService _playerService; + private readonly ISettingsUtility _settingsUtility; + private readonly ITrackSaveService _trackSaveService; private readonly IConverter _webAlbumConverter; private Album _album; private SolidColorBrush _backgroundBrush; @@ -39,24 +41,28 @@ public class AlbumPageViewModel : ViewModelBase public AlbumPageViewModel(ILibraryService libraryService, INavigationService navigationService, IEnumerable metadataProviders, IConverter webAlbumConverter, - ISettingsUtility settingsUtility, IPlayerService playerService) + ISettingsUtility settingsUtility, IPlayerService playerService, ITrackSaveService trackSaveService) { _libraryService = libraryService; _navigationService = navigationService; _webAlbumConverter = webAlbumConverter; _settingsUtility = settingsUtility; _playerService = playerService; + _trackSaveService = trackSaveService; _metadataProviders = metadataProviders.FilterAndSort(); ViewInCatalogCommand = new Command(ViewInCatalogExecute); PlayAllCommand = new Command(PlayAllExecute); + SaveAllCommand = new Command(SaveAllExecute); if (IsInDesignMode) OnNavigatedTo(new AlbumPageParameter("Kauai", "Childish Gambino"), NavigationMode.New, new Dictionary()); } - public Command PlayAllCommand { get; set; } + public Command SaveAllCommand { get; } + + public Command PlayAllCommand { get; } public Command ViewInCatalogCommand { get; } @@ -84,6 +90,22 @@ public bool IsCatalogMode set { Set(ref _isCatalogMode, value); } } + private async void SaveAllExecute(object sender) + { + foreach (var track in Album.Tracks.Where(p => !p.IsFromLibrary)) + { + try + { + await _trackSaveService.SaveAsync(track); + } + catch (AppException ex) + { + track.Status = TrackStatus.None; + CurtainPrompt.ShowError(ex.Message ?? "Problem saving: " + track); + } + } + } + private void PlayAllExecute() { if (Album.Tracks.Count == 0) return; @@ -112,23 +134,7 @@ public override sealed async void OnNavigatedTo(object parameter, NavigationMode { try { - var webAlbum = albumParameter.WebAlbum; - - if (webAlbum == null) - { - if (albumParameter.Provider != null) - { - var provider = _metadataProviders.FirstOrDefault(p => p.GetType() == albumParameter.Provider); - webAlbum = await provider.GetAlbumAsync(albumParameter.Token); - } - else - { - webAlbum = await GetAlbumByTitleAsync(albumParameter.Title, albumParameter.Artist); - } - } - - if (webAlbum != null) - Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); + await LoadAsync(albumParameter); } catch { @@ -138,6 +144,7 @@ public override sealed async void OnNavigatedTo(object parameter, NavigationMode if (Album == null) { _navigationService.GoBack(); + return; } } @@ -145,25 +152,58 @@ public override sealed async void OnNavigatedTo(object parameter, NavigationMode DetectColorFromArtwork(); } + private async Task LoadAsync(AlbumPageParameter albumParameter) + { + var webAlbum = albumParameter.WebAlbum; + if (webAlbum == null) + { + if (albumParameter.Provider != null) + { + var provider = _metadataProviders.FirstOrDefault(p => p.GetType() == albumParameter.Provider); + webAlbum = await provider.GetAlbumAsync(albumParameter.Token); + + if (webAlbum != null) + Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); + } + else + { + for (var i = 0; i < _metadataProviders.Count; i++) + { + try + { + webAlbum = await GetAlbumByTitleAsync(albumParameter.Title, albumParameter.Artist, i); + + if (webAlbum != null) + { + Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); + break; + } + } + catch + { + // ignored + } + } + } + } + } + public override void OnNavigatedFrom() { // Bug: if we don't reset the theme when we go out it fucks with the TrackViewer control on other pages RequestedTheme = ElementTheme.Default; } - private async Task GetAlbumByTitleAsync(string title, string artist) + private async Task GetAlbumByTitleAsync(string title, string artist, int providerIndex) { - foreach (var provider in _metadataProviders) + try { - try - { - var webAlbum = await provider.GetAlbumByTitleAsync(title, artist); - if (webAlbum != null) return webAlbum; - } - catch - { - // ignored - } + var webAlbum = await _metadataProviders[providerIndex].GetAlbumByTitleAsync(title, artist); + if (webAlbum != null) return webAlbum; + } + catch + { + // ignored } return null; } diff --git a/Windows/Audiotica.Windows/Views/AlbumPage.xaml b/Windows/Audiotica.Windows/Views/AlbumPage.xaml index ae75ef0f..b42caf98 100644 --- a/Windows/Audiotica.Windows/Views/AlbumPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumPage.xaml @@ -62,6 +62,14 @@ + + + + + + From 66b3e4512c582673526adca37cfe58c2f281b7b4 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Wed, 23 Sep 2015 18:03:10 -0400 Subject: [PATCH 10/31] Fix playback bug and add plain player bar --- Audiotica.Converters/WebToAlbumConverter.cs | 1 + .../Audiotica.Windows.Player/PlayerWrapper.cs | 17 +- .../Audiotica.Windows.csproj | 1 + .../Services/RunTime/PlayerService.cs | 10 +- Windows/Audiotica.Windows/Shell.xaml | 236 ++++++++++-------- Windows/Audiotica.Windows/Shell.xaml.cs | 5 + .../Audiotica.Windows/Styles/TextStyles.xaml | 7 +- .../ViewModels/PlayerBarViewModel.cs | 44 ++++ .../Audiotica.Windows/Views/AlbumPage.xaml | 6 +- 9 files changed, 205 insertions(+), 122 deletions(-) create mode 100644 Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs diff --git a/Audiotica.Converters/WebToAlbumConverter.cs b/Audiotica.Converters/WebToAlbumConverter.cs index 7f62c589..151cbba3 100644 --- a/Audiotica.Converters/WebToAlbumConverter.cs +++ b/Audiotica.Converters/WebToAlbumConverter.cs @@ -63,6 +63,7 @@ public async Task ConvertAsync(WebAlbum other, bool ignoreLibrary = false Year = other.ReleaseDate?.Year, }; + // TODO: ISupportIncrementalLoading? if (other.Tracks != null) { // only let 10 concurrent conversions diff --git a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs index 9e45e7cd..76073242 100644 --- a/Windows/Audiotica.Windows.Player/PlayerWrapper.cs +++ b/Windows/Audiotica.Windows.Player/PlayerWrapper.cs @@ -382,21 +382,16 @@ private void ForegroundMessengerOnUpdatePlaylist(object sender, List private void ForegroundMessengerOnTrackChanged(object sender, string queueId) { - var index = _mediaPlaybackList.Items.ToList().FindIndex(i => i.Source.Queue().Id == queueId); + var queue = _mediaPlaybackList.Items.Select(p => p.Source.Queue()).ToList(); + var index = queue.FindIndex(i => i.Id == queueId); + if (index < 0) return; Debug.WriteLine("Skipping to track " + index); _smtcWrapper.PlaybackStatus = MediaPlaybackStatus.Changing; - try - { - _mediaPlaybackList.MoveTo((uint) index); + _mediaPlaybackList.MoveTo((uint)index); - // TODO: Work around playlist bug that doesn't continue playing after a switch; remove later - BackgroundMediaPlayer.Current.Play(); - } - catch - { - // ignored - } + // TODO: Work around playlist bug that doesn't continue playing after a switch; remove later + BackgroundMediaPlayer.Current.Play(); } private void ForegroundMessengerOnStartPlayback(object sender, EventArgs eventArgs) diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index 09276510..b861e2a5 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -220,6 +220,7 @@ + diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index 79848d5d..d58a3abe 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -80,7 +80,7 @@ public bool StartBackgroundTask() public async Task AddAsync(Track track, int position = -1) { await PrepareTrackAsync(track); - return Add(track, position); + return await InternalAddAsync(track, position); } public async Task AddAsync(IEnumerable tracks, int position = -1) @@ -121,6 +121,7 @@ public async Task NewQueueAsync(IEnumerable tracks) foreach (var track in arr) await PrepareTrackAsync(track); var newQueue = arr.Select(track => new QueueTrack(track)).ToList(); + PlaybackQueue.SwitchTo(newQueue); MessageHelper.SendMessageToBackground(new UpdatePlaylistMessage(newQueue)); } @@ -253,21 +254,21 @@ private void UpdatePlaybackQueue() } } - private QueueTrack Add(Track track, int position = -1) + private async Task InternalAddAsync(Track track, int position = -1) { var queue = new QueueTrack(track); - MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); if (position > -1 && position < PlaybackQueue.Count) PlaybackQueue.Insert(position, queue); else PlaybackQueue.Add(queue); + MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); + await Task.Delay(25); return queue; } private void Add(IEnumerable tracks, int position = -1) { var queue = tracks.Select(track => new QueueTrack(track)).ToList(); - MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); if (position > -1 && position < PlaybackQueue.Count) foreach (var item in queue) { @@ -275,6 +276,7 @@ private void Add(IEnumerable tracks, int position = -1) } else PlaybackQueue.AddRange(queue); + MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); } private async Task PrepareTrackAsync(Track track) diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 0ad3e836..5b3d476b 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -8,103 +8,108 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - - - - - + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs new file mode 100644 index 00000000..86637a60 --- /dev/null +++ b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs @@ -0,0 +1,44 @@ +using System.Linq; +using Audiotica.Core.Utilities.Interfaces; +using Audiotica.Database.Models; +using Audiotica.Windows.Services.Interfaces; +using Audiotica.Windows.Tools.Mvvm; + +namespace Audiotica.Windows.ViewModels +{ + public class PlayerBarViewModel : ViewModelBase + { + private readonly IDispatcherUtility _dispatcherUtility; + private readonly IPlayerService _playerService; + private QueueTrack _currentQueueTrack; + + public PlayerBarViewModel(IPlayerService playerService, IDispatcherUtility dispatcherUtility) + { + _playerService = playerService; + _dispatcherUtility = dispatcherUtility; + _playerService.TrackChanged += PlayerServiceOnTrackChanged; + + PlayPauseCommand = new Command(() => _playerService.PlayOrPause()); + NextCommand = new Command(() => _playerService.Next()); + PrevCommand = new Command(() => _playerService.Previous()); + } + + public Command PrevCommand { get; } + + public Command NextCommand { get; } + + public Command PlayPauseCommand { get; } + + public QueueTrack CurrentQueueTrack + { + get { return _currentQueueTrack; } + set { Set(ref _currentQueueTrack, value); } + } + + private void PlayerServiceOnTrackChanged(object sender, string s) + { + CurrentQueueTrack = + _playerService.PlaybackQueue.FirstOrDefault(queueTrack => queueTrack.Id == s); + } + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/AlbumPage.xaml b/Windows/Audiotica.Windows/Views/AlbumPage.xaml index b42caf98..42c2e433 100644 --- a/Windows/Audiotica.Windows/Views/AlbumPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumPage.xaml @@ -49,7 +49,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -65,7 +65,7 @@ - + From 3067f36d91eb399e3bf8afce2e4b104b47432696 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Wed, 23 Sep 2015 18:17:05 -0400 Subject: [PATCH 11/31] Bind playback position/duration to slider --- Windows/Audiotica.Windows/Shell.xaml | 2 +- .../ViewModels/PlayerBarViewModel.cs | 57 +++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 5b3d476b..d2b48670 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -157,7 +157,7 @@ - + diff --git a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs index 86637a60..3b04b4e7 100644 --- a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs @@ -1,5 +1,8 @@ +using System; using System.Linq; -using Audiotica.Core.Utilities.Interfaces; +using Windows.Media.Playback; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; using Audiotica.Database.Models; using Audiotica.Windows.Services.Interfaces; using Audiotica.Windows.Tools.Mvvm; @@ -8,19 +11,25 @@ namespace Audiotica.Windows.ViewModels { public class PlayerBarViewModel : ViewModelBase { - private readonly IDispatcherUtility _dispatcherUtility; private readonly IPlayerService _playerService; + private readonly DispatcherTimer _timer; private QueueTrack _currentQueueTrack; + private double _playbackDuration; + private double _playbackPosition; + private Symbol _playPauseIcon = Symbol.Play; - public PlayerBarViewModel(IPlayerService playerService, IDispatcherUtility dispatcherUtility) + public PlayerBarViewModel(IPlayerService playerService) { _playerService = playerService; - _dispatcherUtility = dispatcherUtility; _playerService.TrackChanged += PlayerServiceOnTrackChanged; + _playerService.MediaStateChanged += PlayerServiceOnMediaStateChanged; PlayPauseCommand = new Command(() => _playerService.PlayOrPause()); NextCommand = new Command(() => _playerService.Next()); PrevCommand = new Command(() => _playerService.Previous()); + + _timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(500)}; + _timer.Tick += TimerOnTick; } public Command PrevCommand { get; } @@ -35,6 +44,46 @@ public QueueTrack CurrentQueueTrack set { Set(ref _currentQueueTrack, value); } } + public Symbol PlayPauseIcon + { + get { return _playPauseIcon; } + set { Set(ref _playPauseIcon, value); } + } + + public double PlaybackPosition + { + get { return _playbackPosition; } + set { Set(ref _playbackPosition, value); } + } + + public double PlaybackDuration + { + get { return _playbackDuration; } + set { Set(ref _playbackDuration, value); } + } + + private void TimerOnTick(object sender, object o) + { + PlaybackPosition = BackgroundMediaPlayer.Current.Position.TotalMilliseconds; + PlaybackDuration = BackgroundMediaPlayer.Current.NaturalDuration.TotalMilliseconds; + } + + private void PlayerServiceOnMediaStateChanged(object sender, MediaPlayerState mediaPlayerState) + { + var icon = Symbol.Play; + switch (mediaPlayerState) + { + case MediaPlayerState.Playing: + icon = Symbol.Pause; + _timer.Start(); + break; + default: + _timer.Stop(); + break; + } + PlayPauseIcon = icon; + } + private void PlayerServiceOnTrackChanged(object sender, string s) { CurrentQueueTrack = From 89753fe7345ee512ebbe11c408e7d607c947a8a2 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 11:03:46 -0400 Subject: [PATCH 12/31] Show now playing indicator next to track closes #274 --- .../Controls/TrackNarrowViewer.xaml.cs | 40 ++++++++++++++- .../Controls/TrackViewer.xaml | 7 ++- .../Controls/TrackViewer.xaml.cs | 50 +++++++++++++++---- .../DataTemplates/LibraryDictionary.xaml | 8 +-- .../DesignTime/DesignPlayerService.cs | 1 + .../Services/Interfaces/IPlayerService.cs | 1 + .../Services/RunTime/PlayerService.cs | 5 +- Windows/Audiotica.Windows/Shell.xaml | 8 +-- .../Styles/ControlStyles.xaml | 4 +- 9 files changed, 99 insertions(+), 25 deletions(-) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs index 59ab7992..27099482 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; @@ -16,7 +18,7 @@ namespace Audiotica.Windows.Controls { // TODO: find a way to get state triggers to work on usercontrol, then we won't need a seperate control _sight_ (hopefully just a bug on the current SDK) - public sealed partial class TrackNarrowViewer + public sealed partial class TrackNarrowViewer: INotifyPropertyChanged { public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof (bool), typeof (TrackNarrowViewer), null); @@ -25,12 +27,23 @@ public sealed partial class TrackNarrowViewer DependencyProperty.Register("IsCatalog", typeof(bool), typeof(TrackViewer), null); private Track _track; + private bool _isPlaying; public TrackNarrowViewer() { InitializeComponent(); } + public bool IsPlaying + { + get { return _isPlaying; } + set + { + _isPlaying = value; + OnPropertyChanged(); + } + } + public bool IsSelected { @@ -57,6 +70,21 @@ public Track Track } } + private void TrackChanged() + { + var player = App.Current.Kernel.Resolve(); + if (player.CurrentQueueTrack?.Track != null) + IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || + player.CurrentQueueTrack.Track.Id == Track.Id; + else + IsPlaying = false; + + player.TrackChanged -= PlayerOnTrackChanged; + player.TrackChanged += PlayerOnTrackChanged; + } + + private void PlayerOnTrackChanged(object sender, string s) => TrackChanged(); + private async void PlayButton_Click(object sender, RoutedEventArgs e) { using (var lifetimeScope = App.Current.Kernel.BeginScope()) @@ -68,6 +96,7 @@ private async void PlayButton_Click(object sender, RoutedEventArgs e) // player auto plays when there is only one track if (playerService.PlaybackQueue.Count > 1) playerService.Play(queue); + IsSelected = false; } catch (AppException ex) { @@ -176,5 +205,12 @@ private async void Delete_Click(object sender, RoutedEventArgs e) } } } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml index 10a1e1ca..0910f687 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml @@ -40,7 +40,7 @@ - + + + + diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs index 9f76395a..b091f0e3 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs @@ -1,13 +1,9 @@ -using System; +using System.ComponentModel; using System.Linq; -using Windows.Foundation; -using Windows.UI; -using Windows.UI.Popups; +using System.Runtime.CompilerServices; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Audiotica.Core.Exceptions; using Audiotica.Core.Extensions; using Audiotica.Core.Windows.Helpers; @@ -15,19 +11,20 @@ using Audiotica.Database.Services.Interfaces; using Audiotica.Windows.Common; using Audiotica.Windows.Services.Interfaces; -using Audiotica.Windows.Services.NavigationService; using Audiotica.Windows.Views; using Autofac; namespace Audiotica.Windows.Controls { - public sealed partial class TrackViewer + public sealed partial class TrackViewer : INotifyPropertyChanged { public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof (bool), typeof (TrackViewer), null); public static readonly DependencyProperty IsCatalogProperty = - DependencyProperty.Register("IsCatalog", typeof(bool), typeof(TrackViewer), null); + DependencyProperty.Register("IsCatalog", typeof (bool), typeof (TrackViewer), null); + + private bool _isPlaying; private Track _track; @@ -36,6 +33,16 @@ public TrackViewer() InitializeComponent(); } + public bool IsPlaying + { + get { return _isPlaying; } + set + { + _isPlaying = value; + OnPropertyChanged(); + } + } + public bool IsSelected { @@ -59,9 +66,27 @@ public Track Track { _track = value; Bindings.Update(); + TrackChanged(); } } + public event PropertyChangedEventHandler PropertyChanged; + + private void TrackChanged() + { + var player = App.Current.Kernel.Resolve(); + if (player.CurrentQueueTrack?.Track != null) + IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || + player.CurrentQueueTrack.Track.Id == Track.Id; + else + IsPlaying = false; + + player.TrackChanged -= PlayerOnTrackChanged; + player.TrackChanged += PlayerOnTrackChanged; + } + + private void PlayerOnTrackChanged(object sender, string s) => TrackChanged(); + private async void PlayButton_Click(object sender, RoutedEventArgs e) { using (var lifetimeScope = App.Current.Kernel.BeginScope()) @@ -73,6 +98,7 @@ private async void PlayButton_Click(object sender, RoutedEventArgs e) // player auto plays when there is only one track if (playerService.PlaybackQueue.Count > 1) playerService.Play(queue); + IsSelected = false; } catch (AppException ex) { @@ -144,7 +170,6 @@ private void Viewer_RightTapped(object sender, RightTappedRoutedEventArgs e) { var grid = (Grid) sender; FlyoutEx.ShowAttachedFlyoutAtPointer(grid); - } private void ExploreArtist_Click(object sender, RoutedEventArgs e) @@ -177,5 +202,10 @@ private async void Delete_Click(object sender, RoutedEventArgs e) } } } + + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml index cb9bb4cd..222ae89a 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml @@ -8,23 +8,23 @@ + IsSelected="{Binding Tag, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> + IsSelected="{Binding Tag, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> + IsSelected="{Binding Tag, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> + IsSelected="{Binding Tag, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> diff --git a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs index 1ab8601e..95511679 100644 --- a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs @@ -19,6 +19,7 @@ public DesignPlayerService() public bool IsBackgroundTaskRunning { get; } public MediaPlayerState CurrentState { get; set; } public string CurrentQueueId { get; } + public QueueTrack CurrentQueueTrack { get; } public OptimizedObservableCollection PlaybackQueue { get; } public event EventHandler MediaStateChanged; public event EventHandler TrackChanged; diff --git a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs index 2b48bad6..1d706c22 100644 --- a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs @@ -13,6 +13,7 @@ public interface IPlayerService bool IsBackgroundTaskRunning { get; } MediaPlayerState CurrentState { get; set; } string CurrentQueueId { get; } + QueueTrack CurrentQueueTrack { get; } OptimizedObservableCollection PlaybackQueue { get; } event EventHandler MediaStateChanged; event EventHandler TrackChanged; diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index d58a3abe..65addfd6 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -59,7 +59,7 @@ public bool IsBackgroundTaskRunning public string CurrentQueueId => _settingsUtility.Read(ApplicationSettingsConstants.QueueId, string.Empty); - + public QueueTrack CurrentQueueTrack { get; private set; } public OptimizedObservableCollection PlaybackQueue { get; private set; } public event EventHandler MediaStateChanged; public event EventHandler TrackChanged; @@ -262,7 +262,7 @@ private async Task InternalAddAsync(Track track, int position = -1) else PlaybackQueue.Add(queue); MessageHelper.SendMessageToBackground(new AddToPlaylistMessage(queue, position)); - await Task.Delay(25); + await Task.Delay(50); return queue; } @@ -347,6 +347,7 @@ private async void BackgroundMediaPlayer_MessageReceivedFromBackground(object se if (message is TrackChangedMessage) { var trackChangedMessage = message as TrackChangedMessage; + CurrentQueueTrack = PlaybackQueue.FirstOrDefault(p => p.Id == trackChangedMessage.QueueId); // When foreground app is active change track based on background message await _dispatcherUtility.RunAsync( () => { TrackChanged?.Invoke(sender, trackChangedMessage.QueueId); }); diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index d2b48670..1ffd9092 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -143,14 +143,14 @@ - + - + @@ -163,8 +163,8 @@ - - + + diff --git a/Windows/Audiotica.Windows/Styles/ControlStyles.xaml b/Windows/Audiotica.Windows/Styles/ControlStyles.xaml index 6b0943bc..bde3938c 100644 --- a/Windows/Audiotica.Windows/Styles/ControlStyles.xaml +++ b/Windows/Audiotica.Windows/Styles/ControlStyles.xaml @@ -17,7 +17,7 @@ - - Date: Sat, 26 Sep 2015 11:59:33 -0400 Subject: [PATCH 13/31] Fix now playing indicator on catalog songs --- Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml | 7 ++++++- .../Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs | 2 +- Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml index af3253ef..b0eaf872 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml @@ -38,7 +38,7 @@ - + + + + diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs index 27099482..867a04ad 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs @@ -75,7 +75,7 @@ private void TrackChanged() var player = App.Current.Kernel.Resolve(); if (player.CurrentQueueTrack?.Track != null) IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || - player.CurrentQueueTrack.Track.Id == Track.Id; + (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id); else IsPlaying = false; diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs index b091f0e3..a4c2c43c 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs @@ -77,7 +77,7 @@ private void TrackChanged() var player = App.Current.Kernel.Resolve(); if (player.CurrentQueueTrack?.Track != null) IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || - player.CurrentQueueTrack.Track.Id == Track.Id; + (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id); else IsPlaying = false; From 80ed9f201337bd8c0384f8fd0f7d09c9b27ef610 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 11:59:48 -0400 Subject: [PATCH 14/31] Add seeking and position duration info --- Windows/Audiotica.Windows/Shell.xaml | 9 ++++- .../ViewModels/PlayerBarViewModel.cs | 39 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 1ffd9092..b63eaac0 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -157,7 +157,14 @@ - + + + + + + + + diff --git a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs index 3b04b4e7..3ae6596a 100644 --- a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs @@ -11,11 +11,14 @@ namespace Audiotica.Windows.ViewModels { public class PlayerBarViewModel : ViewModelBase { + private const int TimerInterval = 500; private readonly IPlayerService _playerService; private readonly DispatcherTimer _timer; private QueueTrack _currentQueueTrack; private double _playbackDuration; + private string _playbackDurationText; private double _playbackPosition; + private string _playbackPositionText; private Symbol _playPauseIcon = Symbol.Play; public PlayerBarViewModel(IPlayerService playerService) @@ -28,7 +31,7 @@ public PlayerBarViewModel(IPlayerService playerService) NextCommand = new Command(() => _playerService.Next()); PrevCommand = new Command(() => _playerService.Previous()); - _timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(500)}; + _timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(TimerInterval)}; _timer.Tick += TimerOnTick; } @@ -53,7 +56,11 @@ public Symbol PlayPauseIcon public double PlaybackPosition { get { return _playbackPosition; } - set { Set(ref _playbackPosition, value); } + set + { + Set(ref _playbackPosition, value); + UpdatePosition(); + } } public double PlaybackDuration @@ -62,10 +69,34 @@ public double PlaybackDuration set { Set(ref _playbackDuration, value); } } + public string PlaybackPositionText + { + get { return _playbackPositionText; } + set { Set(ref _playbackPositionText, value); } + } + + public string PlaybackDurationText + { + get { return _playbackDurationText; } + set { Set(ref _playbackDurationText, value); } + } + + private void UpdatePosition() + { + var playerPosition = BackgroundMediaPlayer.Current.Position.TotalMilliseconds; + var difference = Math.Abs(PlaybackPosition - playerPosition); + if (difference > TimerInterval) + BackgroundMediaPlayer.Current.Position = TimeSpan.FromMilliseconds(PlaybackPosition); + } + private void TimerOnTick(object sender, object o) { - PlaybackPosition = BackgroundMediaPlayer.Current.Position.TotalMilliseconds; - PlaybackDuration = BackgroundMediaPlayer.Current.NaturalDuration.TotalMilliseconds; + var position = BackgroundMediaPlayer.Current.Position; + var duration = BackgroundMediaPlayer.Current.NaturalDuration; + PlaybackPosition = position.TotalMilliseconds; + PlaybackDuration = duration.TotalMilliseconds; + PlaybackPositionText = position.ToString(@"m\:ss"); + PlaybackDurationText = duration.ToString(@"m\:ss"); } private void PlayerServiceOnMediaStateChanged(object sender, MediaPlayerState mediaPlayerState) From d8b1f6a086c5b4a0f6638d88e177df9e524bfdf7 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 12:01:27 -0400 Subject: [PATCH 15/31] Add playing indicator to narrow track --- Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs index 867a04ad..f574e9ca 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs @@ -67,6 +67,7 @@ public Track Track { _track = value; Bindings.Update(); + TrackChanged(); } } From 5c747f382dd7d19669f1cea1a53714a0188d126c Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 12:03:06 -0400 Subject: [PATCH 16/31] Move playing indicator on narrow viewer to top --- Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml index b0eaf872..c0a4eb74 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml @@ -53,7 +53,7 @@ Visibility="{x:Bind Track.Type, Mode=OneWay, Converter={StaticResource StreamingVisibilityConverter}}" /> - + From 2f95c11efa3f2fc30c6e256776c5df40ae6fabc3 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 19:29:28 -0400 Subject: [PATCH 17/31] Tweak playlist models --- Audiotica.Database/Models/Playlist.cs | 3 +++ Audiotica.Database/Models/PlaylistTrack.cs | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Audiotica.Database/Models/Playlist.cs b/Audiotica.Database/Models/Playlist.cs index f90f2aa7..55cfbec1 100644 --- a/Audiotica.Database/Models/Playlist.cs +++ b/Audiotica.Database/Models/Playlist.cs @@ -10,7 +10,10 @@ public class Playlist { public string Id { get; set; } public DateTime CreatedAt { get; set; } + public DateTime EditedAt { get; set; } + public DateTime DeletedAt { get; set; } public string Name { get; set; } + public bool IsVisible { get; set; } public List Tracks { get; set; } } } \ No newline at end of file diff --git a/Audiotica.Database/Models/PlaylistTrack.cs b/Audiotica.Database/Models/PlaylistTrack.cs index 6d2a9532..edf84a5a 100644 --- a/Audiotica.Database/Models/PlaylistTrack.cs +++ b/Audiotica.Database/Models/PlaylistTrack.cs @@ -7,7 +7,6 @@ namespace Audiotica.Database.Models /// public class PlaylistTrack { - public string Id { get; set; } public string TrackId { get; set; } [JsonIgnore] From f4d0252ed77b2ff744dfe84acb9ad43f2ef16b4d Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Sat, 26 Sep 2015 19:49:02 -0400 Subject: [PATCH 18/31] Add list of current queue in now playing page --- Audiotica.Database/Models/Track.cs | 2 ++ .../Controls/TrackNarrowViewer.xaml.cs | 2 +- Windows/Audiotica.Windows/Controls/TrackViewer.xaml | 4 ++-- .../Audiotica.Windows/Controls/TrackViewer.xaml.cs | 13 ++++++++++++- .../DataTemplates/LibraryDictionary.xaml | 8 +++++++- .../Services/DesignTime/DesignPlayerService.cs | 5 +++++ .../Services/Interfaces/IPlayerService.cs | 1 + .../Services/RunTime/PlayerService.cs | 2 ++ .../ViewModels/NowPlayingPageViewModel.cs | 9 ++++++++- Windows/Audiotica.Windows/Views/NowPlayingPage.xaml | 8 +++++++- .../Audiotica.Windows/Views/NowPlayingPage.xaml.cs | 7 ++++++- 11 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Audiotica.Database/Models/Track.cs b/Audiotica.Database/Models/Track.cs index 51eedaf7..8587d663 100644 --- a/Audiotica.Database/Models/Track.cs +++ b/Audiotica.Database/Models/Track.cs @@ -354,6 +354,8 @@ public class TrackComparer : IEqualityComparer { public bool Equals(Track x, Track y) { + if (x == null && y == null) return true; + if (x == null || y == null) return false; if (x.IsFromLibrary && y.IsFromLibrary) return x.Id == y.Id; return GetHashCode(x) == GetHashCode(y); diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs index f574e9ca..0a71ea34 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs @@ -93,7 +93,7 @@ private async void PlayButton_Click(object sender, RoutedEventArgs e) var playerService = lifetimeScope.Resolve(); try { - var queue = await playerService.AddAsync(Track); + var queue = playerService.ContainsTrack(Track) ?? await playerService.AddAsync(Track); // player auto plays when there is only one track if (playerService.PlaybackQueue.Count > 1) playerService.Play(queue); diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml index 0910f687..1d769ee7 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml @@ -18,8 +18,8 @@ Visibility="{x:Bind Track.IsFromLibrary, Mode=OneWay, Converter={StaticResource ReverseVisibilityConverter}}" /> - - + + (); try { - var queue = await playerService.AddAsync(Track); + var queue = playerService.ContainsTrack(Track) ?? await playerService.AddAsync(Track); // player auto plays when there is only one track if (playerService.PlaybackQueue.Count > 1) playerService.Play(queue); diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml index 222ae89a..05c0d90a 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml @@ -10,7 +10,7 @@ - + + + + + diff --git a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs index 95511679..18772556 100644 --- a/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/DesignTime/DesignPlayerService.cs @@ -35,6 +35,11 @@ public void PlayOrPause() throw new NotImplementedException(); } + public QueueTrack ContainsTrack(Track track) + { + throw new NotImplementedException(); + } + public Task AddAsync(Track track, int position = -1) { throw new NotImplementedException(); diff --git a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs index 1d706c22..77c4ebb3 100644 --- a/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs +++ b/Windows/Audiotica.Windows/Services/Interfaces/IPlayerService.cs @@ -28,6 +28,7 @@ public interface IPlayerService /// void PlayOrPause(); + QueueTrack ContainsTrack(Track track); /// /// Adds the specified track to the queue. /// diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index 65addfd6..5987f70a 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -77,6 +77,8 @@ public bool StartBackgroundTask() return started; } + public QueueTrack ContainsTrack(Track track) => PlaybackQueue.FirstOrDefault(p => track.Id == p.Track.Id); + public async Task AddAsync(Track track, int position = -1) { await PrepareTrackAsync(track); diff --git a/Windows/Audiotica.Windows/ViewModels/NowPlayingPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/NowPlayingPageViewModel.cs index 512cd7d5..f741d2da 100644 --- a/Windows/Audiotica.Windows/ViewModels/NowPlayingPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/NowPlayingPageViewModel.cs @@ -1,8 +1,15 @@ +using Audiotica.Windows.Services.Interfaces; using Audiotica.Windows.Tools.Mvvm; namespace Audiotica.Windows.ViewModels { - internal class NowPlayingPageViewModel : ViewModelBase + public class NowPlayingPageViewModel : ViewModelBase { + public NowPlayingPageViewModel(IPlayerService playerService) + { + PlayerService = playerService; + } + + public IPlayerService PlayerService { get; } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml b/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml index 7e33c347..89ba86e3 100644 --- a/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml +++ b/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml @@ -6,8 +6,14 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:navigationService="using:Audiotica.Windows.Services.NavigationService" + xmlns:controls="using:Audiotica.Windows.Controls" mc:Ignorable="d" DataContext="{Binding NowPlaying, Source={StaticResource ViewModelLocator}}"> - + + + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml.cs b/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml.cs index 64c828ba..54bda8a8 100644 --- a/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml.cs +++ b/Windows/Audiotica.Windows/Views/NowPlayingPage.xaml.cs @@ -1,10 +1,15 @@ -namespace Audiotica.Windows.Views +using Audiotica.Windows.ViewModels; + +namespace Audiotica.Windows.Views { public sealed partial class NowPlayingPage { public NowPlayingPage() { InitializeComponent(); + ViewModel = DataContext as NowPlayingPageViewModel; } + + public NowPlayingPageViewModel ViewModel { get; } } } \ No newline at end of file From 08e1aa0acb98e13b5b11d14618d2e3b700351ff8 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 09:34:38 -0400 Subject: [PATCH 19/31] Fix add list to queue bug --- Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs index 5987f70a..c643fedd 100644 --- a/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs +++ b/Windows/Audiotica.Windows/Services/RunTime/PlayerService.cs @@ -270,11 +270,12 @@ private async Task InternalAddAsync(Track track, int position = -1) private void Add(IEnumerable tracks, int position = -1) { + var index = position; var queue = tracks.Select(track => new QueueTrack(track)).ToList(); - if (position > -1 && position < PlaybackQueue.Count) + if (index > -1 && index < PlaybackQueue.Count) foreach (var item in queue) { - PlaybackQueue.Insert(position++, item); + PlaybackQueue.Insert(index++, item); } else PlaybackQueue.AddRange(queue); From 9964f573e5a11d471e0ffd654403c265980e8dfb Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 09:50:36 -0400 Subject: [PATCH 20/31] Make indicator show by queue id in queue viewer --- .../Controls/TrackNarrowViewer.xaml.cs | 38 +++++++++++++++++-- .../Controls/TrackViewer.xaml.cs | 26 +++++++++++-- .../DataTemplates/LibraryDictionary.xaml | 12 ++++-- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs index 0a71ea34..e14c948d 100644 --- a/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackNarrowViewer.xaml.cs @@ -26,6 +26,13 @@ public sealed partial class TrackNarrowViewer: INotifyPropertyChanged public static readonly DependencyProperty IsCatalogProperty = DependencyProperty.Register("IsCatalog", typeof(bool), typeof(TrackViewer), null); + + public static readonly DependencyProperty IsQueueProperty = + DependencyProperty.Register("IsQueue", typeof(bool), typeof(TrackViewer), null); + + public static readonly DependencyProperty QueueIdProperty = + DependencyProperty.Register("QueueId", typeof(string), typeof(TrackViewer), null); + private Track _track; private bool _isPlaying; @@ -71,14 +78,37 @@ public Track Track } } + public bool IsQueue + + { + get { return (bool)GetValue(IsQueueProperty); } + + set { SetValue(IsQueueProperty, value); } + } + + public string QueueId + { + get { return (string)GetValue(QueueIdProperty); } + + set { SetValue(QueueIdProperty, value); } + } + private void TrackChanged() { var player = App.Current.Kernel.Resolve(); - if (player.CurrentQueueTrack?.Track != null) - IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || - (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id); - else + + if (Track == null) IsPlaying = false; + else + { + if (IsQueue && QueueId != null) + IsPlaying = player.CurrentQueueId == QueueId; + else if (!IsQueue && player.CurrentQueueTrack?.Track != null) + IsPlaying = (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id) + || TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track); + else + IsPlaying = false; + } player.TrackChanged -= PlayerOnTrackChanged; player.TrackChanged += PlayerOnTrackChanged; diff --git a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs index 02c3f950..b74c5cab 100644 --- a/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/TrackViewer.xaml.cs @@ -27,6 +27,9 @@ public sealed partial class TrackViewer : INotifyPropertyChanged public static readonly DependencyProperty IsQueueProperty = DependencyProperty.Register("IsQueue", typeof (bool), typeof (TrackViewer), null); + public static readonly DependencyProperty QueueIdProperty = + DependencyProperty.Register("QueueId", typeof(string), typeof(TrackViewer), null); + private bool _isPlaying; private Track _track; @@ -70,6 +73,13 @@ public bool IsQueue set { SetValue(IsQueueProperty, value); } } + public string QueueId + { + get { return (string)GetValue(QueueIdProperty); } + + set { SetValue(QueueIdProperty, value); } + } + public Track Track { get { return _track; } @@ -86,11 +96,19 @@ public Track Track private void TrackChanged() { var player = App.Current.Kernel.Resolve(); - if (player.CurrentQueueTrack?.Track != null) - IsPlaying = TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track) || - (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id); - else + + if (Track == null) IsPlaying = false; + else + { + if (IsQueue && QueueId != null) + IsPlaying = player.CurrentQueueId == QueueId; + else if (!IsQueue && player.CurrentQueueTrack?.Track != null) + IsPlaying = (Track.Id > 0 && player.CurrentQueueTrack.Track.Id == Track.Id) + || TrackComparer.AreEqual(player.CurrentQueueTrack.Track, Track); + else + IsPlaying = false; + } player.TrackChanged -= PlayerOnTrackChanged; player.TrackChanged += PlayerOnTrackChanged; diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml index 05c0d90a..2733b39e 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml @@ -28,11 +28,17 @@ - - + + + + + From efba36e3b4910a2b1d215f3c1cdc33f96da8db8b Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 13:09:42 -0400 Subject: [PATCH 21/31] Fix album page bug --- .../DataTemplates/LibraryDictionary.xaml | 6 ++- .../ViewModels/AlbumPageViewModel.cs | 54 +++++++++++-------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml index 2733b39e..4f46137c 100644 --- a/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml +++ b/Windows/Audiotica.Windows/DataTemplates/LibraryDictionary.xaml @@ -28,13 +28,15 @@ - - diff --git a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs index f3654b82..2df4d412 100644 --- a/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/AlbumPageViewModel.cs @@ -143,7 +143,7 @@ public override sealed async void OnNavigatedTo(object parameter, NavigationMode if (Album == null) { - _navigationService.GoBack(); + CurtainPrompt.ShowError("Problem loading album"); return; } } @@ -155,35 +155,43 @@ public override sealed async void OnNavigatedTo(object parameter, NavigationMode private async Task LoadAsync(AlbumPageParameter albumParameter) { var webAlbum = albumParameter.WebAlbum; - if (webAlbum == null) + if (webAlbum == null && albumParameter.Provider != null) { - if (albumParameter.Provider != null) + var provider = _metadataProviders.FirstOrDefault(p => p.GetType() == albumParameter.Provider); + webAlbum = await provider.GetAlbumAsync(albumParameter.Token); + } + if (webAlbum != null) + { + try + { + Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); + } + catch + { + await LoadByTitleAsync(albumParameter); + } + } + else + await LoadByTitleAsync(albumParameter); + } + + private async Task LoadByTitleAsync(AlbumPageParameter albumParameter) + { + for (var i = 0; i < _metadataProviders.Count; i++) + { + try { - var provider = _metadataProviders.FirstOrDefault(p => p.GetType() == albumParameter.Provider); - webAlbum = await provider.GetAlbumAsync(albumParameter.Token); + var webAlbum = await GetAlbumByTitleAsync(albumParameter.Title, albumParameter.Artist, i); if (webAlbum != null) + { Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); + break; + } } - else + catch { - for (var i = 0; i < _metadataProviders.Count; i++) - { - try - { - webAlbum = await GetAlbumByTitleAsync(albumParameter.Title, albumParameter.Artist, i); - - if (webAlbum != null) - { - Album = await _webAlbumConverter.ConvertAsync(webAlbum, IsCatalogMode); - break; - } - } - catch - { - // ignored - } - } + // ignored } } } From 4ab898a77d0030a0fd5c8cadeaf160008373a684 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 13:12:34 -0400 Subject: [PATCH 22/31] Use settings for theme --- .../Helpers/ApplicationSettingsConstants.cs | 1 + Windows/Audiotica.Windows/App.xaml.cs | 5 ----- Windows/Audiotica.Windows/Shell.xaml | 12 ++++++------ Windows/Audiotica.Windows/Shell.xaml.cs | 12 ++++++++++++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Windows/Audiotica.Core.Windows/Helpers/ApplicationSettingsConstants.cs b/Windows/Audiotica.Core.Windows/Helpers/ApplicationSettingsConstants.cs index e489a6ac..b9c1c3ff 100644 --- a/Windows/Audiotica.Core.Windows/Helpers/ApplicationSettingsConstants.cs +++ b/Windows/Audiotica.Core.Windows/Helpers/ApplicationSettingsConstants.cs @@ -13,5 +13,6 @@ public static class ApplicationSettingsConstants public const string IsAlbumAdaptiveColorEnabled = "IsAlbumAdaptiveColorEnabled"; public const string SongSort = "SongSort"; public const string AlbumSort = "AlbumSort"; + public const string Theme = "Theme"; } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/App.xaml.cs b/Windows/Audiotica.Windows/App.xaml.cs index c3be1868..697d06c4 100644 --- a/Windows/Audiotica.Windows/App.xaml.cs +++ b/Windows/Audiotica.Windows/App.xaml.cs @@ -14,11 +14,6 @@ sealed partial class App public App() { WindowsAppInitializer.InitializeAsync(); - - // Only the dark theme is supported in everything else (they only have light option) - if (!DeviceHelper.IsType(DeviceFamily.Mobile)) - RequestedTheme = ApplicationTheme.Dark; - InitializeComponent(); } diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index b63eaac0..532f386d 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -149,11 +149,11 @@ - - + + - - + + @@ -162,9 +162,9 @@ - + - + diff --git a/Windows/Audiotica.Windows/Shell.xaml.cs b/Windows/Audiotica.Windows/Shell.xaml.cs index 62fb6170..9da3dffd 100644 --- a/Windows/Audiotica.Windows/Shell.xaml.cs +++ b/Windows/Audiotica.Windows/Shell.xaml.cs @@ -3,6 +3,8 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; +using Audiotica.Core.Utilities.Interfaces; +using Audiotica.Core.Windows.Helpers; using Audiotica.Windows.Services.Interfaces; using Audiotica.Windows.Services.RunTime; using Audiotica.Windows.Tools.Mvvm; @@ -46,6 +48,16 @@ public Shell(Frame frame) Loaded += (s, e) => update(); DataContext = this; ViewModel = App.Current.Kernel.Resolve(); + + var settings = App.Current.Kernel.Resolve(); + RequestedTheme = settings.Read(ApplicationSettingsConstants.Theme, ElementTheme.Dark); + } + + public void SetTheme(ElementTheme theme) + { + var settings = App.Current.Kernel.Resolve(); + settings.Write(ApplicationSettingsConstants.Theme, theme); + RequestedTheme = theme; } public Thickness HamburgerPadding From 5a305a6631b2602bd071978432160cb50312f86e Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 13:41:47 -0400 Subject: [PATCH 23/31] Improve theme changing code --- .../Interfaces/IAppSettingsUtility.cs | 1 + .../Utilities/AppSettingsUtility.cs | 20 ++++++++++++++++++- Windows/Audiotica.Windows/App.xaml | 1 + .../AppEngine/Modules/UtilityModule.cs | 2 +- .../Audiotica.Windows.csproj | 1 + .../Controls/SelectModeCommandBar.xaml | 1 + .../Controls/SelectModeCommandBar.xaml.cs | 5 +++++ .../DeviceFamily-Mobile/Shell.xaml | 1 + Windows/Audiotica.Windows/Shell.xaml | 1 + Windows/Audiotica.Windows/Shell.xaml.cs | 10 ++-------- .../Tools/Converters/IntToThemeConverter.cs | 20 +++++++++++++++++++ 11 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs diff --git a/Audiotica.Core/Utilities/Interfaces/IAppSettingsUtility.cs b/Audiotica.Core/Utilities/Interfaces/IAppSettingsUtility.cs index 54c9a204..03b4402d 100644 --- a/Audiotica.Core/Utilities/Interfaces/IAppSettingsUtility.cs +++ b/Audiotica.Core/Utilities/Interfaces/IAppSettingsUtility.cs @@ -4,5 +4,6 @@ public interface IAppSettingsUtility { string DownloadsPath { get; set; } string TempDownloadsPath { get; } + int Theme { get; set; } } } \ No newline at end of file diff --git a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs index a6af8267..e86c3bf5 100644 --- a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs +++ b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs @@ -1,18 +1,36 @@ using Windows.Storage; +using Windows.UI.Xaml; +using Audiotica.Core.Common; using Audiotica.Core.Utilities.Interfaces; +using Audiotica.Core.Windows.Helpers; namespace Audiotica.Core.Windows.Utilities { - public class AppSettingsUtility : IAppSettingsUtility + public class AppSettingsUtility : ObservableObject, IAppSettingsUtility { + private readonly ISettingsUtility _settingsUtility; + private int _theme; + public AppSettingsUtility(ISettingsUtility settingsUtility) { + _settingsUtility = settingsUtility; DownloadsPath = settingsUtility.Read("DownloadsPath", "virtual://Music/Audiotica/"); TempDownloadsPath = settingsUtility.Read("TempDownloadsPath", ApplicationData.Current.TemporaryFolder.Path); + _theme = _settingsUtility.Read(ApplicationSettingsConstants.Theme, (int)ElementTheme.Dark); } public string DownloadsPath { get; set; } public string TempDownloadsPath { get; } + + public int Theme + { + get { return _theme; } + set + { + Set(ref _theme, value); + _settingsUtility.Write(ApplicationSettingsConstants.Theme, value); + } + } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/App.xaml b/Windows/Audiotica.Windows/App.xaml index b6d6499a..7cd6acd0 100644 --- a/Windows/Audiotica.Windows/App.xaml +++ b/Windows/Audiotica.Windows/App.xaml @@ -36,6 +36,7 @@ + diff --git a/Windows/Audiotica.Windows/AppEngine/Modules/UtilityModule.cs b/Windows/Audiotica.Windows/AppEngine/Modules/UtilityModule.cs index c9df2fd1..de7d0082 100644 --- a/Windows/Audiotica.Windows/AppEngine/Modules/UtilityModule.cs +++ b/Windows/Audiotica.Windows/AppEngine/Modules/UtilityModule.cs @@ -24,7 +24,7 @@ public override void LoadRunTime(ContainerBuilder builder) builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); } } diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index b861e2a5..9be2c132 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -200,6 +200,7 @@ + diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml index d30693fc..eefbe1b2 100644 --- a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" + RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" d:DesignHeight="200" d:DesignWidth="400"> diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs index 0038d07f..61ad816a 100644 --- a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs @@ -4,6 +4,7 @@ using Windows.UI.Xaml; using Audiotica.Core.Exceptions; using Audiotica.Core.Extensions; +using Audiotica.Core.Utilities.Interfaces; using Audiotica.Database.Models; using Audiotica.Database.Services.Interfaces; using Audiotica.Windows.Common; @@ -22,8 +23,12 @@ public sealed partial class SelectModeCommandBar public SelectModeCommandBar() { InitializeComponent(); + + AppSettings = App.Current.Kernel.Resolve(); } + public IAppSettingsUtility AppSettings { get; } + public bool IsCatalog { get; set; } public ObservableCollection SelectedItems diff --git a/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml b/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml index a3a672be..25dd7e08 100644 --- a/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml +++ b/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml @@ -6,6 +6,7 @@ xmlns:views="using:Audiotica.Windows.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" mc:Ignorable="d"> diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 532f386d..42cf1d3c 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -6,6 +6,7 @@ xmlns:views="using:Audiotica.Windows.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" mc:Ignorable="d"> diff --git a/Windows/Audiotica.Windows/Shell.xaml.cs b/Windows/Audiotica.Windows/Shell.xaml.cs index 9da3dffd..59c021cc 100644 --- a/Windows/Audiotica.Windows/Shell.xaml.cs +++ b/Windows/Audiotica.Windows/Shell.xaml.cs @@ -49,16 +49,10 @@ public Shell(Frame frame) DataContext = this; ViewModel = App.Current.Kernel.Resolve(); - var settings = App.Current.Kernel.Resolve(); - RequestedTheme = settings.Read(ApplicationSettingsConstants.Theme, ElementTheme.Dark); + AppSettings = App.Current.Kernel.Resolve(); } - public void SetTheme(ElementTheme theme) - { - var settings = App.Current.Kernel.Resolve(); - settings.Write(ApplicationSettingsConstants.Theme, theme); - RequestedTheme = theme; - } + public IAppSettingsUtility AppSettings { get; } public Thickness HamburgerPadding { diff --git a/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs b/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs new file mode 100644 index 00000000..f02caa2b --- /dev/null +++ b/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs @@ -0,0 +1,20 @@ +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace Audiotica.Windows.Tools.Converters +{ + public class IntToThemeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + var i = (int) value; + return (ElementTheme) i; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file From e3d5f53ea08a75070601fadedb7c6101692f8b55 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 13:51:38 -0400 Subject: [PATCH 24/31] Make the play pause button's icon change --- Windows/Audiotica.Windows/Shell.xaml | 2 +- Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 42cf1d3c..821a82ce 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -169,7 +169,7 @@ - + diff --git a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs index 3ae6596a..113c9364 100644 --- a/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/PlayerBarViewModel.cs @@ -19,7 +19,7 @@ public class PlayerBarViewModel : ViewModelBase private string _playbackDurationText; private double _playbackPosition; private string _playbackPositionText; - private Symbol _playPauseIcon = Symbol.Play; + private IconElement _playPauseIcon = new SymbolIcon(Symbol.Play); public PlayerBarViewModel(IPlayerService playerService) { @@ -47,7 +47,7 @@ public QueueTrack CurrentQueueTrack set { Set(ref _currentQueueTrack, value); } } - public Symbol PlayPauseIcon + public IconElement PlayPauseIcon { get { return _playPauseIcon; } set { Set(ref _playPauseIcon, value); } @@ -112,7 +112,7 @@ private void PlayerServiceOnMediaStateChanged(object sender, MediaPlayerState me _timer.Stop(); break; } - PlayPauseIcon = icon; + PlayPauseIcon = new SymbolIcon(icon); } private void PlayerServiceOnTrackChanged(object sender, string s) From 679fe5d311b7f3459badfb47ba3b796ff2c94550 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Mon, 28 Sep 2015 16:22:32 -0400 Subject: [PATCH 25/31] Add theme option in settings page --- .../Utilities/AppSettingsUtility.cs | 2 +- Windows/Audiotica.Windows/App.xaml | 3 ++- .../Audiotica.Windows.csproj | 1 + .../DeviceFamily-Mobile/Shell.xaml | 2 +- Windows/Audiotica.Windows/Shell.xaml | 2 +- Windows/Audiotica.Windows/Shell.xaml.cs | 3 +-- .../Tools/Converters/IntToBoolConverter.cs | 20 +++++++++++++++++++ .../Tools/Converters/IntToThemeConverter.cs | 3 ++- .../ViewModels/SettingsPageViewModel.cs | 11 ++++++++-- .../Audiotica.Windows/Views/AlbumPage.xaml | 2 +- .../Audiotica.Windows/Views/SettingsPage.xaml | 2 +- .../Views/SettingsPage.xaml.cs | 7 ++++++- 12 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 Windows/Audiotica.Windows/Tools/Converters/IntToBoolConverter.cs diff --git a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs index e86c3bf5..df0ed808 100644 --- a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs +++ b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs @@ -16,7 +16,7 @@ public AppSettingsUtility(ISettingsUtility settingsUtility) _settingsUtility = settingsUtility; DownloadsPath = settingsUtility.Read("DownloadsPath", "virtual://Music/Audiotica/"); TempDownloadsPath = settingsUtility.Read("TempDownloadsPath", ApplicationData.Current.TemporaryFolder.Path); - _theme = _settingsUtility.Read(ApplicationSettingsConstants.Theme, (int)ElementTheme.Dark); + _theme = _settingsUtility.Read(ApplicationSettingsConstants.Theme, (int)ElementTheme.Light); } public string DownloadsPath { get; set; } diff --git a/Windows/Audiotica.Windows/App.xaml b/Windows/Audiotica.Windows/App.xaml index 7cd6acd0..dcc6f3f2 100644 --- a/Windows/Audiotica.Windows/App.xaml +++ b/Windows/Audiotica.Windows/App.xaml @@ -37,8 +37,9 @@ + - + Multiple diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index 9be2c132..bc6d795c 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -200,6 +200,7 @@ + diff --git a/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml b/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml index 25dd7e08..98135877 100644 --- a/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml +++ b/Windows/Audiotica.Windows/DeviceFamily-Mobile/Shell.xaml @@ -6,7 +6,7 @@ xmlns:views="using:Audiotica.Windows.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" + RequestedTheme="{Binding AppSettings.Theme, Converter={StaticResource IntToThemeConverter}}" mc:Ignorable="d"> diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 821a82ce..155dd078 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -6,7 +6,7 @@ xmlns:views="using:Audiotica.Windows.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" + RequestedTheme="{Binding AppSettings.Theme, Converter={StaticResource IntToThemeConverter}}" mc:Ignorable="d"> diff --git a/Windows/Audiotica.Windows/Shell.xaml.cs b/Windows/Audiotica.Windows/Shell.xaml.cs index 59c021cc..baf9749f 100644 --- a/Windows/Audiotica.Windows/Shell.xaml.cs +++ b/Windows/Audiotica.Windows/Shell.xaml.cs @@ -46,10 +46,9 @@ public Shell(Frame frame) }); frame.Navigated += (s, e) => update(); Loaded += (s, e) => update(); - DataContext = this; ViewModel = App.Current.Kernel.Resolve(); - AppSettings = App.Current.Kernel.Resolve(); + DataContext = this; } public IAppSettingsUtility AppSettings { get; } diff --git a/Windows/Audiotica.Windows/Tools/Converters/IntToBoolConverter.cs b/Windows/Audiotica.Windows/Tools/Converters/IntToBoolConverter.cs new file mode 100644 index 00000000..ce883d76 --- /dev/null +++ b/Windows/Audiotica.Windows/Tools/Converters/IntToBoolConverter.cs @@ -0,0 +1,20 @@ +using System; +using Windows.UI.Xaml.Data; + +namespace Audiotica.Windows.Tools.Converters +{ + public class IntToBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + var i = (int) value; + return i == 2; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + var i = (bool) value; + return i ? 2 : 1; + } + } +} \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs b/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs index f02caa2b..6c6abaa2 100644 --- a/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs +++ b/Windows/Audiotica.Windows/Tools/Converters/IntToThemeConverter.cs @@ -14,7 +14,8 @@ public object Convert(object value, Type targetType, object parameter, string la public object ConvertBack(object value, Type targetType, object parameter, string language) { - throw new NotImplementedException(); + var i = (ElementTheme)value; + return (int)i; } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/ViewModels/SettingsPageViewModel.cs b/Windows/Audiotica.Windows/ViewModels/SettingsPageViewModel.cs index de294fcc..0b12ebcc 100644 --- a/Windows/Audiotica.Windows/ViewModels/SettingsPageViewModel.cs +++ b/Windows/Audiotica.Windows/ViewModels/SettingsPageViewModel.cs @@ -1,8 +1,15 @@ -using Audiotica.Windows.Tools.Mvvm; +using Audiotica.Core.Utilities.Interfaces; +using Audiotica.Windows.Tools.Mvvm; namespace Audiotica.Windows.ViewModels { - internal class SettingsPageViewModel : ViewModelBase + public class SettingsPageViewModel : ViewModelBase { + public SettingsPageViewModel(IAppSettingsUtility appSettingsUtility) + { + AppSettingsUtility = appSettingsUtility; + } + + public IAppSettingsUtility AppSettingsUtility { get; } } } \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/AlbumPage.xaml b/Windows/Audiotica.Windows/Views/AlbumPage.xaml index 42c2e433..cd5bd04a 100644 --- a/Windows/Audiotica.Windows/Views/AlbumPage.xaml +++ b/Windows/Audiotica.Windows/Views/AlbumPage.xaml @@ -10,7 +10,7 @@ xmlns:customTriggers="using:Audiotica.Windows.CustomTriggers" xmlns:converters="using:Audiotica.Windows.Tools.Converters" mc:Ignorable="d" - RequestedTheme="{Binding RequestedTheme, Mode=OneWay}" + RequestedTheme="{Binding RequestedTheme}" DataContext="{Binding AlbumPage, Source={StaticResource ViewModelLocator}}"> diff --git a/Windows/Audiotica.Windows/Views/SettingsPage.xaml b/Windows/Audiotica.Windows/Views/SettingsPage.xaml index 6a72055d..1dfd77e7 100644 --- a/Windows/Audiotica.Windows/Views/SettingsPage.xaml +++ b/Windows/Audiotica.Windows/Views/SettingsPage.xaml @@ -10,6 +10,6 @@ DataContext="{Binding SettingsPage, Source={StaticResource ViewModelLocator}}"> - + \ No newline at end of file diff --git a/Windows/Audiotica.Windows/Views/SettingsPage.xaml.cs b/Windows/Audiotica.Windows/Views/SettingsPage.xaml.cs index 2cd8c23e..f12a426b 100644 --- a/Windows/Audiotica.Windows/Views/SettingsPage.xaml.cs +++ b/Windows/Audiotica.Windows/Views/SettingsPage.xaml.cs @@ -1,10 +1,15 @@ -namespace Audiotica.Windows.Views +using Audiotica.Windows.ViewModels; + +namespace Audiotica.Windows.Views { public sealed partial class SettingsPage { public SettingsPage() { InitializeComponent(); + ViewModel = DataContext as SettingsPageViewModel; } + + public SettingsPageViewModel ViewModel { get; } } } \ No newline at end of file From d61f0cdf1e4c2c9eff4a7e45662e0ea9302a5919 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Wed, 30 Sep 2015 11:53:44 -0400 Subject: [PATCH 26/31] Make select bar use classical binding for theme --- Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml | 2 +- Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml index eefbe1b2..59d7a310 100644 --- a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" - RequestedTheme="{x:Bind AppSettings.Theme, Mode=OneWay, Converter={StaticResource IntToThemeConverter}}" + RequestedTheme="{Binding AppSettings.Theme, Converter={StaticResource IntToThemeConverter}}" d:DesignHeight="200" d:DesignWidth="400"> diff --git a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs index 61ad816a..aafee0c9 100644 --- a/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs +++ b/Windows/Audiotica.Windows/Controls/SelectModeCommandBar.xaml.cs @@ -23,8 +23,8 @@ public sealed partial class SelectModeCommandBar public SelectModeCommandBar() { InitializeComponent(); - AppSettings = App.Current.Kernel.Resolve(); + DataContext = this; } public IAppSettingsUtility AppSettings { get; } From 52942da7d0fbe4c07cadce5692b01b7f7ec0dacd Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Thu, 1 Oct 2015 08:17:38 -0400 Subject: [PATCH 27/31] Align theme toggle --- Windows/Audiotica.Windows/Views/SettingsPage.xaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/Views/SettingsPage.xaml b/Windows/Audiotica.Windows/Views/SettingsPage.xaml index 1dfd77e7..d234e9e9 100644 --- a/Windows/Audiotica.Windows/Views/SettingsPage.xaml +++ b/Windows/Audiotica.Windows/Views/SettingsPage.xaml @@ -9,7 +9,9 @@ mc:Ignorable="d" DataContext="{Binding SettingsPage, Source={StaticResource ViewModelLocator}}"> - - + + + + \ No newline at end of file From 1d00ca5f845587a3b6fbecd6edf8799854a4719c Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Thu, 1 Oct 2015 08:19:47 -0400 Subject: [PATCH 28/31] Use ElementTheme.Default as default --- Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs index df0ed808..5acab05d 100644 --- a/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs +++ b/Windows/Audiotica.Core.Windows/Utilities/AppSettingsUtility.cs @@ -16,7 +16,7 @@ public AppSettingsUtility(ISettingsUtility settingsUtility) _settingsUtility = settingsUtility; DownloadsPath = settingsUtility.Read("DownloadsPath", "virtual://Music/Audiotica/"); TempDownloadsPath = settingsUtility.Read("TempDownloadsPath", ApplicationData.Current.TemporaryFolder.Path); - _theme = _settingsUtility.Read(ApplicationSettingsConstants.Theme, (int)ElementTheme.Light); + _theme = _settingsUtility.Read(ApplicationSettingsConstants.Theme, (int)ElementTheme.Default); } public string DownloadsPath { get; set; } From ff273c4132f8efbc925c8941b67973d9d2d53fb2 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Thu, 1 Oct 2015 08:23:42 -0400 Subject: [PATCH 29/31] Add a small description in the about page --- Windows/Audiotica.Windows/Views/AboutPage.xaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Windows/Audiotica.Windows/Views/AboutPage.xaml b/Windows/Audiotica.Windows/Views/AboutPage.xaml index 4b61fa0d..fbb69188 100644 --- a/Windows/Audiotica.Windows/Views/AboutPage.xaml +++ b/Windows/Audiotica.Windows/Views/AboutPage.xaml @@ -10,6 +10,8 @@ DataContext="{Binding AboutPage, Source={StaticResource ViewModelLocator}}"> - + + + \ No newline at end of file From e8fb3b8b1637c8811c7379a4b8abfdcbc8eb052e Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Thu, 1 Oct 2015 08:59:44 -0400 Subject: [PATCH 30/31] Disable shuffle and repeat buttons --- Windows/Audiotica.Windows/Shell.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/Audiotica.Windows/Shell.xaml b/Windows/Audiotica.Windows/Shell.xaml index 155dd078..e643353c 100644 --- a/Windows/Audiotica.Windows/Shell.xaml +++ b/Windows/Audiotica.Windows/Shell.xaml @@ -171,8 +171,8 @@ - - + + From c1809099ccac0bf1bb94dbb401fadd1a74dc44f8 Mon Sep 17 00:00:00 2001 From: Harold Martinez Date: Thu, 1 Oct 2015 09:10:30 -0400 Subject: [PATCH 31/31] Version bump and fix sdk bug --- Windows/Audiotica.Windows/Audiotica.Windows.csproj | 2 ++ Windows/Audiotica.Windows/Package.StoreAssociation.xml | 8 ++++++++ Windows/Audiotica.Windows/Package.appxmanifest | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Windows/Audiotica.Windows/Audiotica.Windows.csproj b/Windows/Audiotica.Windows/Audiotica.Windows.csproj index bc6d795c..969c6f3c 100644 --- a/Windows/Audiotica.Windows/Audiotica.Windows.csproj +++ b/Windows/Audiotica.Windows/Audiotica.Windows.csproj @@ -104,6 +104,8 @@ + + diff --git a/Windows/Audiotica.Windows/Package.StoreAssociation.xml b/Windows/Audiotica.Windows/Package.StoreAssociation.xml index 69c2bd10..ab0338f5 100644 --- a/Windows/Audiotica.Windows/Package.StoreAssociation.xml +++ b/Windows/Audiotica.Windows/Package.StoreAssociation.xml @@ -27,6 +27,7 @@ + @@ -92,6 +93,7 @@ + @@ -155,6 +157,7 @@ + @@ -274,6 +277,7 @@ + @@ -323,6 +327,7 @@ + @@ -334,6 +339,7 @@ + @@ -343,6 +349,8 @@ + + diff --git a/Windows/Audiotica.Windows/Package.appxmanifest b/Windows/Audiotica.Windows/Package.appxmanifest index e6bec9cd..1d5ac711 100644 --- a/Windows/Audiotica.Windows/Package.appxmanifest +++ b/Windows/Audiotica.Windows/Package.appxmanifest @@ -1,6 +1,6 @@  - + Audiotica