Files
AvaloniaDesktopNotifications/DesktopNotifications.Windows/WindowsNotificationManager.cs
T
2021-02-07 17:28:41 +01:00

138 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications;
namespace DesktopNotifications.Windows
{
public class WindowsNotificationManager : INotificationManager
{
private const int LaunchNotificationWaitMs = 5_000;
private readonly WindowsApplicationContext _applicationContext;
private readonly TaskCompletionSource<string>? _launchActionPromise;
private readonly Dictionary<ToastNotification, Notification> _notifications;
private readonly ToastNotifier _toastNotifier;
/// <summary>
/// </summary>
/// <param name="applicationContext"></param>
public WindowsNotificationManager(WindowsApplicationContext? applicationContext = null)
{
_applicationContext = applicationContext ?? WindowsApplicationContext.FromCurrentProcess();
_launchActionPromise = new TaskCompletionSource<string>();
if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
{
ToastNotificationManagerCompat.OnActivated += OnAppActivated;
if (_launchActionPromise.Task.Wait(LaunchNotificationWaitMs))
{
LaunchActionId = _launchActionPromise.Task.Result;
}
}
_toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier();
_notifications = new Dictionary<ToastNotification, Notification>();
}
public event EventHandler<NotificationActivatedEventArgs>? NotificationActivated;
public event EventHandler<NotificationDismissedEventArgs>? NotificationDismissed;
public string? LaunchActionId { get; }
public ValueTask Initialize()
{
return default;
}
public ValueTask ShowNotification(Notification notification, DateTimeOffset? expirationTime)
{
if (expirationTime < DateTimeOffset.Now)
{
throw new ArgumentException(nameof(expirationTime));
}
var xmlContent = GenerateXml(notification);
var toastNotification = new ToastNotification(xmlContent)
{
ExpirationTime = expirationTime
};
toastNotification.Activated += ToastNotificationOnActivated;
toastNotification.Dismissed += ToastNotificationOnDismissed;
toastNotification.Failed += ToastNotificationOnFailed;
_toastNotifier.Show(toastNotification);
_notifications[toastNotification] = notification;
return default;
}
public void Dispose()
{
ToastNotificationManagerCompat.Uninstall();
}
private static XmlDocument GenerateXml(Notification notification)
{
var builder = new ToastContentBuilder();
builder.AddText(notification.Title);
builder.AddText(notification.Body);
foreach (var (title, actionId) in notification.Buttons)
{
builder.AddButton(title, ToastActivationType.Foreground, actionId);
}
return builder.GetXml();
}
private void OnAppActivated(ToastNotificationActivatedEventArgsCompat e)
{
Debug.Assert(_launchActionPromise != null);
var actionId = GetActionId(e.Argument);
_launchActionPromise.SetResult(actionId);
}
private static void ToastNotificationOnFailed(ToastNotification sender, ToastFailedEventArgs args)
{
throw args.ErrorCode;
}
private void ToastNotificationOnDismissed(ToastNotification sender, ToastDismissedEventArgs args)
{
var notification = _notifications[sender];
var reason = args.Reason switch
{
ToastDismissalReason.UserCanceled => NotificationDismissReason.User,
ToastDismissalReason.TimedOut => NotificationDismissReason.Expired,
ToastDismissalReason.ApplicationHidden => NotificationDismissReason.Application,
_ => throw new ArgumentOutOfRangeException()
};
NotificationDismissed?.Invoke(this, new NotificationDismissedEventArgs(notification, reason));
_notifications.Remove(sender);
}
private static string GetActionId(string argument)
{
return string.IsNullOrEmpty(argument) ? "default" : argument;
}
private void ToastNotificationOnActivated(ToastNotification sender, object args)
{
var activationArgs = (ToastActivatedEventArgs) args;
var notification = _notifications[sender];
var actionId = GetActionId(activationArgs.Arguments);
NotificationActivated?.Invoke(this, new NotificationActivatedEventArgs(notification, actionId));
}
}
}