From c9440dfe080eba4288fbf791cd82575aa7ec5fcd Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 23 Jun 2023 01:11:50 +0200 Subject: [PATCH] HideNotification (#18) * HideNotification support * Fix warning --- .../AppleNotificationManager.cs | 5 ++ .../FreeDesktopNotificationManager.cs | 34 +++++-- .../NullImpl_WindowsNotificationManager.cs | 5 ++ .../WindowsNotificationManager.cs | 31 ++++++- DesktopNotifications/Extensions.cs | 23 +++++ DesktopNotifications/INotificationManager.cs | 9 +- Example.Avalonia/MainWindow.axaml | 1 + Example.Avalonia/MainWindow.axaml.cs | 88 ++++++++++++++----- 8 files changed, 165 insertions(+), 31 deletions(-) create mode 100644 DesktopNotifications/Extensions.cs diff --git a/DesktopNotifications.Apple/AppleNotificationManager.cs b/DesktopNotifications.Apple/AppleNotificationManager.cs index c3d657d..203b03d 100644 --- a/DesktopNotifications.Apple/AppleNotificationManager.cs +++ b/DesktopNotifications.Apple/AppleNotificationManager.cs @@ -37,5 +37,10 @@ public Task ScheduleNotification(Notification notification, DateTimeOffset deliv { return Task.CompletedTask; } + + public Task HideNotification(Notification notification) + { + return Task.CompletedTask; + } } } diff --git a/DesktopNotifications.FreeDesktop/FreeDesktopNotificationManager.cs b/DesktopNotifications.FreeDesktop/FreeDesktopNotificationManager.cs index c95b88c..40e77d1 100644 --- a/DesktopNotifications.FreeDesktop/FreeDesktopNotificationManager.cs +++ b/DesktopNotifications.FreeDesktop/FreeDesktopNotificationManager.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Tmds.DBus; +using static DesktopNotifications.Extensions; namespace DesktopNotifications.FreeDesktop { @@ -63,10 +64,7 @@ public async Task Initialize() public async Task ShowNotification(Notification notification, DateTimeOffset? expirationTime = null) { - if (_connection == null || _proxy == null) - { - throw new InvalidOperationException("Not connected. Call Initialize() first."); - } + CheckConnection(); if (expirationTime < DateTimeOffset.Now) { @@ -76,25 +74,45 @@ public async Task ShowNotification(Notification notification, DateTimeOffset? ex var duration = expirationTime - DateTimeOffset.Now; var actions = GenerateActions(notification); - var id = await _proxy.NotifyAsync( + var id = await _proxy!.NotifyAsync( _appContext.Name, 0, _appContext.AppIcon ?? string.Empty, notification.Title ?? throw new ArgumentException(), notification.Body ?? throw new ArgumentException(), actions.ToArray(), - new Dictionary {{"urgency", 1}}, + new Dictionary { { "urgency", 1 } }, duration?.Milliseconds ?? 0 ).ConfigureAwait(false); _activeNotifications[id] = notification; } + private void CheckConnection() + { + if (_connection == null || _proxy == null) + { + throw new InvalidOperationException("Not connected. Call Initialize() first."); + } + } + + public async Task HideNotification(Notification notification) + { + CheckConnection(); + + if (_activeNotifications.TryGetKey(notification, out var id)) + { + await _proxy!.CloseNotificationAsync(id); + } + } + public async Task ScheduleNotification( Notification notification, DateTimeOffset deliveryTime, DateTimeOffset? expirationTime = null) { + CheckConnection(); + if (deliveryTime < DateTimeOffset.Now || deliveryTime > expirationTime) { throw new ArgumentException(nameof(deliveryTime)); @@ -135,7 +153,7 @@ private static NotificationDismissReason GetReason(uint reason) private void OnNotificationClosed((uint id, uint reason) @event) { if (!_activeNotifications.TryGetValue(@event.id, out var notification)) return; - + _activeNotifications.Remove(@event.id); //TODO: Not sure why but it calls this event twice sometimes @@ -144,7 +162,7 @@ private void OnNotificationClosed((uint id, uint reason) @event) { return; } - + var dismissReason = GetReason(@event.reason); NotificationDismissed?.Invoke(this, diff --git a/DesktopNotifications.Windows/NullImpl_WindowsNotificationManager.cs b/DesktopNotifications.Windows/NullImpl_WindowsNotificationManager.cs index 6f7f4d1..1c762b1 100644 --- a/DesktopNotifications.Windows/NullImpl_WindowsNotificationManager.cs +++ b/DesktopNotifications.Windows/NullImpl_WindowsNotificationManager.cs @@ -48,6 +48,11 @@ public Task ShowNotification(Notification notification, DateTimeOffset? expirati throw new PlatformNotSupportedException(); } + public Task HideNotification(Notification notification) + { + throw new PlatformNotSupportedException(); + } + public Task ScheduleNotification(Notification notification, DateTimeOffset deliveryTime, DateTimeOffset? expirationTime = null) { diff --git a/DesktopNotifications.Windows/WindowsNotificationManager.cs b/DesktopNotifications.Windows/WindowsNotificationManager.cs index 2905330..b43c90d 100644 --- a/DesktopNotifications.Windows/WindowsNotificationManager.cs +++ b/DesktopNotifications.Windows/WindowsNotificationManager.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Windows.UI.Notifications; using XmlDocument = Windows.Data.Xml.Dom.XmlDocument; +using System.Linq; #if NETSTANDARD using System.IO; @@ -20,6 +21,7 @@ public class WindowsNotificationManager : INotificationManager private readonly WindowsApplicationContext _applicationContext; private readonly TaskCompletionSource? _launchActionPromise; private readonly Dictionary _notifications; + private readonly Dictionary _scheduledNotification; #if NETSTANDARD private readonly ToastNotifier _toastNotifier; @@ -54,6 +56,7 @@ public WindowsNotificationManager(WindowsApplicationContext? applicationContext #endif _notifications = new Dictionary(); + _scheduledNotification = new Dictionary(); } public event EventHandler? NotificationActivated; @@ -90,6 +93,21 @@ public Task ShowNotification(Notification notification, DateTimeOffset? expirati return Task.CompletedTask; } + public Task HideNotification(Notification notification) + { + if (_notifications.TryGetKey(notification, out var toastNotification)) + { + _toastNotifier.Hide(toastNotification); + } + + if (_scheduledNotification.TryGetKey(notification, out var scheduledToastNotification)) + { + _toastNotifier.RemoveFromSchedule(scheduledToastNotification); + } + + return Task.CompletedTask; + } + public Task ScheduleNotification( Notification notification, DateTimeOffset deliveryTime, @@ -107,12 +125,15 @@ public Task ScheduleNotification( }; _toastNotifier.AddToSchedule(toastNotification); + _scheduledNotification[toastNotification] = notification; return Task.CompletedTask; } public void Dispose() { + _notifications.Clear(); + _scheduledNotification.Clear(); } private static XmlDocument GenerateXml(Notification notification) @@ -225,11 +246,17 @@ private static string GetActionId(string argument) private void ToastNotificationOnActivated(ToastNotification sender, object args) { + if (!_notifications.TryGetValue(sender, out var notification)) + { + return; + } + var activationArgs = (ToastActivatedEventArgs)args; - var notification = _notifications[sender]; var actionId = GetActionId(activationArgs.Arguments); - NotificationActivated?.Invoke(this, new NotificationActivatedEventArgs(notification, actionId)); + NotificationActivated?.Invoke( + this, + new NotificationActivatedEventArgs(notification, actionId)); } } } \ No newline at end of file diff --git a/DesktopNotifications/Extensions.cs b/DesktopNotifications/Extensions.cs new file mode 100644 index 0000000..d6a3ee4 --- /dev/null +++ b/DesktopNotifications/Extensions.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace DesktopNotifications +{ + public static class Extensions + { + public static bool TryGetKey(this IDictionary dict, V value, out K key) + { + foreach (var entry in dict) + { + if (entry.Value?.Equals(value) == true) + { + key = entry.Key; + return true; + } + } + + key = default!; + + return false; + } + } +} \ No newline at end of file diff --git a/DesktopNotifications/INotificationManager.cs b/DesktopNotifications/INotificationManager.cs index b541e58..287bfcc 100644 --- a/DesktopNotifications/INotificationManager.cs +++ b/DesktopNotifications/INotificationManager.cs @@ -35,12 +35,19 @@ public interface INotificationManager : IDisposable Task Initialize(); /// - /// Schedules a notification for presentation. + /// Schedules a notification for delivery. /// /// The notification to present. /// The expiration time marking the point when the notification gets removed. Task ShowNotification(Notification notification, DateTimeOffset? expirationTime = null); + /// + /// Hides an already delivered notification (if possible). + /// If the notification is scheduled for delivery the schedule will be cancelled. + /// + /// The notification to hide + Task HideNotification(Notification notification); + /// /// /// diff --git a/Example.Avalonia/MainWindow.axaml b/Example.Avalonia/MainWindow.axaml index 9bded4d..d3c7c87 100644 --- a/Example.Avalonia/MainWindow.axaml +++ b/Example.Avalonia/MainWindow.axaml @@ -13,6 +13,7 @@