Added application session services, along with initial restore and tracker implementations.
This commit is contained in:
@@ -9,6 +9,7 @@ public interface IPlaylistManager
|
||||
Task<Playlist> AddPlaylistAsync();
|
||||
void RemovePlaylist(Playlist playlist);
|
||||
Playlist? FindPlaylistContaining(PlaylistSong playlistSong);
|
||||
Playlist? FindPlaylistContaining(string playlistSongUID);
|
||||
|
||||
event EventHandler? CurrentPlaylistChanged;
|
||||
event EventHandler<PlaylistAddedEventArgs> PlaylistAdded;
|
||||
|
||||
@@ -88,6 +88,11 @@ public class PlaylistManager : IPlaylistManager
|
||||
return _playlistsBySongUid.GetValueOrDefault(playlistSong.UID);
|
||||
}
|
||||
|
||||
public Playlist? FindPlaylistContaining(string playlistSongUID)
|
||||
{
|
||||
return _playlistsBySongUid.GetValueOrDefault(playlistSongUID);
|
||||
}
|
||||
|
||||
private async void OnPlaylistUpdated(object? sender, PlaylistUpdatedEventArgs e)
|
||||
{
|
||||
if (sender is not Playlist playlist)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Harmonia.Core.Extensions;
|
||||
using Harmonia.WinUI.Caching;
|
||||
using Harmonia.WinUI.Messaging;
|
||||
using Harmonia.WinUI.Session;
|
||||
using Harmonia.WinUI.Storage;
|
||||
using Harmonia.WinUI.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -31,6 +32,14 @@ public partial class App : Application
|
||||
services.AddSingleton<IStorageProvider, WindowsStorageProvider>();
|
||||
services.AddSingleton<IMessageService, WindowsMessageService>();
|
||||
|
||||
services.AddSingleton<ISessionService, JsonSessionService>();
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ISessionService>().Session.Window);
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ISessionService>().Session.Player);
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ISessionService>().Session.Playlist);
|
||||
|
||||
services.AddSingleton<ISessionRestorer, SessionRestorer>();
|
||||
services.AddSingleton<ISessionTracker, SessionTracker>();
|
||||
|
||||
services.AddHarmonia();
|
||||
|
||||
ServiceProvider = services.BuildServiceProvider();
|
||||
@@ -45,6 +54,15 @@ public partial class App : Application
|
||||
{
|
||||
await ServiceProvider.InitializeHarmoniaAsync();
|
||||
|
||||
ISessionService sessionService = ServiceProvider.GetRequiredService<ISessionService>();
|
||||
await sessionService.LoadAsync();
|
||||
|
||||
ISessionRestorer sessionRestorer = ServiceProvider.GetRequiredService<ISessionRestorer>();
|
||||
sessionRestorer.Restore();
|
||||
|
||||
ISessionTracker sessionTracker = ServiceProvider.GetRequiredService<ISessionTracker>();
|
||||
sessionTracker.Start();
|
||||
|
||||
_mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
|
||||
_mainWindow.Activate();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="Harmonia.WinUI">
|
||||
Title="Harmonia.WinUI"
|
||||
Closed="OnMainWindowClosed">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Harmonia.WinUI.Session;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Harmonia.WinUI;
|
||||
@@ -6,10 +8,17 @@ public sealed partial class MainWindow : Window
|
||||
{
|
||||
private const string ApplicationTitle = "Harmonia";
|
||||
|
||||
public MainWindow()
|
||||
private readonly ISessionService _sessionService;
|
||||
|
||||
private bool _isSaved;
|
||||
|
||||
public MainWindow(ISessionService sessionService)
|
||||
{
|
||||
_sessionService = sessionService;
|
||||
|
||||
InitializeComponent();
|
||||
InitializeTitleBar();
|
||||
InitializeWindowState();
|
||||
}
|
||||
|
||||
private void InitializeTitleBar()
|
||||
@@ -18,4 +27,39 @@ public sealed partial class MainWindow : Window
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
SetTitleBar(AppTitleBar);
|
||||
}
|
||||
|
||||
private void InitializeWindowState()
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter && _sessionService.Session.Window.IsMaximized)
|
||||
{
|
||||
presenter.Maximize();
|
||||
}
|
||||
|
||||
AppWindow.Changed += OnAppWindowChanged;
|
||||
}
|
||||
|
||||
private void OnAppWindowChanged(AppWindow sender, AppWindowChangedEventArgs args)
|
||||
{
|
||||
if (sender.Presenter is not OverlappedPresenter presenter)
|
||||
return;
|
||||
|
||||
// Ignore the minimized state so a minimize-then-close doesn't lose the maximized flag.
|
||||
if (presenter.State is OverlappedPresenterState.Minimized)
|
||||
return;
|
||||
|
||||
_sessionService.Session.Window.IsMaximized = presenter.State == OverlappedPresenterState.Maximized;
|
||||
}
|
||||
|
||||
private async void OnMainWindowClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (_isSaved)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
await _sessionService.SaveAsync();
|
||||
|
||||
_isSaved = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
6
Harmonia.WinUI/Session/ISessionRestorer.cs
Normal file
6
Harmonia.WinUI/Session/ISessionRestorer.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public interface ISessionRestorer
|
||||
{
|
||||
void Restore();
|
||||
}
|
||||
10
Harmonia.WinUI/Session/ISessionService.cs
Normal file
10
Harmonia.WinUI/Session/ISessionService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public interface ISessionService
|
||||
{
|
||||
ApplicationSession Session { get; }
|
||||
Task LoadAsync();
|
||||
Task SaveAsync();
|
||||
}
|
||||
6
Harmonia.WinUI/Session/ISessionTracker.cs
Normal file
6
Harmonia.WinUI/Session/ISessionTracker.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public interface ISessionTracker
|
||||
{
|
||||
void Start();
|
||||
}
|
||||
47
Harmonia.WinUI/Session/JsonSessionService.cs
Normal file
47
Harmonia.WinUI/Session/JsonSessionService.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public sealed class JsonSessionService : ISessionService
|
||||
{
|
||||
private static readonly JsonSerializerOptions Options = new()
|
||||
{
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
private readonly string _filePath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Harmonia",
|
||||
"session.json"
|
||||
);
|
||||
|
||||
public ApplicationSession Session { get; private set; } = new();
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_filePath))
|
||||
{
|
||||
await using var stream = File.OpenRead(_filePath);
|
||||
Session = await JsonSerializer.DeserializeAsync<ApplicationSession>(stream) ?? new();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Session = new();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveAsync()
|
||||
{
|
||||
string directoryName = Path.GetDirectoryName(_filePath)!;
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
await using var stream = File.Create(_filePath);
|
||||
await JsonSerializer.SerializeAsync(stream, Session, Options);
|
||||
}
|
||||
}
|
||||
55
Harmonia.WinUI/Session/SessionRestorer.cs
Normal file
55
Harmonia.WinUI/Session/SessionRestorer.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Harmonia.Core.Player;
|
||||
using Harmonia.Core.Playlists;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public class SessionRestorer(
|
||||
ISessionService sessionService,
|
||||
IPlaylistManager playlistManager,
|
||||
IAudioPlayer audioPlayer) : ISessionRestorer
|
||||
{
|
||||
public void Restore()
|
||||
{
|
||||
RestorePlaylist();
|
||||
RestoreAudioPlayer();
|
||||
}
|
||||
|
||||
private void RestorePlaylist()
|
||||
{
|
||||
PlaylistState playlistState = sessionService.Session.Playlist;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(playlistState.OpenPlaylistUID))
|
||||
return;
|
||||
|
||||
Playlist? playlist = playlistManager.Playlists.FirstOrDefault(p => p.UID == playlistState.OpenPlaylistUID);
|
||||
|
||||
if (playlist is null)
|
||||
return;
|
||||
|
||||
playlistManager.CurrentPlaylist = playlist;
|
||||
}
|
||||
|
||||
private void RestoreAudioPlayer()
|
||||
{
|
||||
AudioPlayerState audioPlayerState = sessionService.Session.Player;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(audioPlayerState.PlaylistSongUID))
|
||||
return;
|
||||
|
||||
Playlist? playlist = playlistManager.FindPlaylistContaining(audioPlayerState.PlaylistSongUID);
|
||||
|
||||
if (playlist is null)
|
||||
return;
|
||||
|
||||
PlaylistSong? song = playlist.Songs.FirstOrDefault(s => s.UID == audioPlayerState.PlaylistSongUID);
|
||||
|
||||
if (song is null)
|
||||
return;
|
||||
|
||||
//PlaybackMode playbackMode = audioPlayerState.PlaybackState
|
||||
|
||||
audioPlayer.LoadAsync(song, PlaybackMode.LoadOnly);
|
||||
}
|
||||
}
|
||||
29
Harmonia.WinUI/Session/SessionTracker.cs
Normal file
29
Harmonia.WinUI/Session/SessionTracker.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Harmonia.Core.Player;
|
||||
using Harmonia.Core.Playlists;
|
||||
using System;
|
||||
|
||||
namespace Harmonia.WinUI.Session;
|
||||
|
||||
public class SessionTracker(
|
||||
ISessionService sessionService,
|
||||
IPlaylistManager playlistManager,
|
||||
IAudioPlayer audioPlayer) : ISessionTracker
|
||||
{
|
||||
public void Start()
|
||||
{
|
||||
playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
|
||||
audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
|
||||
}
|
||||
|
||||
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
||||
{
|
||||
PlaylistState playlistState = sessionService.Session.Playlist;
|
||||
playlistState.OpenPlaylistUID = playlistManager.CurrentPlaylist?.UID;
|
||||
}
|
||||
|
||||
private void OnPlayingSongChanged(object? sender, PlayingSongChangedEventArgs e)
|
||||
{
|
||||
AudioPlayerState audioPlayerState = sessionService.Session.Player;
|
||||
audioPlayerState.PlaylistSongUID = e.NewSong?.UID;
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ public partial class PlaylistsViewModel : ViewModelBase
|
||||
|
||||
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
|
||||
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
||||
_activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
||||
}
|
||||
|
||||
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
||||
|
||||
@@ -17,6 +17,20 @@ public sealed partial class PlaylistsView : UserControl
|
||||
_viewModel = (PlaylistsViewModel)DataContext;
|
||||
_viewModel.PropertyChanging += OnViewModelPropertyChanging;
|
||||
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
||||
|
||||
PlaylistsListView.ContainerContentChanging += OnContainerContentChanging;
|
||||
}
|
||||
|
||||
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
||||
{
|
||||
if (args.InRecycleQueue || args.ItemContainer is not ListViewItem listViewItem)
|
||||
return;
|
||||
|
||||
PlaylistItemStyle style = ReferenceEquals(args.Item, _viewModel.ActivePlaylist)
|
||||
? PlaylistItemStyle.Selected
|
||||
: PlaylistItemStyle.Normal;
|
||||
|
||||
UpdateListViewItemStyle(listViewItem, style);
|
||||
}
|
||||
|
||||
private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
|
||||
@@ -41,11 +55,14 @@ public sealed partial class PlaylistsView : UserControl
|
||||
|
||||
private void UpdateListViewItemStyle(PlaylistItemStyle style)
|
||||
{
|
||||
ListViewItem listViewItem = (ListViewItem)PlaylistsListView.ContainerFromItem(_viewModel.ActivePlaylist);
|
||||
|
||||
if (listViewItem is null)
|
||||
if (PlaylistsListView.ContainerFromItem(_viewModel.ActivePlaylist) is not ListViewItem listViewItem)
|
||||
return;
|
||||
|
||||
UpdateListViewItemStyle(listViewItem, style);
|
||||
}
|
||||
|
||||
private void UpdateListViewItemStyle(ListViewItem listViewItem, PlaylistItemStyle style)
|
||||
{
|
||||
FrameworkElement? frameworkElement = TryGetFrameworkElement(listViewItem, "PlaylistListViewItem");
|
||||
|
||||
if (frameworkElement == null)
|
||||
|
||||
Reference in New Issue
Block a user