fix notification activation

This commit is contained in:
DismissedLight
2024-06-16 17:27:20 +08:00
parent 4fa5270070
commit 502fb6dbed
7 changed files with 137 additions and 166 deletions

View File

@@ -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)

View File

@@ -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
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
public void PostInitialization()
{
@@ -74,19 +99,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
{
await taskContext.SwitchToBackgroundAsync();
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
{
// TODO: Introduced in 1.10.2, remove in later version
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
{
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
}
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
{
return;
}
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
// RegisterHotKey should be called from main thread
await taskContext.SwitchToMainThreadAsync();
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
@@ -99,7 +127,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
_ = serviceProvider.GetRequiredService<NotifyIconController>();
}
serviceProvider.GetRequiredService<IQuartzService>().StartAsync(default).SafeForget();
serviceProvider.GetRequiredService<IDiscordService>().SetNormalActivityAsync().SafeForget();
serviceProvider.GetRequiredService<IQuartzService>().StartAsync().SafeForget();
if (serviceProvider.GetRequiredService<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
{
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
}
if (serviceProvider.GetRequiredService<IHutaoUserService>() 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<INavigationService>()
.NavigateAsync<View.Page.AchievementPage>(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<string, string> 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<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
{
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
}
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
{
hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget();
}
serviceProvider.GetRequiredService<IDiscordService>().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<INavigationService>()
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
.ConfigureAwait(false);
break;
}
}
}
private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirectTo)
{
_ = parameter;
switch (action)
{
case UrlActionRefresh:
{
try
{
await serviceProvider
.GetRequiredService<IDailyNoteService>()
.RefreshDailyNotesAsync()
.ConfigureAwait(false);
}
catch
{
}
// Check if it's redirected.
if (!isRedirectTo)
{
// It's a direct open process, should exit immediately.
Process.GetCurrentProcess().Kill();
}
break;
}
}
}
}

View File

@@ -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<string, string>? arguments, [NotNullWhen(true)] out IDictionary<string, string>? 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;
}
}

View File

@@ -15,6 +15,10 @@ internal sealed class HutaoActivationArguments
public string? LaunchActivatedArguments { get; set; }
public IDictionary<string, string>? AppNotificationActivatedArguments { get; set; }
public IDictionary<string, string>? 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<string, string>? arguments, out IDictionary<string, string>? userInput))
{
result.LaunchActivatedArguments = argument;
result.AppNotificationActivatedArguments = arguments;
result.AppNotificationActivatedUserInput = userInput;
}
break;
}
}

View File

@@ -7,6 +7,6 @@ internal enum HutaoActivationKind
{
None,
Launch,
Toast,
AppNotification,
Protocol,
}

View File

@@ -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();
}

View File

@@ -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);