mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
[WIP] Basic Notification Compat intergration
This commit is contained in:
parent
33689931ca
commit
3a2b74b049
File diff suppressed because it is too large
Load Diff
87
DisplayMagician/DesktopNotificationActivator.cs
Normal file
87
DisplayMagician/DesktopNotificationActivator.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using DesktopNotifications;
|
||||
using static DesktopNotifications.NotificationActivator;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DisplayMagician
|
||||
{
|
||||
// The GUID must be unique to your app. Create a new GUID if copying this code.
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(INotificationActivationCallback))]
|
||||
[Guid("56F14154-6339-4B94-8B82-80F78D5BCEAF"), ComVisible(true)]
|
||||
public class DesktopNotificationActivator : NotificationActivator
|
||||
{
|
||||
public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId)
|
||||
{
|
||||
/*Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
// Tapping on the top-level header launches with empty args
|
||||
if (arguments.Length == 0)
|
||||
{
|
||||
// Perform a normal launch
|
||||
OpenWindowIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the query string (using NuGet package QueryString.NET)
|
||||
QueryString args = QueryString.Parse(invokedArgs);
|
||||
|
||||
// See what action is being requested
|
||||
switch (args["action"])
|
||||
{
|
||||
// Open the image
|
||||
case "viewImage":
|
||||
|
||||
// The URL retrieved from the toast args
|
||||
string imageUrl = args["imageUrl"];
|
||||
|
||||
// Make sure we have a window open and in foreground
|
||||
OpenWindowIfNeeded();
|
||||
|
||||
// And then show the image
|
||||
(App.Current.Windows[0] as MainWindow).ShowImage(imageUrl);
|
||||
|
||||
break;
|
||||
|
||||
// Background: Quick reply to the conversation
|
||||
case "reply":
|
||||
|
||||
// Get the response the user typed
|
||||
string msg = userInput["tbReply"];
|
||||
|
||||
// And send this message
|
||||
SendMessage(msg);
|
||||
|
||||
// If there's no windows open, exit the app
|
||||
if (App.Current.Windows.Count == 0)
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
private void OpenWindowIfNeeded()
|
||||
{
|
||||
/*// Make sure we have a window open (in case user clicked toast while app closed)
|
||||
if (App.Current.Windows.Count == 0)
|
||||
{
|
||||
new MainWindow().Show();
|
||||
}
|
||||
|
||||
// Activate the window, bringing it to focus
|
||||
App.Current.Windows[0].Activate();
|
||||
|
||||
// And make sure to maximize the window too, in case it was currently minimized
|
||||
App.Current.Windows[0].WindowState = WindowState.Normal;*/
|
||||
}
|
||||
}
|
||||
}
|
506
DisplayMagician/DesktopNotificationManagerCompat.cs
Normal file
506
DisplayMagician/DesktopNotificationManagerCompat.cs
Normal file
@ -0,0 +1,506 @@
|
||||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
/*
|
||||
* License for the RegisterActivator portion of code from FrecherxDachs
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Michael Dietrich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
* */
|
||||
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using Windows.UI.Notifications;
|
||||
using static DesktopNotifications.NotificationActivator;
|
||||
|
||||
namespace DesktopNotifications
|
||||
{
|
||||
public class DesktopNotificationManagerCompat
|
||||
{
|
||||
public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated";
|
||||
|
||||
private static bool _registeredAumidAndComServer;
|
||||
private static string _aumid;
|
||||
private static bool _registeredActivator;
|
||||
|
||||
/// <summary>
|
||||
/// If you're not using MSIX or sparse packages, you must call this method to register your AUMID with the Compat library and to
|
||||
/// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running
|
||||
/// under Desktop Bridge. Call this upon application startup, before calling any other APIs.
|
||||
/// </summary>
|
||||
/// <param name="aumid">An AUMID that uniquely identifies your application.</param>
|
||||
public static void RegisterAumidAndComServer<T>(string aumid)
|
||||
where T : NotificationActivator
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(aumid))
|
||||
{
|
||||
throw new ArgumentException("You must provide an AUMID.", nameof(aumid));
|
||||
}
|
||||
|
||||
// If running as Desktop Bridge
|
||||
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||
{
|
||||
// Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
|
||||
// Desktop Bridge apps are registered with platform through their manifest.
|
||||
// Their LocalServer32 key is also registered through their manifest.
|
||||
_aumid = null;
|
||||
_registeredAumidAndComServer = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_aumid = aumid;
|
||||
|
||||
String exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
RegisterComServer<T>(exePath);
|
||||
|
||||
_registeredAumidAndComServer = true;
|
||||
}
|
||||
|
||||
private static void RegisterComServer<T>(String exePath)
|
||||
where T : NotificationActivator
|
||||
{
|
||||
// We register the EXE to start up when the notification is activated
|
||||
string regString = String.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}", typeof(T).GUID);
|
||||
using (var key = Registry.CurrentUser.CreateSubKey(regString))
|
||||
{
|
||||
// Include a flag so we know this was a toast activation and should wait for COM to process
|
||||
// We also wrap EXE path in quotes for extra security
|
||||
key.SetValue("LocalServer32", '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||
}
|
||||
|
||||
if (IsElevated)
|
||||
{
|
||||
// For elevated apps, we need to ensure they'll activate in existing running process by adding
|
||||
// some values in local machine
|
||||
using (var key = Registry.LocalMachine.CreateSubKey(regString))
|
||||
{
|
||||
// Same as above, except also including AppId to link to our AppId entry below
|
||||
key.SetValue("LocalServer32", '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||
key.SetValue("AppId", "{" + typeof(T).GUID + "}");
|
||||
}
|
||||
|
||||
// This tells COM to match any client, so Action Center will activate our elevated process.
|
||||
// More info: https://docs.microsoft.com/windows/win32/com/runas
|
||||
using (var key = Registry.LocalMachine.CreateSubKey(String.Format("SOFTWARE\\Classes\\AppID\\{{{0}}}", typeof(T).GUID)))
|
||||
{
|
||||
key.SetValue("RunAs", "Interactive User");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the activator type as a COM server client so that Windows can launch your activator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Your implementation of NotificationActivator. Must have GUID and ComVisible attributes on class.</typeparam>
|
||||
public static void RegisterActivator<T>()
|
||||
where T : NotificationActivator, new()
|
||||
{
|
||||
// Big thanks to FrecherxDachs for figuring out the following code which works in .NET Core 3: https://github.com/FrecherxDachs/UwpNotificationNetCoreTest
|
||||
var uuid = typeof(T).GUID;
|
||||
uint _cookie;
|
||||
CoRegisterClassObject(uuid, new NotificationActivatorClassFactory<T>(), CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE, out _cookie);
|
||||
|
||||
_registeredActivator = true;
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[Guid("00000001-0000-0000-C000-000000000046")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
private interface IClassFactory
|
||||
{
|
||||
[PreserveSig]
|
||||
int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
|
||||
|
||||
[PreserveSig]
|
||||
int LockServer(bool fLock);
|
||||
}
|
||||
|
||||
private const int CLASS_E_NOAGGREGATION = -2147221232;
|
||||
private const int E_NOINTERFACE = -2147467262;
|
||||
private const int CLSCTX_LOCAL_SERVER = 4;
|
||||
private const int REGCLS_MULTIPLEUSE = 1;
|
||||
private const int S_OK = 0;
|
||||
private static readonly Guid IUnknownGuid = new Guid("00000000-0000-0000-C000-000000000046");
|
||||
|
||||
private class NotificationActivatorClassFactory<T> : IClassFactory where T : NotificationActivator, new()
|
||||
{
|
||||
public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject)
|
||||
{
|
||||
ppvObject = IntPtr.Zero;
|
||||
|
||||
if (pUnkOuter != IntPtr.Zero)
|
||||
Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
|
||||
|
||||
if (riid == typeof(T).GUID || riid == IUnknownGuid)
|
||||
// Create the instance of the .NET object
|
||||
ppvObject = Marshal.GetComInterfaceForObject(new T(),
|
||||
typeof(INotificationActivationCallback));
|
||||
else
|
||||
// The object that ppvObject points to does not support the
|
||||
// interface identified by riid.
|
||||
Marshal.ThrowExceptionForHR(E_NOINTERFACE);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
public int LockServer(bool fLock)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
private static extern int CoRegisterClassObject(
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
|
||||
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
|
||||
uint dwClsContext,
|
||||
uint flags,
|
||||
out uint lpdwRegister);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notifier. You must have called <see cref="RegisterActivator{T}"/> first (and also <see cref="RegisterAumidAndComServer(string)"/> if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ToastNotifier CreateToastNotifier()
|
||||
{
|
||||
EnsureRegistered();
|
||||
|
||||
if (_aumid != null)
|
||||
{
|
||||
// Non-Desktop Bridge
|
||||
return ToastNotificationManager.CreateToastNotifier(_aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Desktop Bridge
|
||||
return ToastNotificationManager.CreateToastNotifier();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DesktopNotificationHistoryCompat"/> object. You must have called <see cref="RegisterActivator{T}"/> first (and also <see cref="RegisterAumidAndComServer(string)"/> if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
public static DesktopNotificationHistoryCompat History
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureRegistered();
|
||||
|
||||
return new DesktopNotificationHistoryCompat(_aumid);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureRegistered()
|
||||
{
|
||||
// If not registered AUMID yet
|
||||
if (!_registeredAumidAndComServer)
|
||||
{
|
||||
// Check if Desktop Bridge
|
||||
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||
{
|
||||
// Implicitly registered, all good!
|
||||
_registeredAumidAndComServer = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Otherwise, incorrect usage
|
||||
throw new Exception("You must call RegisterAumidAndComServer first.");
|
||||
}
|
||||
}
|
||||
|
||||
// If not registered activator yet
|
||||
if (!_registeredActivator)
|
||||
{
|
||||
// Incorrect usage
|
||||
throw new Exception("You must call RegisterActivator first.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean representing whether http images can be used within toasts. This is true if running with package identity (MSIX or sparse package).
|
||||
/// </summary>
|
||||
public static bool CanUseHttpImages { get { return DesktopBridgeHelpers.IsRunningAsUwp(); } }
|
||||
|
||||
private static bool IsElevated
|
||||
{
|
||||
get
|
||||
{
|
||||
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs
|
||||
/// </summary>
|
||||
private class DesktopBridgeHelpers
|
||||
{
|
||||
const long APPMODEL_ERROR_NO_PACKAGE = 15700L;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
|
||||
|
||||
private static bool? _isRunningAsUwp;
|
||||
public static bool IsRunningAsUwp()
|
||||
{
|
||||
if (_isRunningAsUwp == null)
|
||||
{
|
||||
if (IsWindows7OrLower)
|
||||
{
|
||||
_isRunningAsUwp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int length = 0;
|
||||
StringBuilder sb = new StringBuilder(0);
|
||||
int result = GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
sb = new StringBuilder(length);
|
||||
result = GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
_isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
}
|
||||
|
||||
return _isRunningAsUwp.Value;
|
||||
}
|
||||
|
||||
private static bool IsWindows7OrLower
|
||||
{
|
||||
get
|
||||
{
|
||||
int versionMajor = Environment.OSVersion.Version.Major;
|
||||
int versionMinor = Environment.OSVersion.Version.Minor;
|
||||
double version = versionMajor + (double)versionMinor / 10;
|
||||
return version <= 6.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages the toast notifications for an app including the ability the clear all toast history and removing individual toasts.
|
||||
/// </summary>
|
||||
public sealed class DesktopNotificationHistoryCompat
|
||||
{
|
||||
private string _aumid;
|
||||
private ToastNotificationHistory _history;
|
||||
|
||||
/// <summary>
|
||||
/// Do not call this. Instead, call <see cref="DesktopNotificationManagerCompat.History"/> to obtain an instance.
|
||||
/// </summary>
|
||||
/// <param name="aumid"></param>
|
||||
internal DesktopNotificationHistoryCompat(string aumid)
|
||||
{
|
||||
_aumid = aumid;
|
||||
_history = ToastNotificationManager.History;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all notifications sent by this app from action center.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if (_aumid != null)
|
||||
{
|
||||
_history.Clear(_aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_history.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all notifications sent by this app that are currently still in Action Center.
|
||||
/// </summary>
|
||||
/// <returns>A collection of toasts.</returns>
|
||||
public IReadOnlyList<ToastNotification> GetHistory()
|
||||
{
|
||||
return _aumid != null ? _history.GetHistory(_aumid) : _history.GetHistory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an individual toast, with the specified tag label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
public void Remove(string tag)
|
||||
{
|
||||
if (_aumid != null)
|
||||
{
|
||||
_history.Remove(tag, string.Empty, _aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_history.Remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a toast notification from the action using the notification's tag and group labels.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
/// <param name="group">The group label of the toast notification to be removed.</param>
|
||||
public void Remove(string tag, string group)
|
||||
{
|
||||
if (_aumid != null)
|
||||
{
|
||||
_history.Remove(tag, group, _aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_history.Remove(tag, group);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a group of toast notifications, identified by the specified group label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="group">The group label of the toast notifications to be removed.</param>
|
||||
public void RemoveGroup(string group)
|
||||
{
|
||||
if (_aumid != null)
|
||||
{
|
||||
_history.RemoveGroup(group, _aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_history.RemoveGroup(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apps must implement this activator to handle notification activation.
|
||||
/// </summary>
|
||||
public abstract class NotificationActivator : NotificationActivator.INotificationActivationCallback
|
||||
{
|
||||
public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount)
|
||||
{
|
||||
OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will be called when the user clicks on a foreground or background activation on a toast. Parent app must implement this method.
|
||||
/// </summary>
|
||||
/// <param name="arguments">The arguments from the original notification. This is either the launch argument if the user clicked the body of your toast, or the arguments from a button on your toast.</param>
|
||||
/// <param name="userInput">Text and selection values that the user entered in your toast.</param>
|
||||
/// <param name="appUserModelId">Your AUMID.</param>
|
||||
public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId);
|
||||
|
||||
// These are the new APIs for Windows 10
|
||||
#region NewAPIs
|
||||
[StructLayout(LayoutKind.Sequential), Serializable]
|
||||
public struct NOTIFICATION_USER_INPUT_DATA
|
||||
{
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string Key;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string Value;
|
||||
}
|
||||
|
||||
[ComImport,
|
||||
Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true),
|
||||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface INotificationActivationCallback
|
||||
{
|
||||
void Activate(
|
||||
[In, MarshalAs(UnmanagedType.LPWStr)]
|
||||
string appUserModelId,
|
||||
[In, MarshalAs(UnmanagedType.LPWStr)]
|
||||
string invokedArgs,
|
||||
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
|
||||
NOTIFICATION_USER_INPUT_DATA[] data,
|
||||
[In, MarshalAs(UnmanagedType.U4)]
|
||||
uint dataCount);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text and selection values that the user entered on your notification. The Key is the ID of the input, and the Value is what the user entered.
|
||||
/// </summary>
|
||||
public class NotificationUserInput : IReadOnlyDictionary<string, string>
|
||||
{
|
||||
private NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] _data;
|
||||
|
||||
internal NotificationUserInput(NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public string this[string key] => _data.First(i => i.Key == key).Value;
|
||||
|
||||
public IEnumerable<string> Keys => _data.Select(i => i.Key);
|
||||
|
||||
public IEnumerable<string> Values => _data.Select(i => i.Value);
|
||||
|
||||
public int Count => _data.Length;
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return _data.Any(i => i.Key == key);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return _data.Select(i => new KeyValuePair<string, string>(i.Key, i.Value)).GetEnumerator();
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
foreach (var item in _data)
|
||||
{
|
||||
if (item.Key == key)
|
||||
{
|
||||
value = item.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
@ -86,6 +86,8 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DesktopNotificationActivator.cs" />
|
||||
<Compile Include="DesktopNotificationManagerCompat.cs" />
|
||||
<Compile Include="GameLibraries\Game.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\AppInfo.cs" />
|
||||
<Compile Include="GameLibraries\SteamAppInfoParser\EUniverse.cs" />
|
||||
@ -255,6 +257,9 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
|
||||
<Version>6.1.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MintPlayer.IconUtils">
|
||||
<Version>1.0.4</Version>
|
||||
</PackageReference>
|
||||
@ -264,6 +269,9 @@
|
||||
<PackageReference Include="NLog">
|
||||
<Version>4.7.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="QueryString.NET">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ValveKeyValue">
|
||||
<Version>0.3.1.152</Version>
|
||||
</PackageReference>
|
||||
|
@ -13,6 +13,9 @@ using DisplayMagician.Shared;
|
||||
using DisplayMagician.UIForms;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Drawing;
|
||||
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using DesktopNotifications;
|
||||
|
||||
//using System.Runtime.InteropServices;
|
||||
|
||||
namespace DisplayMagician {
|
||||
@ -53,7 +56,11 @@ namespace DisplayMagician {
|
||||
// taskbar pinning and similar.
|
||||
// This is a helper function that sets the AUMID to a static default defined
|
||||
// within ShellUtils under the DisplayMagician.Shared project.
|
||||
ShellUtils.SetDefaultProcessExplicitAppUserModelID();
|
||||
//ShellUtils.SetDefaultProcessExplicitAppUserModelID();
|
||||
|
||||
// Register AUMID, COM server, and activator
|
||||
DesktopNotificationManagerCompat.RegisterAumidAndComServer<DesktopNotificationActivator>(ShellUtils.AUMID);
|
||||
DesktopNotificationManagerCompat.RegisterActivator<DesktopNotificationActivator>();
|
||||
|
||||
// Prepare NLog for logging
|
||||
var config = new NLog.Config.LoggingConfiguration();
|
||||
|
13
DisplayMagician/UIForms/MainForm.Designer.cs
generated
13
DisplayMagician/UIForms/MainForm.Designer.cs
generated
@ -31,6 +31,7 @@
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
this.btn_toast = new System.Windows.Forms.Button();
|
||||
this.btn_settings = new System.Windows.Forms.Button();
|
||||
this.lbl_create_profile = new System.Windows.Forms.Label();
|
||||
this.btn_setup_display_profiles = new System.Windows.Forms.Button();
|
||||
@ -71,6 +72,7 @@
|
||||
//
|
||||
// splitContainer1.Panel1
|
||||
//
|
||||
this.splitContainer1.Panel1.Controls.Add(this.btn_toast);
|
||||
this.splitContainer1.Panel1.Controls.Add(this.btn_settings);
|
||||
this.splitContainer1.Panel1.Controls.Add(this.lbl_create_profile);
|
||||
this.splitContainer1.Panel1.Controls.Add(this.btn_setup_display_profiles);
|
||||
@ -86,6 +88,16 @@
|
||||
this.splitContainer1.Panel2.Controls.Add(this.pb_game_shortcut);
|
||||
this.splitContainer1.TabStop = false;
|
||||
//
|
||||
// btn_toast
|
||||
//
|
||||
this.btn_toast.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
|
||||
this.btn_toast.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
|
||||
resources.ApplyResources(this.btn_toast, "btn_toast");
|
||||
this.btn_toast.ForeColor = System.Drawing.Color.White;
|
||||
this.btn_toast.Name = "btn_toast";
|
||||
this.btn_toast.UseVisualStyleBackColor = true;
|
||||
this.btn_toast.Click += new System.EventHandler(this.btn_toast_Click);
|
||||
//
|
||||
// btn_settings
|
||||
//
|
||||
this.btn_settings.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
|
||||
@ -303,5 +315,6 @@
|
||||
private System.Windows.Forms.Label lbl_create_profile;
|
||||
private System.Windows.Forms.Label lbl_create_shortcut;
|
||||
private System.Windows.Forms.Button btn_settings;
|
||||
private System.Windows.Forms.Button btn_toast;
|
||||
}
|
||||
}
|
@ -12,10 +12,16 @@ using System.Threading;
|
||||
using System.Reflection;
|
||||
using DisplayMagician.Shared;
|
||||
using System.Runtime.InteropServices;
|
||||
//using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using DesktopNotifications;
|
||||
using Microsoft.QueryStringDotNET;
|
||||
using Windows.UI.Notifications;
|
||||
using System.IO;
|
||||
using AutoUpdaterDotNET;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
|
||||
namespace DisplayMagician.UIForms
|
||||
{
|
||||
@ -390,5 +396,78 @@ namespace DisplayMagician.UIForms
|
||||
else if (!mySettings.MinimiseOnStart && cb_minimise_notification_area.Checked)
|
||||
cb_minimise_notification_area.Checked = false;
|
||||
}
|
||||
|
||||
private void btn_toast_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
// Construct the toast content
|
||||
ToastContent toastContent = new ToastContent()
|
||||
{
|
||||
// Arguments when the user taps body of toast
|
||||
Launch = "DisplayMagician_Notification",
|
||||
|
||||
Visual = new ToastVisual()
|
||||
{
|
||||
BindingGeneric = new ToastBindingGeneric()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new AdaptiveText()
|
||||
{
|
||||
Text = "Hello from DisplayMagician!"
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
/*
|
||||
Actions = new ToastActionsCustom()
|
||||
{
|
||||
Inputs =
|
||||
{
|
||||
new ToastTextBox("tbReply")
|
||||
{
|
||||
PlaceholderContent = "Type a response"
|
||||
}
|
||||
},
|
||||
|
||||
Buttons =
|
||||
{
|
||||
// Note that there's no reason to specify background activation, since our COM
|
||||
// activator decides whether to process in background or launch foreground window
|
||||
new ToastButton("Reply", new QueryString()
|
||||
{
|
||||
{ "action", "reply" },
|
||||
{ "conversationId", conversationId.ToString() }
|
||||
|
||||
}.ToString()),
|
||||
|
||||
new ToastButton("Like", new QueryString()
|
||||
{
|
||||
{ "action", "like" },
|
||||
{ "conversationId", conversationId.ToString() }
|
||||
|
||||
}.ToString()),
|
||||
|
||||
new ToastButton("View", new QueryString()
|
||||
{
|
||||
{ "action", "viewImage" },
|
||||
{ "imageUrl", image }
|
||||
|
||||
}.ToString())
|
||||
}
|
||||
}*/
|
||||
};
|
||||
|
||||
// Make sure to use Windows.Data.Xml.Dom
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(toastContent.GetContent());
|
||||
|
||||
// And create the toast notification
|
||||
var toast = new ToastNotification(doc);
|
||||
|
||||
// And then show it
|
||||
DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,36 @@
|
||||
<data name="splitContainer1.Orientation" type="System.Windows.Forms.Orientation, System.Windows.Forms">
|
||||
<value>Horizontal</value>
|
||||
</data>
|
||||
<data name="btn_toast.FlatStyle" type="System.Windows.Forms.FlatStyle, System.Windows.Forms">
|
||||
<value>Flat</value>
|
||||
</data>
|
||||
<data name="btn_toast.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="btn_toast.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>366, 64</value>
|
||||
</data>
|
||||
<data name="btn_toast.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>75, 23</value>
|
||||
</data>
|
||||
<data name="btn_toast.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>8</value>
|
||||
</data>
|
||||
<data name="btn_toast.Text" xml:space="preserve">
|
||||
<value>Show Toast</value>
|
||||
</data>
|
||||
<data name=">>btn_toast.Name" xml:space="preserve">
|
||||
<value>btn_toast</value>
|
||||
</data>
|
||||
<data name=">>btn_toast.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>btn_toast.Parent" xml:space="preserve">
|
||||
<value>splitContainer1.Panel1</value>
|
||||
</data>
|
||||
<data name=">>btn_toast.ZOrder" xml:space="preserve">
|
||||
<value>0</value>
|
||||
</data>
|
||||
<data name="btn_settings.FlatStyle" type="System.Windows.Forms.FlatStyle, System.Windows.Forms">
|
||||
<value>Flat</value>
|
||||
</data>
|
||||
@ -157,7 +187,7 @@
|
||||
<value>splitContainer1.Panel1</value>
|
||||
</data>
|
||||
<data name=">>btn_settings.ZOrder" xml:space="preserve">
|
||||
<value>0</value>
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name="lbl_create_profile.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
||||
<value>None</value>
|
||||
@ -190,7 +220,7 @@
|
||||
<value>splitContainer1.Panel1</value>
|
||||
</data>
|
||||
<data name=">>lbl_create_profile.ZOrder" xml:space="preserve">
|
||||
<value>1</value>
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name="btn_setup_display_profiles.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
|
||||
<value>None</value>
|
||||
@ -223,7 +253,7 @@
|
||||
<value>splitContainer1.Panel1</value>
|
||||
</data>
|
||||
<data name=">>btn_setup_display_profiles.ZOrder" xml:space="preserve">
|
||||
<value>2</value>
|
||||
<value>3</value>
|
||||
</data>
|
||||
<data name="pb_display_profile.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
|
||||
<value>Fill</value>
|
||||
@ -10771,7 +10801,7 @@
|
||||
<value>splitContainer1.Panel1</value>
|
||||
</data>
|
||||
<data name=">>pb_display_profile.ZOrder" xml:space="preserve">
|
||||
<value>3</value>
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name=">>splitContainer1.Panel1.Name" xml:space="preserve">
|
||||
<value>splitContainer1.Panel1</value>
|
||||
@ -10798,7 +10828,7 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="lbl_create_shortcut.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>220, 253</value>
|
||||
<value>220, 257</value>
|
||||
</data>
|
||||
<data name="lbl_create_shortcut.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>343, 22</value>
|
||||
@ -10837,7 +10867,7 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="cb_minimise_notification_area.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>234, 357</value>
|
||||
<value>234, 363</value>
|
||||
</data>
|
||||
<data name="cb_minimise_notification_area.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>332, 20</value>
|
||||
@ -10870,7 +10900,7 @@
|
||||
<value>Microsoft Sans Serif, 9.75pt</value>
|
||||
</data>
|
||||
<data name="lbl_version.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 358</value>
|
||||
<value>12, 364</value>
|
||||
</data>
|
||||
<data name="lbl_version.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>25, 16</value>
|
||||
@ -10906,7 +10936,7 @@
|
||||
<value>Microsoft Sans Serif, 21.75pt</value>
|
||||
</data>
|
||||
<data name="btn_setup_game_shortcuts.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>212, 180</value>
|
||||
<value>212, 184</value>
|
||||
</data>
|
||||
<data name="btn_setup_game_shortcuts.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>360, 50</value>
|
||||
@ -10939,7 +10969,7 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="btn_exit.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>700, 354</value>
|
||||
<value>700, 360</value>
|
||||
</data>
|
||||
<data name="btn_exit.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>75, 23</value>
|
||||
|
Loading…
Reference in New Issue
Block a user