From 502fb6dbed3c0b26608b49a15e02f9f8e82c4fb2 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 16 Jun 2024 17:27:20 +0800 Subject: [PATCH] fix notification activation --- src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 11 +- .../Core/LifeCycle/AppActivation.cs | 237 ++++++++---------- .../AppActivationArgumentsExtensions.cs | 25 +- .../LifeCycle/HutaoActivationArguments.cs | 24 +- .../Core/LifeCycle/HutaoActivationKind.cs | 2 +- .../Core/LifeCycle/IAppActivation.cs | 2 +- .../Job/DailyNoteRefreshJobScheduler.cs | 2 +- 7 files changed, 137 insertions(+), 166 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index d084385a..4e950dc9 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -71,7 +71,7 @@ public sealed partial class App : Application { // Important: You must call AppNotificationManager::Default().Register // before calling AppInstance.GetCurrent.GetActivatedEventArgs. - AppNotificationManager.Default.NotificationInvoked += activation.NotificationActivate; + AppNotificationManager.Default.NotificationInvoked += activation.NotificationInvoked; AppNotificationManager.Default.Register(); AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); @@ -85,15 +85,8 @@ public sealed partial class App : Application logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.DarkYellow)); LogDiagnosticInformation(); - HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs); - if (hutaoArgs.Kind is HutaoActivationKind.Toast) - { - Exit(); - return; - } - // Manually invoke - activation.Activate(hutaoArgs); + activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs)); activation.PostInitialization(); } catch (Exception ex) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs index 4fef7e60..9eb7cf3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs @@ -11,7 +11,6 @@ using Snap.Hutao.Core.Windowing; using Snap.Hutao.Core.Windowing.HotKey; using Snap.Hutao.Core.Windowing.NotifyIcon; using Snap.Hutao.Service; -using Snap.Hutao.Service.DailyNote; using Snap.Hutao.Service.Discord; using Snap.Hutao.Service.Hutao; using Snap.Hutao.Service.Job; @@ -37,9 +36,7 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard); private const string CategoryAchievement = "ACHIEVEMENT"; - private const string CategoryDailyNote = "DAILYNOTE"; private const string UrlActionImport = "/IMPORT"; - private const string UrlActionRefresh = "/REFRESH"; private readonly ICurrentXamlWindowReference currentWindowReference; private readonly IServiceProvider serviceProvider; @@ -50,21 +47,49 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi /// public void Activate(HutaoActivationArguments args) { - HandleActivationAsync(args).SafeForget(); - } + HandleActivationExclusiveAsync(args).SafeForget(); - public void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args) - { - if (args.Arguments.TryGetValue(Action, out string? action)) + async ValueTask HandleActivationExclusiveAsync(HutaoActivationArguments args) { - if (action == LaunchGame) + await taskContext.SwitchToBackgroundAsync(); + + if (activateSemaphore.CurrentCount > 0) { - _ = args.Arguments.TryGetValue(Uid, out string? uid); - HandleLaunchGameActionAsync(uid).SafeForget(); + using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) + { + switch (args.Kind) + { + case HutaoActivationKind.Protocol: + { + ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); + await HandleProtocolActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); + break; + } + + case HutaoActivationKind.Launch: + { + ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); + await HandleLaunchActivationAsync(args.IsRedirectTo).ConfigureAwait(false); + break; + } + + case HutaoActivationKind.AppNotification: + { + ArgumentNullException.ThrowIfNull(args.AppNotificationActivatedArguments); + await HandleAppNotificationActivationAsync(args.AppNotificationActivatedArguments, args.IsRedirectTo).ConfigureAwait(false); + break; + } + } + } } } } + public void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args) + { + HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget(); + } + /// public void PostInitialization() { @@ -74,19 +99,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi { await taskContext.SwitchToBackgroundAsync(); - serviceProvider.GetRequiredService().RunAsync().SafeForget(); - using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) { // TODO: Introduced in 1.10.2, remove in later version - serviceProvider.GetRequiredService().ClearAsync().SafeForget(); - serviceProvider.GetRequiredService().UnregisterAllTasks(); + { + serviceProvider.GetRequiredService().ClearAsync().SafeForget(); + serviceProvider.GetRequiredService().UnregisterAllTasks(); + } if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) { return; } + serviceProvider.GetRequiredService().RunAsync().SafeForget(); + + // RegisterHotKey should be called from main thread await taskContext.SwitchToMainThreadAsync(); serviceProvider.GetRequiredService().RegisterAll(); @@ -99,7 +127,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi _ = serviceProvider.GetRequiredService(); } - serviceProvider.GetRequiredService().StartAsync(default).SafeForget(); + serviceProvider.GetRequiredService().SetNormalActivityAsync().SafeForget(); + serviceProvider.GetRequiredService().StartAsync().SafeForget(); + + if (serviceProvider.GetRequiredService() is IMetadataServiceInitialization metadataServiceInitialization) + { + metadataServiceInitialization.InitializeInternalAsync().SafeForget(); + } + + if (serviceProvider.GetRequiredService() is IHutaoUserServiceInitialization hutaoUserServiceInitialization) + { + hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget(); + } } } } @@ -145,38 +184,36 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi } } - private async ValueTask HandleActivationAsync(HutaoActivationArguments args) + private async ValueTask HandleProtocolActivationAsync(Uri uri, bool isRedirectTo) { - await taskContext.SwitchToBackgroundAsync(); + UriBuilder builder = new(uri); - if (activateSemaphore.CurrentCount > 0) + string category = builder.Host.ToUpperInvariant(); + string action = builder.Path.ToUpperInvariant(); + + // string parameter = builder.Query.ToUpperInvariant(); + switch (category) { - using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) - { - await HandleActivationCoreAsync(args).ConfigureAwait(false); - } - } - } - - private async ValueTask HandleActivationCoreAsync(HutaoActivationArguments args) - { - switch (args.Kind) - { - case HutaoActivationKind.Protocol: + case CategoryAchievement: { - ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); - await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); - break; - } - - case HutaoActivationKind.Launch: - { - ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); - switch (args.LaunchActivatedArguments) + await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); + if (currentWindowReference.Window is not MainWindow) { - default: + // TODO: Send notification to hint? + return; + } + + switch (action) + { + case UrlActionImport: { - await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false); + await taskContext.SwitchToMainThreadAsync(); + + INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard); + await serviceProvider + .GetRequiredService() + .NavigateAsync(navigationAwaiter, true) + .ConfigureAwait(false); break; } } @@ -184,14 +221,15 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi break; } - case HutaoActivationKind.Toast: + default: { + await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false); break; } } } - private async ValueTask HandleNormalLaunchActionAsync(bool isRedirectTo) + private async ValueTask HandleLaunchActivationAsync(bool isRedirectTo) { if (!isRedirectTo) { @@ -223,6 +261,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); } + private async ValueTask HandleAppNotificationActivationAsync(IDictionary arguments, bool isRedirectTo) + { + if (arguments.TryGetValue(Action, out string? action)) + { + if (action == LaunchGame) + { + _ = arguments.TryGetValue(Uid, out string? uid); + await HandleLaunchGameActionAsync(uid).ConfigureAwait(false); + } + } + else + { + await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false); + } + } + private async ValueTask WaitMainWindowOrCurrentAsync() { if (currentWindowReference.Window is { } window) @@ -239,100 +293,5 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi mainWindow.SwitchTo(); mainWindow.BringToForeground(); - - await taskContext.SwitchToBackgroundAsync(); - - if (serviceProvider.GetRequiredService() is IMetadataServiceInitialization metadataServiceInitialization) - { - metadataServiceInitialization.InitializeInternalAsync().SafeForget(); - } - - if (serviceProvider.GetRequiredService() is IHutaoUserServiceInitialization hutaoUserServiceInitialization) - { - hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget(); - } - - serviceProvider.GetRequiredService().SetNormalActivityAsync().SafeForget(); - } - - private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirectTo) - { - UriBuilder builder = new(uri); - - string category = builder.Host.ToUpperInvariant(); - string action = builder.Path.ToUpperInvariant(); - string parameter = builder.Query.ToUpperInvariant(); - - switch (category) - { - case CategoryAchievement: - { - await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); - await HandleAchievementActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false); - break; - } - - case CategoryDailyNote: - { - await HandleDailyNoteActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false); - break; - } - - default: - { - await HandleNormalLaunchActionAsync(isRedirectTo).ConfigureAwait(false); - break; - } - } - } - - private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirectTo) - { - _ = parameter; - _ = isRedirectTo; - switch (action) - { - case UrlActionImport: - { - await taskContext.SwitchToMainThreadAsync(); - - INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard); - await serviceProvider - .GetRequiredService() - .NavigateAsync(navigationAwaiter, true) - .ConfigureAwait(false); - break; - } - } - } - - private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirectTo) - { - _ = parameter; - switch (action) - { - case UrlActionRefresh: - { - try - { - await serviceProvider - .GetRequiredService() - .RefreshDailyNotesAsync() - .ConfigureAwait(false); - } - catch - { - } - - // Check if it's redirected. - if (!isRedirectTo) - { - // It's a direct open process, should exit immediately. - Process.GetCurrentProcess().Kill(); - } - - break; - } - } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs index 2fc31d32..fc69033f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.Extensions.Primitives; using Microsoft.Windows.AppLifecycle; +using Microsoft.Windows.AppNotifications; using Windows.ApplicationModel.Activation; namespace Snap.Hutao.Core.LifeCycle; @@ -25,10 +25,9 @@ internal static class AppActivationArgumentsExtensions return true; } - public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments, out bool isToastActivated) + public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments) { arguments = null; - isToastActivated = false; if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs) { @@ -36,15 +35,23 @@ internal static class AppActivationArgumentsExtensions } arguments = launchArgs.Arguments.Trim(); - foreach (StringSegment segment in new StringTokenizer(arguments, [' '])) + return true; + } + + public static bool TryGetAppNotificationActivatedArguments(this AppActivationArguments activatedEventArgs, out string? argument, [NotNullWhen(true)] out IDictionary? arguments, [NotNullWhen(true)] out IDictionary? userInput) + { + argument = null; + arguments = null; + userInput = null; + + if (activatedEventArgs.Data is not AppNotificationActivatedEventArgs appNotificationArgs) { - if (segment.AsSpan().SequenceEqual("----AppNotificationActivated:")) - { - isToastActivated = true; - break; - } + return false; } + argument = appNotificationArgs.Argument; + arguments = appNotificationArgs.Arguments; + userInput = appNotificationArgs.UserInput; return true; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs index 3fa39818..7b71f052 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs @@ -15,6 +15,10 @@ internal sealed class HutaoActivationArguments public string? LaunchActivatedArguments { get; set; } + public IDictionary? AppNotificationActivatedArguments { get; set; } + + public IDictionary? AppNotificationActivatedUserInput { get; set; } + public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false) { HutaoActivationArguments result = new() @@ -27,14 +31,9 @@ internal sealed class HutaoActivationArguments case ExtendedActivationKind.Launch: { result.Kind = HutaoActivationKind.Launch; - - if (args.TryGetLaunchActivatedArguments(out string? arguments, out bool isToastActivated)) + if (args.TryGetLaunchActivatedArguments(out string? arguments)) { result.LaunchActivatedArguments = arguments; - if (isToastActivated) - { - result.Kind = HutaoActivationKind.Toast; - } } break; @@ -48,6 +47,19 @@ internal sealed class HutaoActivationArguments result.ProtocolActivatedUri = uri; } + break; + } + + case ExtendedActivationKind.AppNotification: + { + result.Kind = HutaoActivationKind.AppNotification; + if (args.TryGetAppNotificationActivatedArguments(out string? argument, out IDictionary? arguments, out IDictionary? userInput)) + { + result.LaunchActivatedArguments = argument; + result.AppNotificationActivatedArguments = arguments; + result.AppNotificationActivatedUserInput = userInput; + } + break; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs index 66211290..8bce6505 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs @@ -7,6 +7,6 @@ internal enum HutaoActivationKind { None, Launch, - Toast, + AppNotification, Protocol, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs index 075894fa..c14d95e3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs @@ -9,7 +9,7 @@ internal interface IAppActivation { void Activate(HutaoActivationArguments args); - void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args); + void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args); void PostInitialization(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs index 9635e576..14837f3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs @@ -25,8 +25,8 @@ internal sealed partial class DailyNoteRefreshJobScheduler : IJobScheduler ITrigger dailyNoteTrigger = TriggerBuilder.Create() .WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName) - .StartNow() .WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever()) + .StartAt(DateTimeOffset.Now.AddSeconds(interval)) .Build(); await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false);