Files
AvaloniaDesktopNotifications/DesktopNotifications.Windows/WindowsNotificationManager.cs
T
Luis v.d.Eltz 68d4c265a5 Target .Netstandard 2.0 (#8)
* Fix build on non-windows platform

* Update to netstandard 2.0
Fix build on OSX

* Fix netstandard UWP API usage

* Update README.md

* Fix build

* Fix build

* Fix build on *Nix

* Remove Win stuff on nonwindows

* fix compilation

* Fixes

* Fix
2022-03-06 19:13:33 +01:00

235 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.UI.Notifications;
using XmlDocument = Windows.Data.Xml.Dom.XmlDocument;
#if NETSTANDARD
using System.IO;
using System.Xml;
#else
using System.Diagnostics;
using Microsoft.Toolkit.Uwp.Notifications;
#endif
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;
#if NETSTANDARD
private readonly ToastNotifier _toastNotifier;
#else
private readonly ToastNotifierCompat _toastNotifier;
#endif
/// <summary>
/// </summary>
/// <param name="applicationContext"></param>
public WindowsNotificationManager(WindowsApplicationContext? applicationContext = null)
{
_applicationContext = applicationContext ?? WindowsApplicationContext.FromCurrentProcess();
_launchActionPromise = new TaskCompletionSource<string>();
#if !NETSTANDARD
if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
{
ToastNotificationManagerCompat.OnActivated += OnAppActivated;
if (_launchActionPromise.Task.Wait(LaunchNotificationWaitMs))
{
LaunchActionId = _launchActionPromise.Task.Result;
}
}
#endif
#if NETSTANDARD
_toastNotifier = ToastNotificationManager.CreateToastNotifier(_applicationContext.AppUserModelId);
#else
_toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier();
#endif
_notifications = new Dictionary<ToastNotification, Notification>();
}
public event EventHandler<NotificationActivatedEventArgs>? NotificationActivated;
public event EventHandler<NotificationDismissedEventArgs>? NotificationDismissed;
public string? LaunchActionId { get; }
public Task Initialize()
{
return Task.CompletedTask;
}
public Task 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 Task.CompletedTask;
}
public Task ScheduleNotification(
Notification notification,
DateTimeOffset deliveryTime,
DateTimeOffset? expirationTime = null)
{
if (deliveryTime < DateTimeOffset.Now || deliveryTime > expirationTime)
{
throw new ArgumentException(nameof(deliveryTime));
}
var xmlContent = GenerateXml(notification);
var toastNotification = new ScheduledToastNotification(xmlContent, deliveryTime)
{
ExpirationTime = expirationTime
};
_toastNotifier.AddToSchedule(toastNotification);
return Task.CompletedTask;
}
public void Dispose()
{
}
private static XmlDocument GenerateXml(Notification notification)
{
#if NETSTANDARD
var sw = new StringWriter();
var xw = XmlWriter.Create(sw, new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
});
xw.WriteStartElement("toast");
xw.WriteStartElement("visual");
xw.WriteStartElement("binding");
xw.WriteAttributeString("template", "ToastGeneric");
xw.WriteStartElement("text");
xw.WriteString(notification.Title ?? string.Empty);
xw.WriteEndElement();
xw.WriteStartElement("text");
xw.WriteString(notification.Body ?? string.Empty);
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteStartElement("actions");
foreach (var (title, actionId) in notification.Buttons)
{
xw.WriteStartElement("action");
xw.WriteAttributeString("content", title);
xw.WriteAttributeString("activationType", "foreground");
xw.WriteAttributeString("arguments", actionId);
xw.WriteEndElement();
}
xw.WriteEndElement();
xw.WriteEndElement();
xw.Flush();
var xmlStr = sw.ToString();
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlStr);
return xmlDoc;
#else
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();
#endif
}
#if !NETSTANDARD
private void OnAppActivated(ToastNotificationActivatedEventArgsCompat e)
{
Debug.Assert(_launchActionPromise != null);
var actionId = GetActionId(e.Argument);
_launchActionPromise.SetResult(actionId);
}
#endif
private static void ToastNotificationOnFailed(ToastNotification sender, ToastFailedEventArgs args)
{
throw args.ErrorCode;
}
private void ToastNotificationOnDismissed(ToastNotification sender, ToastDismissedEventArgs args)
{
if (!_notifications.TryGetValue(sender, out var notification))
{
return;
}
_notifications.Remove(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));
}
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));
}
}
}