using System; using System.Linq; using CommunityToolkit.Diagnostics; using OwlCore; using StrixMusic.Sdk.MediaPlayback; using Windows.Media; using Windows.Storage; using Windows.Storage.Streams; namespace StrixMusic.Services { /// /// Integrates an with the system media transport controls. /// public sealed class SystemMediaTransportControlsHandler : IDisposable { private readonly IPlaybackHandlerService _playbackHandlerService; private readonly SystemMediaTransportControls _systemMediaTransportControls; /// /// Creates a new instance of /// /// The playback handler to use for display with the system transport controls. public SystemMediaTransportControlsHandler(IPlaybackHandlerService playbackHandlerService) { _systemMediaTransportControls = SystemMediaTransportControls.GetForCurrentView(); _playbackHandlerService = playbackHandlerService; _systemMediaTransportControls.IsPlayEnabled = true; _systemMediaTransportControls.IsPauseEnabled = true; AttachEvents(); } private void AttachEvents() { _playbackHandlerService.CurrentItemChanged += PlaybackHandlerService_CurrentItemChanged; _playbackHandlerService.PlaybackStateChanged += PlaybackHandlerService_PlaybackStateChanged; _playbackHandlerService.RepeatStateChanged += PlaybackHandlerService_RepeatStateChanged; _playbackHandlerService.ShuffleStateChanged += PlaybackHandlerService_ShuffleStateChanged; _playbackHandlerService.PositionChanged += PlaybackHandlerService_PositionChanged; _systemMediaTransportControls.ShuffleEnabledChangeRequested += SystemMediaTransportControls_ShuffleEnabledChangeRequested; _systemMediaTransportControls.AutoRepeatModeChangeRequested += SystemMediaTransportControls_AutoRepeatModeChangeRequested; _systemMediaTransportControls.PlaybackPositionChangeRequested += SystemMediaTransportControls_PlaybackPositionChangeRequested; _systemMediaTransportControls.PlaybackRateChangeRequested += SystemMediaTransportControls_PlaybackRateChangeRequested; _systemMediaTransportControls.ButtonPressed += SystemMediaTransportControls_ButtonPressed; } private void DetachEvents() { _playbackHandlerService.CurrentItemChanged -= PlaybackHandlerService_CurrentItemChanged; _playbackHandlerService.PlaybackStateChanged -= PlaybackHandlerService_PlaybackStateChanged; _playbackHandlerService.RepeatStateChanged -= PlaybackHandlerService_RepeatStateChanged; _playbackHandlerService.ShuffleStateChanged -= PlaybackHandlerService_ShuffleStateChanged; _playbackHandlerService.PositionChanged -= PlaybackHandlerService_PositionChanged; _systemMediaTransportControls.ShuffleEnabledChangeRequested -= SystemMediaTransportControls_ShuffleEnabledChangeRequested; _systemMediaTransportControls.AutoRepeatModeChangeRequested -= SystemMediaTransportControls_AutoRepeatModeChangeRequested; _systemMediaTransportControls.PlaybackPositionChangeRequested -= SystemMediaTransportControls_PlaybackPositionChangeRequested; _systemMediaTransportControls.PlaybackRateChangeRequested -= SystemMediaTransportControls_PlaybackRateChangeRequested; _systemMediaTransportControls.ButtonPressed -= SystemMediaTransportControls_ButtonPressed; } private void SystemMediaTransportControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) { _ = Threading.OnPrimaryThread(() => { switch (args.Button) { case SystemMediaTransportControlsButton.Play: _playbackHandlerService.ResumeAsync(); break; case SystemMediaTransportControlsButton.Stop: case SystemMediaTransportControlsButton.Pause: _playbackHandlerService.PauseAsync(); break; case SystemMediaTransportControlsButton.FastForward: _playbackHandlerService.SeekAsync(_playbackHandlerService.Position + TimeSpan.FromSeconds(5)); break; case SystemMediaTransportControlsButton.Rewind: _playbackHandlerService.SeekAsync(_playbackHandlerService.Position - TimeSpan.FromSeconds(5)); break; case SystemMediaTransportControlsButton.Next: _playbackHandlerService.NextAsync(); break; case SystemMediaTransportControlsButton.Previous: _playbackHandlerService.PreviousAsync(); break; case SystemMediaTransportControlsButton.Record: case SystemMediaTransportControlsButton.ChannelUp: case SystemMediaTransportControlsButton.ChannelDown: break; } }); } private void SystemMediaTransportControls_PlaybackRateChangeRequested(SystemMediaTransportControls sender, PlaybackRateChangeRequestedEventArgs args) { _playbackHandlerService.ChangePlaybackSpeedAsync(args.RequestedPlaybackRate); } private void SystemMediaTransportControls_AutoRepeatModeChangeRequested(SystemMediaTransportControls sender, AutoRepeatModeChangeRequestedEventArgs args) { _playbackHandlerService.SetRepeatStateAsync((RepeatState)args.RequestedAutoRepeatMode); } private void SystemMediaTransportControls_PlaybackPositionChangeRequested(SystemMediaTransportControls sender, PlaybackPositionChangeRequestedEventArgs args) { _playbackHandlerService.SeekAsync(args.RequestedPlaybackPosition); } private void SystemMediaTransportControls_ShuffleEnabledChangeRequested(SystemMediaTransportControls sender, ShuffleEnabledChangeRequestedEventArgs args) { if (args.RequestedShuffleEnabled != _playbackHandlerService.ShuffleState) _playbackHandlerService.ToggleShuffleAsync(); } private void PlaybackHandlerService_PositionChanged(object? sender, TimeSpan e) { _systemMediaTransportControls.UpdateTimelineProperties(new SystemMediaTransportControlsTimelineProperties { Position = e, EndTime = _playbackHandlerService.CurrentItem?.Track?.Duration ?? TimeSpan.MaxValue, StartTime = TimeSpan.Zero, }); } private void PlaybackHandlerService_ShuffleStateChanged(object? sender, bool e) { _systemMediaTransportControls.ShuffleEnabled = e; } private void PlaybackHandlerService_RepeatStateChanged(object? sender, RepeatState e) { _systemMediaTransportControls.AutoRepeatMode = (MediaPlaybackAutoRepeatMode)e; } private void PlaybackHandlerService_PlaybackStateChanged(object? sender, PlaybackState e) { if (e == PlaybackState.Loaded) return; _systemMediaTransportControls.IsStopEnabled = e == PlaybackState.Playing; _systemMediaTransportControls.IsRewindEnabled = e == PlaybackState.Playing; _systemMediaTransportControls.IsFastForwardEnabled = e == PlaybackState.Playing; _systemMediaTransportControls.IsChannelUpEnabled = false; _systemMediaTransportControls.IsChannelDownEnabled = false; _systemMediaTransportControls.IsRecordEnabled = false; _systemMediaTransportControls.PlaybackStatus = e switch { PlaybackState.None => MediaPlaybackStatus.Stopped, PlaybackState.Failed => MediaPlaybackStatus.Closed, PlaybackState.Playing => MediaPlaybackStatus.Playing, PlaybackState.Paused => MediaPlaybackStatus.Paused, PlaybackState.Loading => MediaPlaybackStatus.Changing, _ => ThrowHelper.ThrowArgumentOutOfRangeException(), }; } private async void PlaybackHandlerService_CurrentItemChanged(object? sender, PlaybackItem? e) { var updater = _systemMediaTransportControls.DisplayUpdater; updater.Type = MediaPlaybackType.Music; var musicProperties = updater.MusicProperties; if (e is null) { updater.ClearAll(); updater.Update(); return; } Guard.IsNotNull(e.Track, nameof(e.Track)); var sourceConfig = e; PlaybackHandlerService_PlaybackStateChanged(sender, _playbackHandlerService.PlaybackState); _systemMediaTransportControls.IsNextEnabled = _playbackHandlerService.NextItems.Count > 0; _systemMediaTransportControls.IsPreviousEnabled = _playbackHandlerService.PreviousItems.Count > 0; // Images if (e.Track.TotalImageCount == 0) { updater.Thumbnail = null; } else { // Just the first, we don't care about the size. var images = await e.Track.GetImagesAsync(1, 0).ToListAsync(); foreach (var image in images) { if (image.Uri.IsFile) { var file = await StorageFile.GetFileFromPathAsync(image.Uri.LocalPath); updater.Thumbnail = RandomAccessStreamReference.CreateFromFile(file); } else updater.Thumbnail = RandomAccessStreamReference.CreateFromUri(image.Uri); break; } } // Genres musicProperties.Genres.Clear(); var genres = await e.Track.GetGenresAsync(1, 0).ToListAsync(); foreach (var genre in genres) musicProperties.Genres.Add(genre.Name); // Track info musicProperties.TrackNumber = (uint)(e.Track.TrackNumber ?? 0); musicProperties.Title = e.Track.Name; // Artist info // Just the first (primary) artist. var artists = await e.Track.GetArtistItemsAsync(1, 0).ToListAsync(); foreach (var artist in artists) { musicProperties.Artist = artist.Name; break; } // Album info musicProperties.AlbumTrackCount = (uint)(e.Track.Album?.TotalTrackCount ?? 0); musicProperties.AlbumTitle = e.Track.Album?.Name ?? string.Empty; updater.Update(); } /// public void Dispose() { DetachEvents(); } } }