From a3a05877dadcc876e4703db42f6cd52fc9dbecb3 Mon Sep 17 00:00:00 2001 From: Rafael Rivera Date: Wed, 10 Apr 2024 20:45:30 -0700 Subject: [PATCH] Track packaged apps via AUMID vs install path --- CHANGELOG.md | 3 ++- .../DataModel/Processing/ActionProcessor.cs | 2 +- .../ViewModel/AppListViewModel.cs | 14 ++++++++++++-- EarTrumpet/DataModel/AppInformation/IAppInfo.cs | 1 + .../AppInformation/Internal/DesktopAppInfo.cs | 1 + .../AppInformation/Internal/ModernAppInfo.cs | 2 ++ .../AppInformation/Internal/SystemSoundsAppInfo.cs | 1 + EarTrumpet/DataModel/Audio/IAudioDeviceSession.cs | 1 + .../DataModel/Audio/Mocks/AudioDeviceSession.cs | 5 ++++- .../WindowsAudio/Internal/AudioDeviceSession.cs | 4 +++- .../Internal/AudioDeviceSessionGroup.cs | 2 ++ EarTrumpet/DebugHelpers.cs | 3 ++- EarTrumpet/Interop/PropertyKeys.cs | 6 ++++++ EarTrumpet/UI/ViewModels/AppItemViewModel.cs | 2 ++ EarTrumpet/UI/ViewModels/IAppItemViewModel.cs | 1 + .../UI/ViewModels/SettingsAppItemViewModel.cs | 3 ++- .../UI/ViewModels/TemporaryAppItemViewModel.cs | 1 + 17 files changed, 44 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bd74056..8b8ef534f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog ## x.x.x.x -- Fixed an issue where EarTrumpet tooltips were not updating in some scenarios (thanks @Tester798!) +- Fixed an issue with EarTrumpet tooltips not updating in some scenarios (thanks @Tester798!) +- Fixed an issue with EarTrumpet Actions getting disconnected with updated packaged applications - Added setting to show the full mixer window on startup - Added support for adjusting volumes by 10% in one step from the flyout when the `Ctrl` key is pressed in combination with `Right`/`Left` or `+`/`-` (thanks @ryanspain) diff --git a/EarTrumpet/Addons/EarTrumpet.Actions/DataModel/Processing/ActionProcessor.cs b/EarTrumpet/Addons/EarTrumpet.Actions/DataModel/Processing/ActionProcessor.cs index 95a38a93c..37ec86501 100644 --- a/EarTrumpet/Addons/EarTrumpet.Actions/DataModel/Processing/ActionProcessor.cs +++ b/EarTrumpet/Addons/EarTrumpet.Actions/DataModel/Processing/ActionProcessor.cs @@ -146,7 +146,7 @@ private static IAudioDeviceSession FindForegroundApp(ObservableCollection(); @@ -51,6 +51,16 @@ public void OnInvoked(object sender, IAppItemViewModel vivewModel) public override string ToString() { var existing = All.FirstOrDefault(d => d.Id == _part.App?.Id); + + // Fallback to checking against package full path, for compatibility with older actions + // that predate changes to how we track packaged applications. + // https://github.com/File-New-Project/EarTrumpet/issues/1524 + + if (existing == null) + { + existing = All.FirstOrDefault(d => d.PackageInstallPath == _part.App?.Id); + } + if (existing != null) { return existing.DisplayName; diff --git a/EarTrumpet/DataModel/AppInformation/IAppInfo.cs b/EarTrumpet/DataModel/AppInformation/IAppInfo.cs index 2267c6ed9..883282e7f 100644 --- a/EarTrumpet/DataModel/AppInformation/IAppInfo.cs +++ b/EarTrumpet/DataModel/AppInformation/IAppInfo.cs @@ -5,6 +5,7 @@ namespace EarTrumpet.DataModel.AppInformation public interface IAppInfo { event Action Stopped; + string AppId { get; } string DisplayName { get; } string ExeName { get; } string PackageInstallPath { get; } diff --git a/EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs b/EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs index 712a96443..eff0e8656 100644 --- a/EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs +++ b/EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs @@ -14,6 +14,7 @@ class DesktopAppInfo : IAppInfo public string ExeName { get; } public string DisplayName { get; } public string PackageInstallPath { get; } + public string AppId => PackageInstallPath; public string SmallLogoPath { get; } public bool IsDesktopApp => true; diff --git a/EarTrumpet/DataModel/AppInformation/Internal/ModernAppInfo.cs b/EarTrumpet/DataModel/AppInformation/Internal/ModernAppInfo.cs index 757e23fa3..eff87433f 100644 --- a/EarTrumpet/DataModel/AppInformation/Internal/ModernAppInfo.cs +++ b/EarTrumpet/DataModel/AppInformation/Internal/ModernAppInfo.cs @@ -10,6 +10,7 @@ class ModernAppInfo : IAppInfo { public event Action Stopped; + public string AppId { get; } public string ExeName { get; } public string DisplayName { get; } public string PackageInstallPath { get; } @@ -28,6 +29,7 @@ public ModernAppInfo(int processId, bool trackProcess) { var shellItem = Shell32.SHCreateItemInKnownFolder(FolderIds.AppsFolder, Shell32.KF_FLAG_DONT_VERIFY, appUserModelId, typeof(IShellItem2).GUID); PackageInstallPath = shellItem.GetString(ref PropertyKeys.PKEY_AppUserModel_PackageInstallPath); + AppId = shellItem.GetString(ref PropertyKeys.PKEY_AppUserModel_ID); DisplayName = shellItem.GetString(ref PropertyKeys.PKEY_ItemNameDisplay); ExeName = PackageInstallPath; SmallLogoPath = appUserModelId; diff --git a/EarTrumpet/DataModel/AppInformation/Internal/SystemSoundsAppInfo.cs b/EarTrumpet/DataModel/AppInformation/Internal/SystemSoundsAppInfo.cs index f221e601b..64ea0e446 100644 --- a/EarTrumpet/DataModel/AppInformation/Internal/SystemSoundsAppInfo.cs +++ b/EarTrumpet/DataModel/AppInformation/Internal/SystemSoundsAppInfo.cs @@ -14,6 +14,7 @@ public event Action Stopped { add { } remove { } } public string PackageInstallPath => "System.SystemSoundsSession"; public bool IsDesktopApp => true; public string SmallLogoPath { get; set; } + public string AppId => PackageInstallPath; public SystemSoundsAppInfo() { diff --git a/EarTrumpet/DataModel/Audio/IAudioDeviceSession.cs b/EarTrumpet/DataModel/Audio/IAudioDeviceSession.cs index a5432559e..b6177b7a7 100644 --- a/EarTrumpet/DataModel/Audio/IAudioDeviceSession.cs +++ b/EarTrumpet/DataModel/Audio/IAudioDeviceSession.cs @@ -15,6 +15,7 @@ public interface IAudioDeviceSession : IStreamWithVolumeControl bool IsSystemSoundsSession { get; } int ProcessId { get; } string AppId { get; } + string PackageInstallPath { get; } SessionState State { get; } ObservableCollection Children { get; } } diff --git a/EarTrumpet/DataModel/Audio/Mocks/AudioDeviceSession.cs b/EarTrumpet/DataModel/Audio/Mocks/AudioDeviceSession.cs index b58f03acb..f560d63a4 100644 --- a/EarTrumpet/DataModel/Audio/Mocks/AudioDeviceSession.cs +++ b/EarTrumpet/DataModel/Audio/Mocks/AudioDeviceSession.cs @@ -77,7 +77,9 @@ public float Volume public Guid GroupingParam => Guid.Empty; - public AudioDeviceSession(IAudioDevice parent, string id, string displayName, string appId, string iconPath) + public string PackageInstallPath { get; } + + public AudioDeviceSession(IAudioDevice parent, string id, string displayName, string appId, string iconPath, string packageInstallPath) { DisplayName = displayName; Id = id; @@ -85,6 +87,7 @@ public AudioDeviceSession(IAudioDevice parent, string id, string displayName, st IconPath = iconPath; Parent = parent; IsDesktopApp = true; + PackageInstallPath = packageInstallPath; } public void Hide() diff --git a/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSession.cs b/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSession.cs index 69d098d10..283d2e7c5 100644 --- a/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSession.cs +++ b/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSession.cs @@ -88,7 +88,7 @@ public bool IsMuted public float PeakValue1 { get; private set; } public float PeakValue2 { get; private set; } public bool IsDesktopApp => _appInfo.IsDesktopApp; - public string AppId => _appInfo.PackageInstallPath; + public string AppId => _appInfo.AppId; public SessionState State { @@ -125,6 +125,7 @@ public SessionState State public ObservableCollection Children { get; private set; } public IEnumerable Channels => _channels.Channels; + public string PackageInstallPath { get; private set; } private readonly string _id; private readonly IAudioSessionControl _session; @@ -159,6 +160,7 @@ public AudioDeviceSession(IAudioDevice parent, IAudioSessionControl session, Dis _appInfo = AppInformationFactory.CreateForProcess(ProcessId, trackProcess: true); _appInfo.Stopped += _ => DisconnectSession(); + PackageInstallPath = _appInfo.PackageInstallPath; // NOTE: Ensure that the callbacks won't touch state that isn't initialized yet (i.e. _appInfo must be valid before the first callback) _session.RegisterAudioSessionNotification(this); diff --git a/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSessionGroup.cs b/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSessionGroup.cs index 1ea533415..70458866a 100644 --- a/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSessionGroup.cs +++ b/EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSessionGroup.cs @@ -109,6 +109,8 @@ public float Volume public ObservableCollection Children => _sessions; + public string PackageInstallPath => _sessions.FirstOrDefault()?.PackageInstallPath; + public void Hide() { foreach (var session in _sessions.ToArray()) diff --git a/EarTrumpet/DebugHelpers.cs b/EarTrumpet/DebugHelpers.cs index fb1711e0c..b1aeb289d 100644 --- a/EarTrumpet/DebugHelpers.cs +++ b/EarTrumpet/DebugHelpers.cs @@ -93,7 +93,8 @@ private static DataModel.Audio.Mocks.AudioDeviceSession MakeMockApp(IAudioDevice Guid.NewGuid().ToString(), displayName, appId, - Environment.ExpandEnvironmentVariables(iconPath)); + Environment.ExpandEnvironmentVariables(iconPath), + appId); } private static void DebugAddMockDevice() diff --git a/EarTrumpet/Interop/PropertyKeys.cs b/EarTrumpet/Interop/PropertyKeys.cs index fa2e122ec..8cb52afa3 100644 --- a/EarTrumpet/Interop/PropertyKeys.cs +++ b/EarTrumpet/Interop/PropertyKeys.cs @@ -4,6 +4,12 @@ namespace EarTrumpet.Interop { public static class PropertyKeys { + public static PROPERTYKEY PKEY_AppUserModel_ID = new PROPERTYKEY + { + fmtid = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), + pid = new UIntPtr(5) + }; + public static PROPERTYKEY PKEY_ItemNameDisplay = new PROPERTYKEY { fmtid = new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), diff --git a/EarTrumpet/UI/ViewModels/AppItemViewModel.cs b/EarTrumpet/UI/ViewModels/AppItemViewModel.cs index 7374e2402..d7ba1857f 100644 --- a/EarTrumpet/UI/ViewModels/AppItemViewModel.cs +++ b/EarTrumpet/UI/ViewModels/AppItemViewModel.cs @@ -53,6 +53,8 @@ public IDeviceViewModel Parent } } + public string PackageInstallPath => _session.PackageInstallPath; + private readonly IAudioDeviceSession _session; private readonly WeakReference _parent; diff --git a/EarTrumpet/UI/ViewModels/IAppItemViewModel.cs b/EarTrumpet/UI/ViewModels/IAppItemViewModel.cs index 82d3aaab6..211bab511 100644 --- a/EarTrumpet/UI/ViewModels/IAppItemViewModel.cs +++ b/EarTrumpet/UI/ViewModels/IAppItemViewModel.cs @@ -15,6 +15,7 @@ public interface IAppItemViewModel : IAppIconSource, INotifyPropertyChanged string DisplayName { get; } string ExeName { get; } string AppId { get; } + string PackageInstallPath { get; } char IconText { get; } bool IsExpanded { get; } bool IsMovable { get; } diff --git a/EarTrumpet/UI/ViewModels/SettingsAppItemViewModel.cs b/EarTrumpet/UI/ViewModels/SettingsAppItemViewModel.cs index 367267ff7..882339b16 100644 --- a/EarTrumpet/UI/ViewModels/SettingsAppItemViewModel.cs +++ b/EarTrumpet/UI/ViewModels/SettingsAppItemViewModel.cs @@ -55,12 +55,13 @@ public int Volume public string PersistedOutputDevice => throw new NotImplementedException(); public int ProcessId => throw new NotImplementedException(); public IDeviceViewModel Parent => throw new NotImplementedException(); - public ICommand Remove { get; set; } + public string PackageInstallPath { get; set; } public SettingsAppItemViewModel(IAudioDeviceSession session) { AppId = session.AppId; + PackageInstallPath = session.PackageInstallPath; DisplayName = session.DisplayName; IsDesktopApp = session.IsDesktopApp; IconPath = session.IconPath; diff --git a/EarTrumpet/UI/ViewModels/TemporaryAppItemViewModel.cs b/EarTrumpet/UI/ViewModels/TemporaryAppItemViewModel.cs index 516d60587..dc9df64ba 100644 --- a/EarTrumpet/UI/ViewModels/TemporaryAppItemViewModel.cs +++ b/EarTrumpet/UI/ViewModels/TemporaryAppItemViewModel.cs @@ -65,6 +65,7 @@ public int Volume public string PersistedOutputDevice => ((IAudioDeviceManagerWindowsAudio)_deviceManager).GetDefaultEndPoint(ProcessId); public int ProcessId { get; } public IDeviceViewModel Parent { get; } + public string PackageInstallPath { get; } private readonly IAudioDeviceManager _deviceManager; private readonly WeakReference _parent;