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();
|
Task<Playlist> AddPlaylistAsync();
|
||||||
void RemovePlaylist(Playlist playlist);
|
void RemovePlaylist(Playlist playlist);
|
||||||
Playlist? FindPlaylistContaining(PlaylistSong playlistSong);
|
Playlist? FindPlaylistContaining(PlaylistSong playlistSong);
|
||||||
|
Playlist? FindPlaylistContaining(string playlistSongUID);
|
||||||
|
|
||||||
event EventHandler? CurrentPlaylistChanged;
|
event EventHandler? CurrentPlaylistChanged;
|
||||||
event EventHandler<PlaylistAddedEventArgs> PlaylistAdded;
|
event EventHandler<PlaylistAddedEventArgs> PlaylistAdded;
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ public class PlaylistManager : IPlaylistManager
|
|||||||
return _playlistsBySongUid.GetValueOrDefault(playlistSong.UID);
|
return _playlistsBySongUid.GetValueOrDefault(playlistSong.UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Playlist? FindPlaylistContaining(string playlistSongUID)
|
||||||
|
{
|
||||||
|
return _playlistsBySongUid.GetValueOrDefault(playlistSongUID);
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnPlaylistUpdated(object? sender, PlaylistUpdatedEventArgs e)
|
private async void OnPlaylistUpdated(object? sender, PlaylistUpdatedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is not Playlist playlist)
|
if (sender is not Playlist playlist)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Harmonia.Core.Extensions;
|
using Harmonia.Core.Extensions;
|
||||||
using Harmonia.WinUI.Caching;
|
using Harmonia.WinUI.Caching;
|
||||||
using Harmonia.WinUI.Messaging;
|
using Harmonia.WinUI.Messaging;
|
||||||
|
using Harmonia.WinUI.Session;
|
||||||
using Harmonia.WinUI.Storage;
|
using Harmonia.WinUI.Storage;
|
||||||
using Harmonia.WinUI.ViewModels;
|
using Harmonia.WinUI.ViewModels;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -31,6 +32,14 @@ public partial class App : Application
|
|||||||
services.AddSingleton<IStorageProvider, WindowsStorageProvider>();
|
services.AddSingleton<IStorageProvider, WindowsStorageProvider>();
|
||||||
services.AddSingleton<IMessageService, WindowsMessageService>();
|
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();
|
services.AddHarmonia();
|
||||||
|
|
||||||
ServiceProvider = services.BuildServiceProvider();
|
ServiceProvider = services.BuildServiceProvider();
|
||||||
@@ -45,6 +54,15 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
await ServiceProvider.InitializeHarmoniaAsync();
|
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 = ServiceProvider.GetRequiredService<MainWindow>();
|
||||||
_mainWindow.Activate();
|
_mainWindow.Activate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Harmonia.WinUI">
|
Title="Harmonia.WinUI"
|
||||||
|
Closed="OnMainWindowClosed">
|
||||||
|
|
||||||
<Window.SystemBackdrop>
|
<Window.SystemBackdrop>
|
||||||
<MicaBackdrop />
|
<MicaBackdrop />
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Harmonia.WinUI.Session;
|
||||||
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
namespace Harmonia.WinUI;
|
namespace Harmonia.WinUI;
|
||||||
@@ -6,10 +8,17 @@ public sealed partial class MainWindow : Window
|
|||||||
{
|
{
|
||||||
private const string ApplicationTitle = "Harmonia";
|
private const string ApplicationTitle = "Harmonia";
|
||||||
|
|
||||||
public MainWindow()
|
private readonly ISessionService _sessionService;
|
||||||
|
|
||||||
|
private bool _isSaved;
|
||||||
|
|
||||||
|
public MainWindow(ISessionService sessionService)
|
||||||
{
|
{
|
||||||
|
_sessionService = sessionService;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
InitializeTitleBar();
|
InitializeTitleBar();
|
||||||
|
InitializeWindowState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeTitleBar()
|
private void InitializeTitleBar()
|
||||||
@@ -18,4 +27,39 @@ public sealed partial class MainWindow : Window
|
|||||||
ExtendsContentIntoTitleBar = true;
|
ExtendsContentIntoTitleBar = true;
|
||||||
SetTitleBar(AppTitleBar);
|
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)));
|
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
|
||||||
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
|
||||||
|
_activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
|
||||||
|
|||||||
@@ -17,6 +17,20 @@ public sealed partial class PlaylistsView : UserControl
|
|||||||
_viewModel = (PlaylistsViewModel)DataContext;
|
_viewModel = (PlaylistsViewModel)DataContext;
|
||||||
_viewModel.PropertyChanging += OnViewModelPropertyChanging;
|
_viewModel.PropertyChanging += OnViewModelPropertyChanging;
|
||||||
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
|
_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)
|
private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
|
||||||
@@ -41,11 +55,14 @@ public sealed partial class PlaylistsView : UserControl
|
|||||||
|
|
||||||
private void UpdateListViewItemStyle(PlaylistItemStyle style)
|
private void UpdateListViewItemStyle(PlaylistItemStyle style)
|
||||||
{
|
{
|
||||||
ListViewItem listViewItem = (ListViewItem)PlaylistsListView.ContainerFromItem(_viewModel.ActivePlaylist);
|
if (PlaylistsListView.ContainerFromItem(_viewModel.ActivePlaylist) is not ListViewItem listViewItem)
|
||||||
|
|
||||||
if (listViewItem is null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UpdateListViewItemStyle(listViewItem, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateListViewItemStyle(ListViewItem listViewItem, PlaylistItemStyle style)
|
||||||
|
{
|
||||||
FrameworkElement? frameworkElement = TryGetFrameworkElement(listViewItem, "PlaylistListViewItem");
|
FrameworkElement? frameworkElement = TryGetFrameworkElement(listViewItem, "PlaylistListViewItem");
|
||||||
|
|
||||||
if (frameworkElement == null)
|
if (frameworkElement == null)
|
||||||
|
|||||||
Reference in New Issue
Block a user