diff --git a/Sources/Stylophone.Common/Services/AlbumArtService.cs b/Sources/Stylophone.Common/Services/AlbumArtService.cs index 7e88da0..0866df0 100644 --- a/Sources/Stylophone.Common/Services/AlbumArtService.cs +++ b/Sources/Stylophone.Common/Services/AlbumArtService.cs @@ -91,6 +91,7 @@ public void Stop() /// MpdFile to check for art /// True if the art is cached, false otherwise. public async Task IsAlbumArtCachedAsync(IMpdFile f) => await _applicationStorageService.DoesFileExistAsync(Miscellaneous.GetFileIdentifier(f), "AlbumArt"); + public async Task IsAlbumArtCachedAsync(string albumName) => await _applicationStorageService.DoesFileExistAsync(albumName, "AlbumArt"); /// /// Queue up an AlbumViewModel for the service to grab its album art. @@ -224,7 +225,7 @@ private QuantizedColor GetDominantColor(SKBitmap art) private async Task SaveArtToFileAsync(string fileName, List data) => await _applicationStorageService.SaveDataToFileAsync(fileName, data.ToArray(), "AlbumArt"); - private async Task LoadImageFromFile(string fileName) + internal async Task LoadImageFromFile(string fileName) { try { diff --git a/Sources/Stylophone.Common/Services/MPDConnectionService.cs b/Sources/Stylophone.Common/Services/MPDConnectionService.cs index 1f6f6db..117af96 100644 --- a/Sources/Stylophone.Common/Services/MPDConnectionService.cs +++ b/Sources/Stylophone.Common/Services/MPDConnectionService.cs @@ -88,6 +88,7 @@ public async Task InitializeAsync(bool withRetry = false) { System.Diagnostics.Debug.WriteLine($"Error while connecting: {e.Message}"); + IsConnecting = false; ConnectionChanged?.Invoke(this, new EventArgs()); if (withRetry && !cancelToken.IsCancellationRequested) diff --git a/Sources/Stylophone.Common/Stylophone.Common.csproj b/Sources/Stylophone.Common/Stylophone.Common.csproj index 082c9c6..0c2ffb0 100644 --- a/Sources/Stylophone.Common/Stylophone.Common.csproj +++ b/Sources/Stylophone.Common/Stylophone.Common.csproj @@ -7,12 +7,12 @@ - - + + - - - + + + diff --git a/Sources/Stylophone.Common/ViewModels/Bases/LibraryViewModelBase.cs b/Sources/Stylophone.Common/ViewModels/Bases/LibraryViewModelBase.cs index 078c979..2d7fec3 100644 --- a/Sources/Stylophone.Common/ViewModels/Bases/LibraryViewModelBase.cs +++ b/Sources/Stylophone.Common/ViewModels/Bases/LibraryViewModelBase.cs @@ -13,8 +13,15 @@ namespace Stylophone.Common.ViewModels { + public abstract partial class LibraryViewModelBase : ViewModelBase { + private record Album + { + public string Name { get; set; } + public string SortName { get; set; } + } + private INavigationService _navigationService; private MPDConnectionService _mpdService; private AlbumViewModelFactory _albumVmFactory; @@ -40,9 +47,13 @@ public async Task LoadDataAsync() FilteredSource.CollectionChanged += (s, e) => OnPropertyChanged(nameof(IsSourceEmpty)); Source.Clear(); - var response = await _mpdService.SafelySendCommandAsync(new ListCommand(MpdTags.AlbumSort)); + var albumList = await _mpdService.SafelySendCommandAsync(new ListCommand(MpdTags.Album)); + var albumSortList = await _mpdService.SafelySendCommandAsync(new ListCommand(MpdTags.AlbumSort)); + + // Create a list of tuples + var response = albumList.Zip(albumSortList, (album, albumSort) => new Album{ Name = album, SortName = albumSort }); - if (response != null) + if (albumSortList != null) GroupAlbumsByName(response); if (Source.Count > 0) @@ -63,10 +74,10 @@ public void FilterLibrary(string text) AddBack(filtered); } - public void GroupAlbumsByName(List albums) + private void GroupAlbumsByName(IEnumerable albums) { var query = from item in albums - group item by GetGroupHeader(item) into g + group item by GetGroupHeader(item.SortName) into g orderby g.Key select new { GroupName = g.Key, Items = g }; @@ -76,9 +87,9 @@ orderby g.Key //GroupInfosList info = new GroupInfosList(); //info.Key = g.GroupName + " (" + g.Items.Count() + ")"; - foreach (var item in g.Items.OrderBy(s => s.ToLower())) + foreach (var item in g.Items.OrderBy(s => s.SortName.ToLower())) { - Source.Add(_albumVmFactory.GetAlbumViewModel(item)); + Source.Add(_albumVmFactory.GetAlbumViewModel(item.Name)); } } } diff --git a/Sources/Stylophone.Common/ViewModels/Items/AlbumViewModel.cs b/Sources/Stylophone.Common/ViewModels/Items/AlbumViewModel.cs index 0a85e31..9fa9946 100644 --- a/Sources/Stylophone.Common/ViewModels/Items/AlbumViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/Items/AlbumViewModel.cs @@ -9,12 +9,14 @@ using MpcNET.Tags; using MpcNET.Types; using SkiaSharp; +using Stylophone.Common.Helpers; using Stylophone.Common.Interfaces; using Stylophone.Common.Services; using Stylophone.Localization.Strings; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Threading.Tasks; using System.Windows.Input; @@ -180,6 +182,16 @@ private async Task PlayAlbum() } } + public async Task LoadAlbumArtFromCacheAsync() + { + // Try to show album art early if we have it in cache + var cacheName = Miscellaneous.EscapeFilename(Name); + if (await _albumArtService.IsAlbumArtCachedAsync(cacheName)) + { + AlbumArt = SKImage.FromBitmap(await _albumArtService.LoadImageFromFile(cacheName)); + } + } + /// /// Load Album Data. You can either provide a MpcConnection object (for batch loading) /// or leave as empty to automatically pick up a connection from the datasource. @@ -196,7 +208,8 @@ public async Task LoadAlbumDataAsync() public async Task LoadAlbumDataAsync(MpcConnection c) { IsDetailLoading = true; - AlbumArt = await _interop.GetPlaceholderImageAsync(); + AlbumArt ??= await _interop.GetPlaceholderImageAsync(); + try { var findReq = await c.SendAsync(new FindCommand(MpdTags.Album, Name)); diff --git a/Sources/Stylophone.iOS/Info.plist b/Sources/Stylophone.iOS/Info.plist index eeebfbe..ca94302 100644 --- a/Sources/Stylophone.iOS/Info.plist +++ b/Sources/Stylophone.iOS/Info.plist @@ -16,9 +16,9 @@ zh CFBundleShortVersionString - 2.6.0 + 2.6.1 CFBundleVersion - 2.6.0 + 2.6.1 LSRequiresIPhoneOS MinimumOSVersion diff --git a/Sources/Stylophone.iOS/Services/NotificationService.cs b/Sources/Stylophone.iOS/Services/NotificationService.cs index 3e83d5b..1f474e0 100644 --- a/Sources/Stylophone.iOS/Services/NotificationService.cs +++ b/Sources/Stylophone.iOS/Services/NotificationService.cs @@ -20,7 +20,7 @@ public override void ShowInAppNotification(InAppNotification notification) { // Let's just use alerts until TipKit is available... This is cheap but w/e var alert = new UIAlertView(notification.NotificationTitle, notification.NotificationText, null, "Ok"); - alert.Show(); + UIApplication.SharedApplication.InvokeOnMainThread(() => alert.Show()); return; } diff --git a/Sources/Stylophone.iOS/Stylophone.iOS.csproj b/Sources/Stylophone.iOS/Stylophone.iOS.csproj index 3889d5c..6277b64 100644 --- a/Sources/Stylophone.iOS/Stylophone.iOS.csproj +++ b/Sources/Stylophone.iOS/Stylophone.iOS.csproj @@ -1,6 +1,6 @@  - net7.0-ios + net7.0-ios;net7.0-maccatalyst Exe enable true @@ -61,12 +61,12 @@ - + - + diff --git a/Sources/Stylophone.iOS/ViewModels/LibraryViewModel.cs b/Sources/Stylophone.iOS/ViewModels/LibraryViewModel.cs index 6e0c75f..40aa415 100644 --- a/Sources/Stylophone.iOS/ViewModels/LibraryViewModel.cs +++ b/Sources/Stylophone.iOS/ViewModels/LibraryViewModel.cs @@ -73,6 +73,13 @@ internal void LoadItems(IEnumerable indexes, CancellationToken token = defa // Albumart loads still use their own connections. Task.Run(async () => { + // Try to show album artwork early if we have it in cache + foreach (var i in indexes) + { + var album = BackingCollection[i]; + await album.LoadAlbumArtFromCacheAsync(); + } + using (var c = await _mpdService.GetConnectionAsync(token)) foreach (var i in indexes) { diff --git a/Sources/Stylophone/Package.appxmanifest b/Sources/Stylophone/Package.appxmanifest index 16a5808..93e2f24 100644 --- a/Sources/Stylophone/Package.appxmanifest +++ b/Sources/Stylophone/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="2.6.1.0" /> diff --git a/Sources/Stylophone/Package.tt b/Sources/Stylophone/Package.tt index 9dcdfd2..70ff8db 100644 --- a/Sources/Stylophone/Package.tt +++ b/Sources/Stylophone/Package.tt @@ -2,7 +2,7 @@ <#@ output extension=".appxmanifest" #> <#@ parameter type="System.String" name="BuildConfiguration" #> <# - string version = "2.6.0.0"; + string version = "2.6.2.0"; // Get configuration name at Build time string configName = Host.ResolveParameterValue("-", "-", "BuildConfiguration"); diff --git a/Sources/Stylophone/Stylophone.csproj b/Sources/Stylophone/Stylophone.csproj index 0ad7510..89a8705 100644 --- a/Sources/Stylophone/Stylophone.csproj +++ b/Sources/Stylophone/Stylophone.csproj @@ -148,22 +148,22 @@ 10.1901.28001 - - - - - - - - - - - - + + + + + + + + + + + + - - + + 1.1.0 diff --git a/Sources/Stylophone/ViewModels/LibraryViewModel.cs b/Sources/Stylophone/ViewModels/LibraryViewModel.cs index c4b6348..c4360c7 100644 --- a/Sources/Stylophone/ViewModels/LibraryViewModel.cs +++ b/Sources/Stylophone/ViewModels/LibraryViewModel.cs @@ -59,6 +59,13 @@ public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList { + // Try to show album artwork early if we have it in cache + for (var i = visibleRange.FirstIndex; i < visibleRange.LastIndex + 1; i++) // Increment LastIndex by one to properly cover the visible range + { + var album = this[i]; + await album.LoadAlbumArtFromCacheAsync(); + } + using (var c = await _mpdService.GetConnectionAsync(token)) for (var i = visibleRange.FirstIndex; i < visibleRange.LastIndex + 1; i++) // Increment LastIndex by one to properly cover the visible range { diff --git a/Sources/Stylophone/Views/ServerQueuePage.xaml b/Sources/Stylophone/Views/ServerQueuePage.xaml index 093ffc4..fb1b1b5 100644 --- a/Sources/Stylophone/Views/ServerQueuePage.xaml +++ b/Sources/Stylophone/Views/ServerQueuePage.xaml @@ -35,7 +35,8 @@ ItemsSource="{x:Bind ViewModel.Source, Mode=OneWay}" ReorderMode="Enabled" RightTapped="Select_Item" - SelectionMode="Extended"> + SelectionMode="Extended" + Visibility="{x:Bind ViewModel.IsSourceEmpty, Mode=OneWay, Converter={StaticResource ReverseBoolToVisibilityConverter}}">