mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix notification activation
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ internal enum HutaoActivationKind
|
||||
{
|
||||
None,
|
||||
Launch,
|
||||
Toast,
|
||||
AppNotification,
|
||||
Protocol,
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user