Merge branch 'master' of https://github.com/pr8x/DesktopNotifications
This commit is contained in:
@@ -14,10 +14,12 @@ namespace DesktopNotifications.Apple
|
||||
|
||||
public ValueTask Initialize()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public ValueTask ShowNotification(Notification notification, DateTimeOffset? expirationTime = null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace DesktopNotifications.FreeDesktop
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class FreeDesktopApplicationContext : ApplicationContext
|
||||
{
|
||||
private FreeDesktopApplicationContext(string name, string? appIcon) : base(name)
|
||||
{
|
||||
AppIcon = appIcon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public string? AppIcon { get; }
|
||||
|
||||
public static FreeDesktopApplicationContext FromCurrentProcess(string? appIcon = null)
|
||||
{
|
||||
var mainModule = Process.GetCurrentProcess().MainModule;
|
||||
return new FreeDesktopApplicationContext(
|
||||
Path.GetFileNameWithoutExtension(mainModule.FileName),
|
||||
appIcon
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,18 +9,24 @@ namespace DesktopNotifications.FreeDesktop
|
||||
{
|
||||
public class FreeDesktopNotificationManager : INotificationManager, IDisposable
|
||||
{
|
||||
private readonly FreeDesktopApplicationContext _appContext;
|
||||
private const string NotificationsService = "org.freedesktop.Notifications";
|
||||
|
||||
private static readonly ObjectPath NotificationsPath = new ObjectPath("/org/freedesktop/Notifications");
|
||||
private readonly Dictionary<uint, Notification> _activeNotifications;
|
||||
private Connection? _connection;
|
||||
private IDisposable? _notificationActionSubscription;
|
||||
private IDisposable? _notificationCloseSubscription;
|
||||
private Dictionary<uint, Notification> _activeNotifications;
|
||||
|
||||
private IFreeDesktopNotificationsProxy? _proxy;
|
||||
|
||||
public FreeDesktopNotificationManager()
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="appContext"></param>
|
||||
public FreeDesktopNotificationManager(FreeDesktopApplicationContext? appContext = null)
|
||||
{
|
||||
_appContext = appContext ?? FreeDesktopApplicationContext.FromCurrentProcess();
|
||||
_activeNotifications = new Dictionary<uint, Notification>();
|
||||
}
|
||||
|
||||
@@ -65,11 +71,11 @@ namespace DesktopNotifications.FreeDesktop
|
||||
var actions = GenerateActions(notification);
|
||||
|
||||
var id = await _proxy.NotifyAsync(
|
||||
"MyApp",
|
||||
_appContext.Name,
|
||||
0,
|
||||
string.Empty,
|
||||
notification.Title,
|
||||
notification.Body,
|
||||
_appContext.AppIcon ?? string.Empty,
|
||||
notification.Title ?? throw new ArgumentException(),
|
||||
notification.Body ?? throw new ArgumentException(),
|
||||
actions.ToArray(),
|
||||
new Dictionary<string, object> {{"urgency", 1}},
|
||||
duration?.Milliseconds ?? 0
|
||||
@@ -110,7 +116,7 @@ namespace DesktopNotifications.FreeDesktop
|
||||
|
||||
var dismissReason = GetReason(@event.reason);
|
||||
|
||||
NotificationDismissed?.Invoke(this,
|
||||
NotificationDismissed?.Invoke(this,
|
||||
new NotificationDismissedEventArgs(notification, dismissReason));
|
||||
}
|
||||
|
||||
@@ -123,7 +129,7 @@ namespace DesktopNotifications.FreeDesktop
|
||||
{
|
||||
var notification = _activeNotifications[@event.id];
|
||||
|
||||
NotificationActivated?.Invoke(this,
|
||||
NotificationActivated?.Invoke(this,
|
||||
new NotificationActivatedEventArgs(notification, @event.actionKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Text;
|
||||
|
||||
namespace Example.Win32
|
||||
namespace DesktopNotifications.Windows
|
||||
{
|
||||
// Modified from http://smdn.jp/programming/tips/createlnk/
|
||||
// Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DesktopNotifications.Windows
|
||||
{
|
||||
public class WindowsApplicationContext : ApplicationContext
|
||||
{
|
||||
private WindowsApplicationContext(string name, string appUserModelId) : base(name)
|
||||
{
|
||||
AppUserModelId = appUserModelId;
|
||||
}
|
||||
|
||||
public string AppUserModelId { get; }
|
||||
|
||||
[DllImport("shell32.dll", SetLastError = true)]
|
||||
private static extern void SetCurrentProcessExplicitAppUserModelID(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appId);
|
||||
|
||||
public static WindowsApplicationContext FromCurrentProcess(string? customName = null,
|
||||
string? appUserModelId = null)
|
||||
{
|
||||
var aumid = appUserModelId ?? Guid.NewGuid().ToString();
|
||||
|
||||
SetCurrentProcessExplicitAppUserModelID(aumid);
|
||||
|
||||
var mainModule = Process.GetCurrentProcess().MainModule;
|
||||
|
||||
using var shortcut = new ShellLink
|
||||
{
|
||||
TargetPath = mainModule.FileName,
|
||||
Arguments = string.Empty,
|
||||
AppUserModelID = aumid
|
||||
};
|
||||
|
||||
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var startMenuPath = Path.Combine(appData, @"Microsoft\Windows\Start Menu\Programs");
|
||||
var appName = customName ?? Path.GetFileNameWithoutExtension(mainModule.FileName);
|
||||
var shortcutFile = Path.Combine(startMenuPath, $"{appName}.lnk");
|
||||
|
||||
shortcut.Save(shortcutFile);
|
||||
|
||||
return new WindowsApplicationContext(appName, aumid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,20 +10,33 @@ namespace DesktopNotifications.Windows
|
||||
{
|
||||
public class WindowsNotificationManager : INotificationManager
|
||||
{
|
||||
private readonly WindowsApplicationContext _applicationContext;
|
||||
private readonly Dictionary<ToastNotification, Notification> _notifications;
|
||||
private readonly ToastNotifier _toastNotifier;
|
||||
private string? _launchAction;
|
||||
private TaskCompletionSource<string?>? _launchActionPromise;
|
||||
private EventHandler<NotificationActivatedEventArgs>? _notificationActivatedHandler;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="appId"></param>
|
||||
public WindowsNotificationManager(string appId)
|
||||
/// <param name="applicationContext"></param>
|
||||
public WindowsNotificationManager(WindowsApplicationContext? applicationContext = null)
|
||||
{
|
||||
_toastNotifier = ToastNotificationManager.CreateToastNotifier(appId);
|
||||
_applicationContext = applicationContext ?? WindowsApplicationContext.FromCurrentProcess();
|
||||
_toastNotifier = ToastNotificationManager.CreateToastNotifier(_applicationContext.AppUserModelId);
|
||||
_notifications = new Dictionary<ToastNotification, Notification>();
|
||||
}
|
||||
|
||||
public event EventHandler<NotificationActivatedEventArgs>? NotificationActivated;
|
||||
public event EventHandler<NotificationActivatedEventArgs>? NotificationActivated
|
||||
{
|
||||
add
|
||||
{
|
||||
_notificationActivatedHandler += value;
|
||||
ProcessLaunchAction();
|
||||
}
|
||||
remove => _notificationActivatedHandler -= value;
|
||||
}
|
||||
|
||||
public event EventHandler<NotificationDismissedEventArgs>? NotificationDismissed;
|
||||
|
||||
public async ValueTask Initialize()
|
||||
@@ -33,31 +46,14 @@ namespace DesktopNotifications.Windows
|
||||
_launchActionPromise = new TaskCompletionSource<string?>();
|
||||
ToastNotificationManagerCompat.OnActivated += OnAppActivated;
|
||||
|
||||
var launchAction = await _launchActionPromise.Task;
|
||||
_launchAction = await _launchActionPromise.Task;
|
||||
|
||||
Debug.Assert(launchAction != null);
|
||||
Debug.Assert(_launchAction != null);
|
||||
|
||||
//TODO: Lookup notification object from history?
|
||||
NotificationActivated?.Invoke(this,
|
||||
new NotificationActivatedEventArgs(null, launchAction));
|
||||
ProcessLaunchAction();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public ValueTask ShowNotification(Notification notification, DateTimeOffset? expirationTime)
|
||||
{
|
||||
if (expirationTime < DateTimeOffset.Now)
|
||||
@@ -81,6 +77,39 @@ namespace DesktopNotifications.Windows
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private void ProcessLaunchAction()
|
||||
{
|
||||
if (_launchAction == null || _notificationActivatedHandler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: Lookup notification object from history?
|
||||
_notificationActivatedHandler.Invoke(this,
|
||||
new NotificationActivatedEventArgs(null, _launchAction));
|
||||
|
||||
_launchAction = null;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -114,11 +143,7 @@ namespace DesktopNotifications.Windows
|
||||
var notification = _notifications[sender];
|
||||
var actionId = string.IsNullOrEmpty(activationArgs.Arguments) ? "default" : activationArgs.Arguments;
|
||||
|
||||
NotificationActivated?.Invoke(this, new NotificationActivatedEventArgs(notification, actionId));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_notificationActivatedHandler?.Invoke(this, new NotificationActivatedEventArgs(notification, actionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DesktopNotifications
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ApplicationContext
|
||||
{
|
||||
public ApplicationContext(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ namespace DesktopNotifications
|
||||
Buttons = new List<(string Title, string ActionId)>();
|
||||
}
|
||||
|
||||
public string Title { get; set; }
|
||||
public string? Title { get; set; }
|
||||
|
||||
public string Body { get; set; }
|
||||
public string? Body { get; set; }
|
||||
|
||||
public List<(string Title, string ActionId)> Buttons { get; }
|
||||
}
|
||||
|
||||
+2
-23
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using DesktopNotifications;
|
||||
using DesktopNotifications.FreeDesktop;
|
||||
using DesktopNotifications.Windows;
|
||||
using Example.Win32;
|
||||
|
||||
namespace Example
|
||||
{
|
||||
@@ -25,24 +22,7 @@ namespace Example
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
const string AppName = "DesktopNotificationsExample";
|
||||
|
||||
SetCurrentProcessExplicitAppUserModelID(AppName);
|
||||
|
||||
using var shortcut = new ShellLink
|
||||
{
|
||||
TargetPath = Process.GetCurrentProcess().MainModule.FileName,
|
||||
Arguments = string.Empty,
|
||||
AppUserModelID = AppName
|
||||
};
|
||||
|
||||
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var startMenuPath = Path.Combine(appData, @"Microsoft\Windows\Start Menu\Programs");
|
||||
var shortcutFile = Path.Combine(startMenuPath, $"{AppName}.lnk");
|
||||
|
||||
shortcut.Save(shortcutFile);
|
||||
|
||||
return new WindowsNotificationManager(AppName);
|
||||
return new WindowsNotificationManager();
|
||||
}
|
||||
|
||||
throw new PlatformNotSupportedException();
|
||||
@@ -51,12 +31,11 @@ namespace Example
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
using var manager = CreateManager();
|
||||
await manager.Initialize();
|
||||
|
||||
manager.NotificationActivated += ManagerOnNotificationActivated;
|
||||
manager.NotificationDismissed += ManagerOnNotificationDismissed;
|
||||
|
||||
await manager.Initialize();
|
||||
|
||||
var notification = new Notification
|
||||
{
|
||||
Title = "Hello World!",
|
||||
|
||||
Reference in New Issue
Block a user