From 48e5d9ad42bcb384c0c78b69e580a74a2dfe3011 Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Wed, 24 Jan 2024 22:17:38 +0200 Subject: [PATCH 1/3] Windows Snackbar rewrite Replaces ToastNotification with AppNotificationManager. Fixes Snackbar and Toast execute new instance of the app --- .../Platforms/Windows/App.xaml.cs | 41 ------------- .../Platforms/Windows/Package.appxmanifest | 23 ++++++- .../Alerts/Snackbar/Snackbar.windows.cs | 61 ++++++++++--------- .../AppBuilderExtensions.shared.cs | 29 ++++++++- .../ToastNotificationExtensions.windows.cs | 48 --------------- 5 files changed, 81 insertions(+), 121 deletions(-) delete mode 100644 src/CommunityToolkit.Maui/Extensions/ToastNotificationExtensions.windows.cs diff --git a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/App.xaml.cs b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/App.xaml.cs index 828160aaf..a3e3d17c2 100644 --- a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/App.xaml.cs +++ b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/App.xaml.cs @@ -1,6 +1,3 @@ -using System.Diagnostics; -using Microsoft.UI.Xaml; - namespace CommunityToolkit.Maui.Sample.Windows; /// @@ -18,42 +15,4 @@ public App() } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); - - static Mutex? mutex; - - protected override void OnLaunched(LaunchActivatedEventArgs args) - { - if (!IsSingleInstance()) - { - Process.GetCurrentProcess().Kill(); - } - else - { - base.OnLaunched(args); - } - } - - - static bool IsSingleInstance() - { - const string applicationId = "1F9C3A44-059B-4FBC-9D92-476E59FB937A"; - mutex = new Mutex(false, applicationId); - - // keep the mutex reference alive until the normal - // termination of the program - GC.KeepAlive(mutex); - - try - { - return mutex.WaitOne(0, false); - } - catch (AbandonedMutexException) - { - // if one thread acquires a Mutex object - // that another thread has abandoned - // by exiting without releasing it - mutex.ReleaseMutex(); - return mutex.WaitOne(0, false); - } - } } \ No newline at end of file diff --git a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest index 09e938e9e..f30e3caec 100644 --- a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest +++ b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest @@ -3,9 +3,11 @@ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" - IgnorableNamespaces="uap rescap"> + xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" + xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" + IgnorableNamespaces="uap rescap com desktop"> - + $placeholder$ @@ -33,6 +35,23 @@ + + + + + + + + + + + + + + + + + diff --git a/src/CommunityToolkit.Maui/Alerts/Snackbar/Snackbar.windows.cs b/src/CommunityToolkit.Maui/Alerts/Snackbar/Snackbar.windows.cs index 35c9ff623..da30ac455 100644 --- a/src/CommunityToolkit.Maui/Alerts/Snackbar/Snackbar.windows.cs +++ b/src/CommunityToolkit.Maui/Alerts/Snackbar/Snackbar.windows.cs @@ -1,15 +1,26 @@ -using Microsoft.Maui.Dispatching; -using Windows.UI.Notifications; -using static CommunityToolkit.Maui.Extensions.ToastNotificationExtensions; +using Microsoft.Windows.AppNotifications; +using Microsoft.Windows.AppNotifications.Builder; namespace CommunityToolkit.Maui.Alerts; public partial class Snackbar { - static Windows.UI.Notifications.ToastNotification? PlatformSnackbar { get; set; } + const string snackbarIdentifierArgumentKey = "snackbarIdentifier"; + + static AppNotification? PlatformSnackbar { get; set; } + + static Dictionary actions = new(); TaskCompletionSource? dismissedTCS; + internal static void HandleSnackbarAction(AppNotificationActivatedEventArgs args) + { + if (args.Arguments.TryGetValue(snackbarIdentifierArgumentKey, out var id) && actions.TryGetValue(id, out var action) && action is not null) + { + Dispatcher.GetForCurrentThread().DispatchIfRequired(action); + } + } + /// /// Dispose Snackbar /// @@ -35,13 +46,15 @@ async Task DismissPlatform(CancellationToken token) } token.ThrowIfCancellationRequested(); - ToastNotificationManager.History.Clear(); + await AppNotificationManager.Default.RemoveAllAsync(); + actions.Clear(); - PlatformSnackbar.Activated -= OnActivated; - PlatformSnackbar.Dismissed -= OnDismissed; - PlatformSnackbar.ExpirationTime = DateTimeOffset.Now; - - PlatformSnackbar = null; + // Verify PlatformToast is not null again after `await` + if (PlatformSnackbar is not null) + { + PlatformSnackbar.Expiration = DateTimeOffset.Now; + PlatformSnackbar = null; + } await (dismissedTCS?.Task ?? Task.CompletedTask); } @@ -55,28 +68,18 @@ async Task ShowPlatform(CancellationToken token) token.ThrowIfCancellationRequested(); dismissedTCS = new(); + var id = Guid.NewGuid().ToString(); + PlatformSnackbar = new AppNotificationBuilder() + .AddText(Text) + .AddButton(new AppNotificationButton(ActionButtonText) + .AddArgument(snackbarIdentifierArgumentKey, id)) + .BuildNotification(); + PlatformSnackbar.Expiration = DateTimeOffset.Now.Add(Duration); - PlatformSnackbar = new ToastNotification(BuildToastNotificationContent(Text, ActionButtonText)); - PlatformSnackbar.Activated += OnActivated; - PlatformSnackbar.Dismissed += OnDismissed; - PlatformSnackbar.ExpirationTime = DateTimeOffset.Now.Add(Duration); + AppNotificationManager.Default.Show(PlatformSnackbar); - ToastNotificationManager.CreateToastNotifier().Show(PlatformSnackbar); + actions.Add(id, Action); OnShown(); } - - void OnActivated(ToastNotification sender, object args) - { - if (PlatformSnackbar is not null && Action is not null) - { - Dispatcher.GetForCurrentThread().DispatchIfRequired(Action); - } - } - - void OnDismissed(ToastNotification sender, ToastDismissedEventArgs args) - { - dismissedTCS?.TrySetResult(true); - Dispatcher.GetForCurrentThread().DispatchIfRequired(OnDismissed); - } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs index 100bce9c2..ea25c4162 100644 --- a/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs @@ -1,6 +1,8 @@ -using CommunityToolkit.Maui.Core; +using CommunityToolkit.Maui.Alerts; +using CommunityToolkit.Maui.Core; using CommunityToolkit.Maui.Core.Handlers; using CommunityToolkit.Maui.Views; +using Microsoft.Maui.LifecycleEvents; namespace CommunityToolkit.Maui; @@ -20,6 +22,23 @@ public static MauiAppBuilder UseMauiCommunityToolkit(this MauiAppBuilder builder // Pass `null` because `options?.Invoke()` will set options on both `CommunityToolkit.Maui` and `CommunityToolkit.Maui.Core` builder.UseMauiCommunityToolkitCore(null); +#if WINDOWS + builder.ConfigureLifecycleEvents(events => + { + events.AddWindows(windows => windows + .OnLaunched((_, _) => + { + Microsoft.Windows.AppNotifications.AppNotificationManager.Default.NotificationInvoked += OnSnackbarNotificationInvoked; + Microsoft.Windows.AppNotifications.AppNotificationManager.Default.Register(); + }) + .OnClosed((_, _) => + { + Microsoft.Windows.AppNotifications.AppNotificationManager.Default.NotificationInvoked -= OnSnackbarNotificationInvoked; + Microsoft.Windows.AppNotifications.AppNotificationManager.Default.Unregister(); + })); + }); +#endif + builder.Services.AddSingleton(); // Invokes options for both `CommunityToolkit.Maui` and `CommunityToolkit.Maui.Core` @@ -36,4 +55,12 @@ public static MauiAppBuilder UseMauiCommunityToolkit(this MauiAppBuilder builder return builder; } +#if WINDOWS + static void OnSnackbarNotificationInvoked( + Microsoft.Windows.AppNotifications.AppNotificationManager sender, + Microsoft.Windows.AppNotifications.AppNotificationActivatedEventArgs args) + { + Snackbar.HandleSnackbarAction(args); + } +#endif } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Extensions/ToastNotificationExtensions.windows.cs b/src/CommunityToolkit.Maui/Extensions/ToastNotificationExtensions.windows.cs deleted file mode 100644 index 45cb72a5b..000000000 --- a/src/CommunityToolkit.Maui/Extensions/ToastNotificationExtensions.windows.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Windows.Data.Xml.Dom; -using Windows.UI.Notifications; - -namespace CommunityToolkit.Maui.Extensions; - -/// -/// Toast Notification Extensions -/// -static class ToastNotificationExtensions -{ - /// - /// Build Toast Text Notification with actions - /// - /// Notification text - /// Action Button text - /// - internal static XmlDocument BuildToastNotificationContent(string text, string actionText) - { - var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); - toastXml.GetElementsByTagName("text").SetContentText(text); - var toastElement = toastXml.SelectSingleNode("/toast"); - toastElement.SetActivationType(true); - toastElement.SetAction(actionText); - return toastXml; - } - - static void SetActivationType(this IXmlNode xmlNode, bool isBackground) - { - var activationTypeAttribute = xmlNode.OwnerDocument.CreateAttribute("activationType"); - activationTypeAttribute.Value = isBackground ? "background" : "foreground"; - xmlNode.Attributes.SetNamedItem(activationTypeAttribute); - } - - static void SetContentText(this XmlNodeList xmlNodes, string text) - { - xmlNodes.ForEach(node => node.InnerText = text); - } - - static void SetAction(this IXmlNode xmlNode, string text) - { - var actions = xmlNode.OwnerDocument.CreateElement("actions"); - xmlNode.AppendChild(actions); - var action = xmlNode.OwnerDocument.CreateElement("action"); - actions.AppendChild(action); - action.SetAttribute("content", text); - action.SetAttribute("arguments", text); - } -} \ No newline at end of file From a729a65d2c3bf194ef27a0cf50436b906dce3812 Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Thu, 25 Jan 2024 12:59:11 +0200 Subject: [PATCH 2/3] reverted to use maui-package-name-placeholder --- .../Platforms/Windows/Package.appxmanifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest index f30e3caec..368925607 100644 --- a/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest +++ b/samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest @@ -7,7 +7,7 @@ xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap rescap com desktop"> - + $placeholder$ From 4009beb8e899f6b61518f40b1fda6a3ad1747a2f Mon Sep 17 00:00:00 2001 From: Brandon Minnick <13558917+brminnick@users.noreply.github.com> Date: Thu, 1 Feb 2024 19:24:01 +0000 Subject: [PATCH 3/3] Update Xcode version for net8.0-maccatalyst --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 76381cd5c..f1079d4cb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,7 +21,7 @@ variables: PathToCommunityToolkitAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.Analyzers.CodeFixes/CommunityToolkit.Maui.Analyzers.CodeFixes.csproj' PathToCommunityToolkitMediaElementAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.MediaElement.Analyzers.CodeFixes/CommunityToolkit.Maui.MediaElement.Analyzers.CodeFixes.csproj' PathToCommunityToolkitAnalyzersUnitTestCsproj: 'src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj' - CommunityToolkitSampleApp_Xcode_Version: '15.0.1' + CommunityToolkitSampleApp_Xcode_Version: '15.1.0' CommunityToolkitLibrary_Xcode_Version: '15.0.1' trigger: