diff --git a/VolumeControl.Core/Config.cs b/VolumeControl.Core/Config.cs index 013cf5f76..02581fa95 100644 --- a/VolumeControl.Core/Config.cs +++ b/VolumeControl.Core/Config.cs @@ -291,6 +291,10 @@ private void PropertyWithCollectionChangedEvents_CollectionChanged(object? sende /// public TargetInfo TargetSession { get; set; } = TargetInfo.Empty; /// + /// Gets or sets the list of selected sessions. + /// + public ObservableImmutableList SelectedSessions { get; set; } = new(); + /// /// Gets or sets whether the target session is locked. /// public bool LockTargetSession { get; set; } = false; diff --git a/VolumeControl.Core/HotkeyManager.cs b/VolumeControl.Core/HotkeyManager.cs index 6fc4d783c..e7a4d3a06 100644 --- a/VolumeControl.Core/HotkeyManager.cs +++ b/VolumeControl.Core/HotkeyManager.cs @@ -262,7 +262,7 @@ private void RecheckAllSelected() { bool prev = this.Hotkeys.First().Registered; bool fullLoop = true; - for (int i = 1; i < this.Hotkeys.Count; ++i) + for (int i = 1, max = this.Hotkeys.Count; i < max; ++i) { if (this.Hotkeys[i].Registered != prev) { diff --git a/VolumeControl.CoreAudio/AudioSessionManager.cs b/VolumeControl.CoreAudio/AudioSessionManager.cs index eeba2a54a..edb27438a 100644 --- a/VolumeControl.CoreAudio/AudioSessionManager.cs +++ b/VolumeControl.CoreAudio/AudioSessionManager.cs @@ -172,14 +172,14 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio /// The with the given if found; otherwise . public AudioSession? FindSessionWithPID(uint processId, bool includeHiddenSessions = false) { // don't use FindSession here, this way is more than 2x faster: - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.PID == processId) return session; } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.PID == processId) return session; @@ -205,14 +205,14 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio { if (processName.Length == 0) return null; - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.ProcessName.Equals(processName, stringComparison)) return session; } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.ProcessName.Equals(processName, stringComparison)) return session; @@ -234,7 +234,7 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio { if (sessionName.Length == 0) return null; - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.HasMatchingName(sessionName, stringComparison)) @@ -242,7 +242,7 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.HasMatchingName(sessionName, stringComparison)) @@ -265,14 +265,14 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio { if (processIdentifier.Length == 0) return null; - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.ProcessIdentifier.Equals(processIdentifier, stringComparison)) return session; } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.ProcessIdentifier.Equals(processIdentifier, stringComparison)) return session; @@ -343,14 +343,14 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio { if (sessionIdentifier.Length == 0) return null; - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.SessionIdentifier.Equals(sessionIdentifier, stringComparison)) return session; } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.SessionIdentifier.Equals(sessionIdentifier, stringComparison)) return session; @@ -372,14 +372,14 @@ private PreviewSessionIsHiddenEventArgs NotifyPreviewSessionIsHidden(AudioSessio { if (sessionInstanceIdentifier.Length == 0) return null; - for (int i = 0; i < Sessions.Count; ++i) + for (int i = 0, max = Sessions.Count; i < max; ++i) { AudioSession session = Sessions[i]; if (session.SessionInstanceIdentifier.Equals(sessionInstanceIdentifier, stringComparison)) return session; } if (includeHiddenSessions) { - for (int i = 0; i < HiddenSessions.Count; ++i) + for (int i = 0, max = HiddenSessions.Count; i < max; ++i) { AudioSession session = HiddenSessions[i]; if (session.SessionInstanceIdentifier.Equals(sessionInstanceIdentifier, stringComparison)) return session; diff --git a/VolumeControl.CoreAudio/AudioSessionMultiSelector.cs b/VolumeControl.CoreAudio/AudioSessionMultiSelector.cs index 7c997eed3..363d19d68 100644 --- a/VolumeControl.CoreAudio/AudioSessionMultiSelector.cs +++ b/VolumeControl.CoreAudio/AudioSessionMultiSelector.cs @@ -21,11 +21,10 @@ public AudioSessionMultiSelector(AudioSessionManager audioSessionManager) AudioSessionManager = audioSessionManager; CurrentIndex = -1; + _selectedSessions = new(); _selectionStates = new(); - foreach (var session in Sessions) // populate the selection states list - { - _selectionStates.Add(NotifyPreviewSessionIsSelected(session, defaultIsSelected: false).IsSelected); - } + // populate the lists: + Sessions.ForEach(AddSession); AudioSessionManager.AddedSessionToList += this.AudioSessionManager_SessionAddedToList; AudioSessionManager.RemovingSessionFromList += this.AudioSessionManager_RemovingSessionFromList; ; @@ -46,12 +45,12 @@ public IReadOnlyList SelectionStates if (LockSelection) return; - var previouslySelectedItems = SelectedItems; + var previouslySelectedItems = SelectedSessions; - _selectionStates = (List)value; + _selectionStates = value.ToList(); NotifyPropertyChanged(); - var selectedItems = SelectedItems; + var selectedItems = SelectedSessions; // trigger selection changed notifications for (int i = 0, max = SelectionStates.Count; i < max; ++i) @@ -60,9 +59,13 @@ public IReadOnlyList SelectionStates var wasSelectedBefore = previouslySelectedItems.Contains(session); var isSelectedNow = selectedItems.Contains(session); if (wasSelectedBefore && !isSelectedNow) + { + AddSelectedSession(session); NotifySessionDeselected(session); + } else if (!wasSelectedBefore && isSelectedNow) { + RemoveSelectedSession(session); NotifySessionSelected(session); } } @@ -73,27 +76,38 @@ public IReadOnlyList SelectionStates /// Gets or sets the list of selected AudioSession instances. /// /// An array of all selected AudioSessions in the order that they appear in the Sessions list. - public AudioSession[] SelectedItems + public IReadOnlyList SelectedSessions { - get - { - List l = new(); - for (int i = 0; i < SelectionStates.Count; ++i) - { - if (SelectionStates[i]) - l.Add(Sessions[i]); - } - return l.ToArray(); - } + get => _selectedSessions; set { - for (int i = 0; i < SelectionStates.Count; ++i) + for (int i = 0, max = SelectionStates.Count; i < max; ++i) { - _selectionStates[i] = value.Contains(Sessions[i]); + var session = Sessions[i]; + var newState = value.Contains(session); + + if (_selectionStates[i] == newState) continue; //< no changes to make + + if (_selectionStates[i] = newState) + { + AddSelectedSession(session); + NotifySessionSelected(session); + } + else + { + RemoveSelectedSession(session); + NotifySessionDeselected(session); + } } NotifyPropertyChanged(); } } + private readonly List _selectedSessions; + /// + /// Gets whether there are any selected sessions or not. + /// + /// when there is at least one selected session; otherwise . + public bool HasSelectedSessions => SelectedSessions.Count > 0; /// public int CurrentIndex { @@ -104,7 +118,7 @@ public int CurrentIndex _currentIndex = value; NotifyPropertyChanged(); - NotifyPropertyChanged(nameof(CurrentItem)); + NotifyPropertyChanged(nameof(CurrentSession)); if (_currentIndex != -1) NotifyCurrentItemChanged(Sessions[_currentIndex]); } @@ -122,9 +136,9 @@ public bool LockCurrentIndex } private bool _lockCurrentIndex; /// - /// Gets or sets the item that the selector is currently at. + /// Gets or sets the session that the selector is currently pointing at. /// - public AudioSession? CurrentItem + public AudioSession? CurrentSession { get => CurrentIndex == -1 ? null : Sessions[CurrentIndex]; set @@ -153,9 +167,24 @@ public bool LockSelection { _lockSelection = value; NotifyPropertyChanged(); + if (LockCurrentIndexOnLockSelection) + LockCurrentIndex = value; } } private bool _lockSelection; + /// + /// Gets or sets whether the LockCurrentIndex property is also set when LockSelection is changed. + /// + public bool LockCurrentIndexOnLockSelection + { + get => _lockCurrentIndexOnLockSelection; + set + { + _lockCurrentIndexOnLockSelection = value; + NotifyPropertyChanged(); + } + } + private bool _lockCurrentIndexOnLockSelection; #endregion Properties #region Events @@ -191,6 +220,45 @@ private PreviewSessionIsSelectedEventArgs NotifyPreviewSessionIsSelected(AudioSe #region Methods + private void AddSelectedSession(AudioSession audioSession) + { + var hadSelectedSessions = HasSelectedSessions; + _selectedSessions.Add(audioSession); + if (!hadSelectedSessions) + NotifyPropertyChanged(nameof(HasSelectedSessions)); + } + private void RemoveSelectedSession(AudioSession audioSession) + { + if (_selectedSessions.Remove(audioSession) && _selectedSessions.Count == 0) + { + NotifyPropertyChanged(nameof(HasSelectedSessions)); + } + } + + #region Add/Remove Session + private void AddSession(AudioSession session) + { + var isSelected = NotifyPreviewSessionIsSelected(session, defaultIsSelected: false).IsSelected; + _selectionStates.Insert(Sessions.IndexOf(session), isSelected); + if (isSelected) + { + AddSelectedSession(session); + NotifySessionSelected(session); + } + } + private void RemoveSession(AudioSession session) + { + var index = Sessions.IndexOf(session); //< we can get the index here because the session hasn't been removed yet + var wasSelected = _selectionStates[index]; + _selectionStates.RemoveAt(index); + if (wasSelected) + { + RemoveSelectedSession(session); + NotifySessionDeselected(session); + } + } + #endregion Add/Remove Session + #region Get/Set SessionIsSelected /// /// Gets whether the specified is selected or not. @@ -221,9 +289,13 @@ public void SetSessionIsSelected(AudioSession audioSession, bool isSelected) if (_selectionStates[index] == isSelected) return; //< don't do anything if nothing is changing if (_selectionStates[index] = isSelected) + { + AddSelectedSession(audioSession); NotifySessionSelected(audioSession); + } else { + RemoveSelectedSession(audioSession); NotifySessionDeselected(audioSession); } } @@ -240,11 +312,16 @@ public void SetSessionIsSelected(int index, bool isSelected) if (_selectionStates[index] == isSelected) return; //< don't do anything if nothing is changing + var session = Sessions[index]; if (_selectionStates[index] = isSelected) - NotifySessionSelected(Sessions[index]); + { + AddSelectedSession(session); + NotifySessionSelected(session); + } else { - NotifySessionDeselected(Sessions[index]); + RemoveSelectedSession(session); + NotifySessionDeselected(session); } } #endregion Get/Set SessionIsSelected @@ -275,7 +352,8 @@ public void SelectCurrentItem() if (LockSelection || CurrentIndex == -1) return; _selectionStates[CurrentIndex] = true; - NotifySessionSelected(CurrentItem!); + AddSelectedSession(CurrentSession!); + NotifySessionSelected(CurrentSession!); } /// /// Deselects the CurrentItem. @@ -288,7 +366,8 @@ public void DeselectCurrentItem() if (LockSelection || CurrentIndex == -1) return; _selectionStates[CurrentIndex] = false; - NotifySessionDeselected(CurrentItem!); + RemoveSelectedSession(CurrentSession!); + NotifySessionDeselected(CurrentSession!); } /// /// Toggles whether the CurrentItem is selected. @@ -301,10 +380,14 @@ public void ToggleSelectCurrentItem() if (LockSelection || CurrentIndex == -1) return; if (_selectionStates[CurrentIndex] = !SelectionStates[CurrentIndex]) - NotifySessionSelected(CurrentItem!); + { + AddSelectedSession(CurrentSession!); + NotifySessionSelected(CurrentSession!); + } else { - NotifySessionDeselected(CurrentItem!); + RemoveSelectedSession(CurrentSession!); + NotifySessionDeselected(CurrentSession!); } } #endregion Select/Deselect/ToggleSelect CurrentItem @@ -320,7 +403,7 @@ public void IncrementCurrentIndex() { if (LockCurrentIndex) return; - if (CurrentIndex + 1 < SelectionStates.Count) + if (CurrentIndex < SelectionStates.Count - 1) ++CurrentIndex; else { // loopback: @@ -341,7 +424,7 @@ public void DecrementCurrentIndex() --CurrentIndex; else { // loopback: - CurrentIndex = SelectionStates.Count; + CurrentIndex = SelectionStates.Count - 1; } } /// @@ -364,20 +447,9 @@ public void UnsetCurrentIndex() #region AudioSessionManager private void AudioSessionManager_SessionAddedToList(object? sender, AudioSession e) - { - var isSelected = NotifyPreviewSessionIsSelected(e, defaultIsSelected: false).IsSelected; - _selectionStates.Insert(Sessions.IndexOf(e), isSelected); - if (isSelected) - NotifySessionSelected(e); - } + => AddSession(e); private void AudioSessionManager_RemovingSessionFromList(object? sender, AudioSession e) - { - var index = Sessions.IndexOf(e); //< we can get the index here because the session hasn't been removed yet - var wasSelected = _selectionStates[index]; - _selectionStates.RemoveAt(index); - if (wasSelected) - NotifySessionDeselected(e); - } + => RemoveSession(e); #endregion AudioSessionManager #endregion EventHandlers diff --git a/VolumeControl.CoreAudio/Events/PreviewSessionIsSelectedEventArgs.cs b/VolumeControl.CoreAudio/Events/PreviewSessionIsSelectedEventArgs.cs index 86dd329ee..ea12f2912 100644 --- a/VolumeControl.CoreAudio/Events/PreviewSessionIsSelectedEventArgs.cs +++ b/VolumeControl.CoreAudio/Events/PreviewSessionIsSelectedEventArgs.cs @@ -1,6 +1,4 @@ -using VolumeControl.CoreAudio; - -namespace VolumeControl.CoreAudio.Events +namespace VolumeControl.CoreAudio.Events { /// /// Event arguments object for the event type. diff --git a/VolumeControl.HotkeyActions/AudioSessionActions.cs b/VolumeControl.HotkeyActions/AudioSessionActions.cs index 0e6ae5915..a58c438d9 100644 --- a/VolumeControl.HotkeyActions/AudioSessionActions.cs +++ b/VolumeControl.HotkeyActions/AudioSessionActions.cs @@ -20,8 +20,9 @@ public sealed class AudioSessionActions #region Properties private static VCAPI VCAPI => VCAPI.Default; - private static AudioSessionSelector AudioSessionSelector => VCAPI.AudioSessionSelector; - private static AudioSession? SelectedSession => AudioSessionSelector.Selected; + private static AudioSessionMultiSelector MultiSelector => VCAPI.AudioSessionMultiSelector; + private static IReadOnlyList SelectedSessions => MultiSelector.SelectedSessions; + private static AudioSession? CurrentSession => MultiSelector.CurrentSession; #endregion Properties #region Action Methods @@ -29,156 +30,228 @@ public sealed class AudioSessionActions [HotkeyActionSetting(ActionTargetSpecifierName, typeof(ActionTargetSpecifier), ActionTargetSpecifierDescription)] public void VolumeUp(object? sender, HotkeyActionPressedEventArgs e) { - bool forceShowNotification = false; + bool showNotification = false; if (e.GetActionSettingValue(ActionTargetSpecifierName) is ActionTargetSpecifier specifier && specifier.Targets.Count > 0) - { - for (int i = 0; i < specifier.Targets.Count; ++i) + { // operate on target overrides: + for (int i = 0, max = specifier.Targets.Count; i < max; ++i) { if (VCAPI.AudioSessionManager.FindSessionWithName(specifier.Targets[i].Value) is AudioSession session) { session.IncreaseVolume(VCAPI.Settings.VolumeStepSize); - forceShowNotification = true; + showNotification = true; } } } - else SelectedSession?.IncreaseVolume(VCAPI.Settings.VolumeStepSize); + else if (SelectedSessions.Count > 0) + { // operate on selected sessions: + for (int i = 0, max = SelectedSessions.Count; i < max; ++i) + { + SelectedSessions[i].IncreaseVolume(VCAPI.Settings.VolumeStepSize); + showNotification = true; + } + } + else if (CurrentSession != null) + { // operate on current item: + CurrentSession.IncreaseVolume(VCAPI.Settings.VolumeStepSize); + showNotification = true; + } - if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) return; //< don't show notifs if they're disabled on volume change + if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) + return; //< don't show notifs if they're disabled on volume change - if (forceShowNotification || VCAPI.AudioSessionSelector.Selected is not null) + if (showNotification) VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Decreases the volume of the selected session by the value of VolumeStep.")] [HotkeyActionSetting(ActionTargetSpecifierName, typeof(ActionTargetSpecifier), ActionTargetSpecifierDescription)] public void VolumeDown(object? sender, HotkeyActionPressedEventArgs e) { - bool forceShowNotification = false; + bool showNotification = false; if (e.GetActionSettingValue(ActionTargetSpecifierName) is ActionTargetSpecifier specifier && specifier.Targets.Count > 0) - { - for (int i = 0; i < specifier.Targets.Count; ++i) + { // operate on target overrides: + for (int i = 0, max = specifier.Targets.Count; i < max; ++i) { if (VCAPI.AudioSessionManager.FindSessionWithName(specifier.Targets[i].Value) is AudioSession session) { session.DecreaseVolume(VCAPI.Settings.VolumeStepSize); - forceShowNotification = true; + showNotification = true; } } } - else SelectedSession?.DecreaseVolume(VCAPI.Settings.VolumeStepSize); + else if (SelectedSessions.Count > 0) + { // operate on selected sessions: + for (int i = 0, max = SelectedSessions.Count; i < max; ++i) + { + SelectedSessions[i].DecreaseVolume(VCAPI.Settings.VolumeStepSize); + showNotification = true; + } + } + else if (CurrentSession != null) + { // operate on current item: + CurrentSession.DecreaseVolume(VCAPI.Settings.VolumeStepSize); + showNotification = true; + } - if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) return; //< don't show notifs if they're disabled on volume change + if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) + return; //< don't show notifs if they're disabled on volume change - if (forceShowNotification || VCAPI.AudioSessionSelector.Selected is not null) + if (showNotification) VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Mutes the selected session.")] [HotkeyActionSetting(ActionTargetSpecifierName, typeof(ActionTargetSpecifier), ActionTargetSpecifierDescription)] public void Mute(object? sender, HotkeyActionPressedEventArgs e) { - bool forceShowNotification = false; + bool showNotification = false; if (e.GetActionSettingValue(ActionTargetSpecifierName) is ActionTargetSpecifier specifier && specifier.Targets.Count > 0) - { - for (int i = 0; i < specifier.Targets.Count; ++i) + { // operate on target overrides: + for (int i = 0, max = specifier.Targets.Count; i < max; ++i) { if (VCAPI.AudioSessionManager.FindSessionWithName(specifier.Targets[i].Value) is AudioSession session) { session.SetMute(true); - forceShowNotification = true; + showNotification = true; } } } - else SelectedSession?.SetMute(true); + else if (SelectedSessions.Count > 0) + { // operate on selected sessions: + for (int i = 0, max = SelectedSessions.Count; i < max; ++i) + { + SelectedSessions[i].SetMute(true); + showNotification = true; + } + } + else if (CurrentSession != null) + { // operate on current item: + CurrentSession.SetMute(true); + showNotification = true; + } - if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) return; //< don't show notifs if they're disabled on volume change + if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) + return; //< don't show notifs if they're disabled on volume change - if (forceShowNotification || VCAPI.AudioSessionSelector.Selected is not null) + if (showNotification) VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Unmutes the selected session.")] [HotkeyActionSetting(ActionTargetSpecifierName, typeof(ActionTargetSpecifier), ActionTargetSpecifierDescription)] public void Unmute(object? sender, HotkeyActionPressedEventArgs e) { - bool forceShowNotification = false; + bool showNotification = false; if (e.GetActionSettingValue(ActionTargetSpecifierName) is ActionTargetSpecifier specifier && specifier.Targets.Count > 0) - { - for (int i = 0; i < specifier.Targets.Count; ++i) + { // operate on target overrides: + for (int i = 0, max = specifier.Targets.Count; i < max; ++i) { if (VCAPI.AudioSessionManager.FindSessionWithName(specifier.Targets[i].Value) is AudioSession session) { session.SetMute(false); - forceShowNotification = true; + showNotification = true; } } } - else SelectedSession?.SetMute(false); + else if (SelectedSessions.Count > 0) + { // operate on selected sessions: + for (int i = 0, max = SelectedSessions.Count; i < max; ++i) + { + SelectedSessions[i].SetMute(false); + showNotification = true; + } + } + else if (CurrentSession != null) + { // operate on current item: + CurrentSession.SetMute(false); + showNotification = true; + } - if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) return; //< don't show notifs if they're disabled on volume change + if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) + return; //< don't show notifs if they're disabled on volume change - if (forceShowNotification || VCAPI.AudioSessionSelector.Selected is not null) + if (showNotification) VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Toggles the selected session's mute state.")] [HotkeyActionSetting(ActionTargetSpecifierName, typeof(ActionTargetSpecifier), ActionTargetSpecifierDescription)] public void ToggleMute(object? sender, HotkeyActionPressedEventArgs e) { - bool forceShowNotification = false; + bool showNotification = false; if (e.GetActionSettingValue(ActionTargetSpecifierName) is ActionTargetSpecifier specifier && specifier.Targets.Count > 0) - { - for (int i = 0; i < specifier.Targets.Count; ++i) + { // operate on target overrides: + for (int i = 0, max = specifier.Targets.Count; i < max; ++i) { if (VCAPI.AudioSessionManager.FindSessionWithName(specifier.Targets[i].Value) is AudioSession session) { session.ToggleMute(); - forceShowNotification = true; + showNotification = true; } } } - else SelectedSession?.ToggleMute(); + else if (SelectedSessions.Count > 0) + { // operate on selected sessions: + for (int i = 0, max = SelectedSessions.Count; i < max; ++i) + { + SelectedSessions[i].ToggleMute(); + showNotification = true; + } + } + else if (CurrentSession != null) + { // operate on current item: + CurrentSession.ToggleMute(); + showNotification = true; + } - if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) return; //< don't show notifs if they're disabled on volume change + if (!VCAPI.Settings.SessionListNotificationConfig.ShowOnVolumeChanged) + return; //< don't show notifs if they're disabled on volume change - if (forceShowNotification || VCAPI.AudioSessionSelector.Selected is not null) + if (showNotification) VCAPI.ShowSessionListNotification(); } - [HotkeyAction(Description = "Selects the next session in the list.")] + [HotkeyAction(Description = "Moves the selector to the next session in the list.")] public void SelectNext(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.SelectNextSession(); + MultiSelector.IncrementCurrentIndex(); VCAPI.ShowSessionListNotification(); } - [HotkeyAction(Description = "Selects the previous session in the list.")] + [HotkeyAction(Description = "Moves the selector to the previous session in the list.")] public void SelectPrevious(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.SelectPreviousSession(); + MultiSelector.DecrementCurrentIndex(); VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Locks the selected session, preventing it from being changed.")] public void Lock(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.LockSelection = true; + MultiSelector.LockSelection = true; VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Unlocks the selected session, allowing it to be changed.")] public void Unlock(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.LockSelection = false; + MultiSelector.LockSelection = false; VCAPI.ShowSessionListNotification(); } [HotkeyAction(Description = "Toggles whether the selected session can be changed or not.")] public void ToggleLock(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.LockSelection = !AudioSessionSelector.LockSelection; + MultiSelector.LockSelection = !MultiSelector.LockSelection; VCAPI.ShowSessionListNotification(); } - [HotkeyAction(Description = "Changes the selected session to null.")] + [HotkeyAction(Description = "Unsets the selector.")] public void Deselect(object? sender, HotkeyActionPressedEventArgs e) { - AudioSessionSelector.DeselectSession(); + MultiSelector.DeselectCurrentItem(); + + VCAPI.ShowSessionListNotification(); + } + [HotkeyAction(Description = "(De)selects the current session.")] + public void ToggleSelected(object? sender, HotkeyActionPressedEventArgs e) + { + MultiSelector.ToggleSelectCurrentItem(); VCAPI.ShowSessionListNotification(); } diff --git a/VolumeControl.SDK/Internal/Initializer.cs b/VolumeControl.SDK/Internal/Initializer.cs index 64d543097..2d3c4bb62 100644 --- a/VolumeControl.SDK/Internal/Initializer.cs +++ b/VolumeControl.SDK/Internal/Initializer.cs @@ -18,12 +18,19 @@ public static class Initializer /// The audio device manager object. /// The audio device selection manager object. /// The audio session manager object. - /// The audio session selection manager object. + /// The audio session selection manager object. /// The hotkey manager object. /// The mixer window's handle /// The program settings container object. /// Initialize was already called previously. - public static VCAPI Initialize(AudioDeviceManager audioDeviceManager, AudioDeviceSelector audioDeviceSelector, AudioSessionManager audioSessionManager, AudioSessionSelector audioSessionSelector, HotkeyManager mgr, IntPtr mainWindowHWnd, Config settings) + public static VCAPI Initialize( + AudioDeviceManager audioDeviceManager, + AudioDeviceSelector audioDeviceSelector, + AudioSessionManager audioSessionManager, + AudioSessionMultiSelector audioSessionMultiSelector, + HotkeyManager mgr, + IntPtr mainWindowHWnd, + Config settings) { if (_initialized) { @@ -32,7 +39,14 @@ public static VCAPI Initialize(AudioDeviceManager audioDeviceManager, AudioDevic } _initialized = true; - return VCAPI.Default = new(audioDeviceManager, audioDeviceSelector, audioSessionManager, audioSessionSelector, mgr, mainWindowHWnd, settings); + return VCAPI.Default = new( + audioDeviceManager, + audioDeviceSelector, + audioSessionManager, + audioSessionMultiSelector, + mgr, + mainWindowHWnd, + settings); } } } diff --git a/VolumeControl.SDK/VCAPI.cs b/VolumeControl.SDK/VCAPI.cs index 54ae372f8..beee2e498 100644 --- a/VolumeControl.SDK/VCAPI.cs +++ b/VolumeControl.SDK/VCAPI.cs @@ -15,12 +15,12 @@ namespace VolumeControl.SDK public class VCAPI { #region Initializer - internal VCAPI(AudioDeviceManager audioDeviceManager, AudioDeviceSelector audioDeviceSelector, AudioSessionManager audioSessionManager, AudioSessionSelector audioSessionSelector, HotkeyManager hkManager, IntPtr MainHWnd, Config settings) + internal VCAPI(AudioDeviceManager audioDeviceManager, AudioDeviceSelector audioDeviceSelector, AudioSessionManager audioSessionManager, AudioSessionMultiSelector audioSessionMultiSelector, HotkeyManager hkManager, IntPtr MainHWnd, Config settings) { AudioDeviceManager = audioDeviceManager; AudioDeviceSelector = audioDeviceSelector; AudioSessionManager = audioSessionManager; - AudioSessionSelector = audioSessionSelector; + AudioSessionMultiSelector = audioSessionMultiSelector; HotkeyManager = hkManager; MainWindowHWnd = MainHWnd; Settings = settings; @@ -54,7 +54,7 @@ internal VCAPI(AudioDeviceManager audioDeviceManager, AudioDeviceSelector audioD /// /// Manages the selected instance for the . /// - public AudioSessionSelector AudioSessionSelector { get; } + public AudioSessionMultiSelector AudioSessionMultiSelector { get; } /// /// This is the global responsible for managing hotkeys. /// diff --git a/VolumeControl/Mixer.xaml b/VolumeControl/Mixer.xaml index 1e1e97391..5df998fae 100644 --- a/VolumeControl/Mixer.xaml +++ b/VolumeControl/Mixer.xaml @@ -227,14 +227,14 @@ - + - + @@ -183,50 +253,26 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/VolumeControl/SessionListNotification.xaml.cs b/VolumeControl/SessionListNotification.xaml.cs index e84a1f1dc..017f3112a 100644 --- a/VolumeControl/SessionListNotification.xaml.cs +++ b/VolumeControl/SessionListNotification.xaml.cs @@ -1,13 +1,13 @@ using System; using System.ComponentModel; using System.Windows; +using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media.Animation; using VolumeControl.Core; using VolumeControl.Helpers; using VolumeControl.Log; -using VolumeControl.SDK; using VolumeControl.SDK.Internal; using VolumeControl.ViewModels; using VolumeControl.WPF; @@ -334,13 +334,13 @@ private void ListView_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton.Equals(MouseButton.Middle)) { // middle mouse toggles session lock - VCSettings.AudioAPI.AudioSessionSelector.LockSelection = !VCSettings.AudioAPI.AudioSessionSelector.LockSelection; + VCSettings.AudioAPI.AudioSessionMultiSelector.LockSelection = !VCSettings.AudioAPI.AudioSessionMultiSelector.LockSelection; e.Handled = true; return; } else if (!Settings.LockTargetSession && e.ChangedButton.Equals(MouseButton.Right)) { // right mouse deselects - VCSettings.AudioAPI.AudioSessionSelector.DeselectSession(); + VCSettings.AudioAPI.AudioSessionMultiSelector.UnsetCurrentIndex(); e.Handled = true; return; } @@ -359,10 +359,7 @@ private void ListView_PreviewMouseDown(object sender, MouseButtonEventArgs e) /// private void ListViewItem_Selected(object sender, RoutedEventArgs e) { - if (VCAPI.Default.AudioSessionSelector.Selected is null) - { // only release mouse capture when there was no select - ListView.ReleaseMouseCapture(); - } + ListView.ReleaseMouseCapture(); } #endregion ListViewItem diff --git a/VolumeControl/ViewModels/AudioDeviceManagerVM.cs b/VolumeControl/ViewModels/AudioDeviceManagerVM.cs index 97c4ec6a0..66fce74a5 100644 --- a/VolumeControl/ViewModels/AudioDeviceManagerVM.cs +++ b/VolumeControl/ViewModels/AudioDeviceManagerVM.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Threading; using VolumeControl.Core; +using VolumeControl.Core.Helpers; using VolumeControl.CoreAudio; using VolumeControl.CoreAudio.Events; using VolumeControl.Log; @@ -23,45 +24,85 @@ public sealed class AudioDeviceManagerVM : DependencyObject, INotifyPropertyChan { public AudioDeviceManagerVM() { - AudioDeviceManager = new(DataFlow.Render); + var doDebugLogging = Log.IsEventVisible(VolumeControl.Log.Enum.EventType.DEBUG); + if (doDebugLogging) Log.Debug("Started initializing CoreAudio APIs."); - Log.Debug("Initializing Audio Devices."); + // # INIT DEVICES # + // setup the device manager + AudioDeviceManager = new(DataFlow.Render); + // setup the devices list Devices = new(); + // attach handlers for devices being added/removed at runtime AudioDeviceManager.DeviceAddedToList += AudioDeviceManager_DeviceAddedToList; AudioDeviceManager.DeviceRemovedFromList += AudioDeviceManager_DeviceRemovedFromList; - foreach (var device in AudioDeviceManager.Devices) + // populate the devices list + for (int i = 0, max = AudioDeviceManager.Devices.Count; i < max; ++i) { - Devices.Add(new(device)); + Devices.Add(new AudioDeviceVM(AudioDeviceManager.Devices[i])); } - Log.Debug("Finished initializing Audio Devices, started initializing Audio Sessions."); + // setup the device selection manager + AudioDeviceSelector = new(AudioDeviceManager); - AllSessions = new(); + if (doDebugLogging) Log.Debug($"Successfully initialized {AudioDeviceManager.Devices.Count} audio devices."); + // # INIT SESSIONS # + // setup the session manager AudioSessionManager = new(); - AudioSessionManager.PreviewSessionName += this.AudioSessionManager_PreviewSessionName; + // setup the sessions list + AllSessions = new(); + + // attach handlers for sessions being added/removed at runtime AudioSessionManager.AddedSessionToList += this.AudioSessionManager_AddedSessionToList; AudioSessionManager.RemovedSessionFromList += this.AudioSessionManager_RemovedSessionFromList; - // hide sessions when started if their name is in the hidden sessions list + // attach handler to override session names + AudioSessionManager.PreviewSessionName += this.AudioSessionManager_PreviewSessionName; + + // attach handler to set session hidden state when added AudioSessionManager.PreviewSessionIsHidden += this.AudioSessionManager_PreviewSessionIsHidden; - // hide/unhide sessions when added/removed from the hidden sessions list + // attach handler to hide/unhide sessions when the hidden sessions names list is changed Settings.HiddenSessionProcessNames.CollectionChanged += this.HiddenSessionProcessNames_CollectionChanged; - // populate the AudioSessionManager with AudioDeviceSessionManager instances, which also populates the lists of sessions - Devices.Select(d => d.AudioDevice.SessionManager).ForEach(AudioSessionManager.AddSessionManager); - - AudioDeviceSelector = new(AudioDeviceManager); - AudioSessionSelector = new(AudioSessionManager) + // setup the session selection manager + SelectedSessions = new(); + SelectedSessionsComparer = Comparer.Create((a, b) => AllSessions.IndexOf(a) - AllSessions.IndexOf(b)); + AudioSessionMultiSelector = new(AudioSessionManager) { LockSelection = Settings.LockTargetSession, + LockCurrentIndex = Settings.LockTargetSession, + LockCurrentIndexOnLockSelection = true }; - Log.Debug("Finished initializing Audio Sessions."); + // populate the sessions lists + Devices.Select(d => d.AudioDevice.SessionManager).ForEach(AudioSessionManager.AddSessionManager); + + // select all previously-selected sessions + foreach (var item in Settings.SelectedSessions) + { + if (AudioSessionManager.FindSessionWithSimilarProcessIdentifier(item.ProcessIdentifier, StringComparison.OrdinalIgnoreCase) is AudioSession session) + { + AudioSessionMultiSelector.SetSessionIsSelected(session, true); + this.SelectedSessions.Add(GetAudioSessionVM(session)!); + } + } + // set the current session + var currentSession = AudioSessionManager.FindSessionWithSimilarProcessIdentifier(Settings.TargetSession.ProcessIdentifier, StringComparison.OrdinalIgnoreCase); + AudioSessionMultiSelector.CurrentSession = currentSession; + this.CurrentSession = currentSession == null ? null : GetAudioSessionVM(currentSession); + + // attach handlers to session selection events + AudioSessionMultiSelector.SessionSelected += this.AudioSessionMultiSelector_SessionSelected; + AudioSessionMultiSelector.SessionDeselected += this.AudioSessionMultiSelector_SessionDeselected; + AudioSessionMultiSelector.CurrentItemChanged += this.AudioSessionMultiSelector_CurrentItemChanged; + + if (doDebugLogging) Log.Debug($"Successfully initialized {AudioSessionManager.Sessions.Count + AudioSessionManager.HiddenSessions.Count} {(AudioSessionManager.HiddenSessions.Count == 0 ? "" : $"({AudioSessionManager.HiddenSessions.Count} hidden)")} audio sessions."); + + if (doDebugLogging) Log.Debug("Finished initializing CoreAudio APIs."); } #region Events @@ -79,8 +120,24 @@ public AudioDeviceManagerVM() public ObservableImmutableList Devices { get; } public CoreAudio.AudioSessionManager AudioSessionManager { get; } public ObservableImmutableList AllSessions { get; } + public ObservableImmutableList SelectedSessions { get; } + public Comparer SelectedSessionsComparer { get; } + public AudioSessionVM? CurrentSession { get; set; } public AudioDeviceSelector AudioDeviceSelector { get; } - public AudioSessionSelector AudioSessionSelector { get; } + public AudioSessionMultiSelector AudioSessionMultiSelector { get; } + public bool? AllSessionsSelected + { + get + { + var selected = SelectedSessions.Count; + var total = AllSessions.Count; + + if (selected == total) return true; + else if (selected == 0) return false; + else return null; + } + set => AudioSessionMultiSelector.SetAllSessionSelectionStates(value == true); + } #endregion Properties #region Methods @@ -122,7 +179,7 @@ private void AudioDeviceManager_DeviceRemovedFromList(object? sender, AudioDevic #region AudioSessionManager private void AudioSessionManager_AddedSessionToList(object? sender, AudioSession e) - => Dispatcher.Invoke(() => AllSessions.Add(new AudioSessionVM(e))); + => Dispatcher.Invoke(() => AllSessions.Add(new AudioSessionVM(this, e))); private void AudioSessionManager_RemovedSessionFromList(object? sender, AudioSession e) { // check if the vm actually exists to prevent possible exception in rare cases @@ -183,6 +240,29 @@ private void HiddenSessionProcessNames_CollectionChanged(object? sender, System. } #endregion HiddenSessionProcessNames + #region AudioSessionMultiSelector + private void AudioSessionMultiSelector_SessionSelected(object? sender, AudioSession e) + { + Settings.SelectedSessions.AddIfUnique(e.GetTargetInfo()); + this.SelectedSessions.Add(GetAudioSessionVM(e)!); + this.SelectedSessions.Sort(SelectedSessionsComparer); + // update the all selected checkbox + NotifyPropertyChanged(nameof(AllSessionsSelected)); + } + private void AudioSessionMultiSelector_SessionDeselected(object? sender, AudioSession e) + { + Settings.SelectedSessions.Remove(e.GetTargetInfo()); + this.SelectedSessions.Remove(GetAudioSessionVM(e)!); + // update the all selected checkbox + NotifyPropertyChanged(nameof(AllSessionsSelected)); + } + private void AudioSessionMultiSelector_CurrentItemChanged(object? sender, AudioSession? e) + { + Settings.TargetSession = e?.GetTargetInfo() ?? TargetInfo.Empty; + CurrentSession = e == null ? null : GetAudioSessionVM(e); + } + #endregion AudioSessionMultiSelector + #endregion EventHandlers } } diff --git a/VolumeControl/ViewModels/AudioDeviceVM.cs b/VolumeControl/ViewModels/AudioDeviceVM.cs index f6cb514a6..70045b323 100644 --- a/VolumeControl/ViewModels/AudioDeviceVM.cs +++ b/VolumeControl/ViewModels/AudioDeviceVM.cs @@ -1,11 +1,9 @@ using System; using System.ComponentModel; -using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Media; using VolumeControl.CoreAudio; using VolumeControl.WPF; -using VolumeControl.WPF.Collections; namespace VolumeControl.ViewModels { @@ -21,17 +19,17 @@ public AudioDeviceVM(AudioDevice audioDevice) Icon = IconExtractor.TryExtractFromPath(AudioDevice.IconPath, out ImageSource icon) ? icon : null; Icon?.Freeze(); //< prevents WPF exceptions in some cases - Sessions = new(); + //Sessions = new(); // attach events to add and remove audio sessions from the Sessions list - AudioDevice.SessionManager.SessionAddedToList += this.SessionManager_SessionAddedToList; - AudioDevice.SessionManager.SessionRemovedFromList += this.SessionManager_SessionRemovedFromList; + //AudioDevice.SessionManager.SessionAddedToList += this.SessionManager_SessionAddedToList; + //AudioDevice.SessionManager.SessionRemovedFromList += this.SessionManager_SessionRemovedFromList; // initialize Sessions list - foreach (var session in AudioDevice.SessionManager.Sessions) - { - Sessions.Add(new AudioSessionVM(session)); - } + //foreach (var session in AudioDevice.SessionManager.Sessions) + //{ + // Sessions.Add(new AudioSessionVM(session)); + //} } #endregion Constructor @@ -54,7 +52,7 @@ public ImageSource? Icon private ImageSource? _icon = null; public string Name => AudioDevice.Name; public string DeviceFriendlyName => AudioDevice.FullName; - public ObservableImmutableList Sessions { get; } + //public ObservableImmutableList Sessions { get; } #endregion Properties #region IDisposable Implementation @@ -68,16 +66,16 @@ public void Dispose() #region EventHandlers #region SessionManager - private void SessionManager_SessionAddedToList(object? sender, AudioSession e) - { - Sessions.Add(new AudioSessionVM(e)); - } - private void SessionManager_SessionRemovedFromList(object? sender, AudioSession e) - { - var vm = Sessions.First(svm => svm.AudioSession.Equals(e)); - Sessions.Remove(vm); - vm.Dispose(); - } + //private void SessionManager_SessionAddedToList(object? sender, AudioSession e) + //{ + // Sessions.Add(new AudioSessionVM(e)); + //} + //private void SessionManager_SessionRemovedFromList(object? sender, AudioSession e) + //{ + // var vm = Sessions.First(svm => svm.AudioSession.Equals(e)); + // Sessions.Remove(vm); + // vm.Dispose(); + //} #endregion SessionManager #endregion EventHandlers diff --git a/VolumeControl/ViewModels/AudioSessionVM.cs b/VolumeControl/ViewModels/AudioSessionVM.cs index bce4f6f01..35a723d25 100644 --- a/VolumeControl/ViewModels/AudioSessionVM.cs +++ b/VolumeControl/ViewModels/AudioSessionVM.cs @@ -15,13 +15,18 @@ namespace VolumeControl.ViewModels public sealed class AudioSessionVM : INotifyPropertyChanged, IDisposable { #region Constructor - public AudioSessionVM(AudioSession audioSession) + public AudioSessionVM(AudioDeviceManagerVM manager, AudioSession audioSession) { + AudioDeviceManagerVM = manager; AudioSession = audioSession; Icon = GetIcon(); AudioSession.IconPathChanged += (s, e) => Icon = GetIcon(); + + // update bindings on the IsSelected property when this session is selected or deselected + manager.AudioSessionMultiSelector.SessionSelected += this.AudioSessionMultiSelector_SessionSelected_SessionDeselected; + manager.AudioSessionMultiSelector.SessionDeselected += this.AudioSessionMultiSelector_SessionSelected_SessionDeselected; } #endregion Constructor @@ -31,6 +36,7 @@ public AudioSessionVM(AudioSession audioSession) #endregion Events #region Properties + AudioDeviceManagerVM AudioDeviceManagerVM { get; } public AudioSession AudioSession { get; } public ImageSource? Icon { @@ -46,6 +52,17 @@ public ImageSource? Icon public string ProcessName => AudioSession.ProcessName; public string ProcessIdentifier => AudioSession.ProcessIdentifier; public string Name => AudioSession.Name; + public bool IsSelected + { + get => SDK.VCAPI.Default.AudioSessionMultiSelector.GetSessionIsSelected(this.AudioSession); + set + { + _isSelectedChanging = true; + SDK.VCAPI.Default.AudioSessionMultiSelector.SetSessionIsSelected(this.AudioSession, value); + _isSelectedChanging = false; + NotifyPropertyChanged(); + } + } #endregion Properties #region Methods @@ -92,5 +109,16 @@ public void Dispose() GC.SuppressFinalize(this); } #endregion IDisposable Implementation + + private bool _isSelectedChanging; + private void AudioSessionMultiSelector_SessionSelected_SessionDeselected(object? sender, AudioSession e) + { + if (_isSelectedChanging) return; //< don't update if the source is this object + + if (e.Equals(this.AudioSession)) + { + NotifyPropertyChanged(nameof(IsSelected)); + } + } } } diff --git a/VolumeControl/ViewModels/VolumeControlVM.cs b/VolumeControl/ViewModels/VolumeControlVM.cs index 8bb54d428..a06b280e1 100644 --- a/VolumeControl/ViewModels/VolumeControlVM.cs +++ b/VolumeControl/ViewModels/VolumeControlVM.cs @@ -41,7 +41,7 @@ public VolumeControlVM() : base() this.HotkeyAPI = new(actionManager); // Initialize the addon API - var api = Initializer.Initialize(this.AudioAPI.AudioDeviceManager, this.AudioAPI.AudioDeviceSelector, this.AudioAPI.AudioSessionManager, this.AudioAPI.AudioSessionSelector, this.HotkeyAPI, this.MainWindowHandle, (AppConfig.Configuration.Default as Config)!); + var api = Initializer.Initialize(this.AudioAPI.AudioDeviceManager, this.AudioAPI.AudioDeviceSelector, this.AudioAPI.AudioSessionManager, this.AudioAPI.AudioSessionMultiSelector, this.HotkeyAPI, this.MainWindowHandle, (AppConfig.Configuration.Default as Config)!); VCHotkeyAddon hotkeyAddonLoader = new(); @@ -82,7 +82,7 @@ public VolumeControlVM() : base() this.HotkeyAPI.LoadHotkeys(); // attach event to update TargetSessionText & LockTargetSession properties - VCAPI.Default.AudioSessionSelector.PropertyChanged += this.AudioSessionSelector_PropertyChanged; + VCAPI.Default.AudioSessionMultiSelector.PropertyChanged += this.AudioSessionSelector_PropertyChanged; // setup autocomplete RefreshSessionAutoCompleteSources(); @@ -139,7 +139,7 @@ public VolumeControlVM() : base() /// public string TargetSessionText { - get => AudioAPI.AudioSessionSelector.Selected?.ProcessIdentifier ?? Settings.TargetSession.ProcessIdentifier; + get => AudioAPI.AudioSessionMultiSelector.CurrentSession?.ProcessIdentifier ?? Settings.TargetSession.ProcessIdentifier; set { value = value.Trim(); @@ -150,7 +150,7 @@ public string TargetSessionText { if (AudioAPI.AudioSessionManager.FindSessionWithSimilarProcessIdentifier(value) is AudioSession session) { // text resolves to a valid AudioSession, select it: - AudioAPI.AudioSessionSelector.Selected = session; + AudioAPI.AudioSessionMultiSelector.CurrentSession = session; } else { // text does not resolve to a valid AudioSession: @@ -162,7 +162,7 @@ public string TargetSessionText } else { - AudioAPI.AudioSessionSelector.Selected = null; + AudioAPI.AudioSessionMultiSelector.CurrentSession = null; } _updatingAudioSessionSelectorFromTargetSessionText = false; @@ -244,7 +244,7 @@ private void AudioSessionSelector_PropertyChanged(object? sender, System.Compone { if (e.PropertyName is null) return; - if (e.PropertyName.Equals(nameof(AudioSessionSelector.Selected))) + if (e.PropertyName.Equals(nameof(AudioSessionMultiSelector.CurrentIndex))) { if (!_updatingAudioSessionSelectorFromTargetSessionText) { @@ -253,7 +253,7 @@ private void AudioSessionSelector_PropertyChanged(object? sender, System.Compone } else if (e.PropertyName.Equals(nameof(AudioSessionSelector.LockSelection))) { - LockTargetSession = VCAPI.Default.AudioSessionSelector.LockSelection; + LockTargetSession = VCAPI.Default.AudioSessionMultiSelector.LockSelection; } } ///