Compare commits

...

1 Commits

Author SHA1 Message Date
qhy040404
ca54faed2d detect notification permission
And prevent the crash caused by lack of permission when sending toast
2024-02-05 00:18:29 +08:00
6 changed files with 107 additions and 14 deletions

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Web.WebView2.Core;
using Microsoft.Win32;
using Snap.Hutao.Core.Setting;
@@ -14,6 +15,8 @@ namespace Snap.Hutao.Core;
[Injection(InjectAs.Singleton)]
internal sealed class RuntimeOptions
{
private readonly IServiceProvider serviceProvider;
private readonly Lazy<(Version Version, string UserAgent)> lazyVersionAndUserAgent = new(() =>
{
Version version = Package.Current.Id.Version.ToVersion();
@@ -83,8 +86,14 @@ internal sealed class RuntimeOptions
private readonly Lazy<string> lazyInstalledLocation = new(() => Package.Current.InstalledLocation.Path);
private readonly Lazy<string> lazyFamilyName = new(() => Package.Current.Id.FamilyName);
public RuntimeOptions(ILogger<RuntimeOptions> logger)
private bool isToastAvailable;
private bool isToastAvailableInitialized;
private object locker = new();
public RuntimeOptions(IServiceProvider serviceProvider, ILogger<RuntimeOptions> logger)
{
this.serviceProvider = serviceProvider;
AppLaunchTime = DateTimeOffset.UtcNow;
}
@@ -108,5 +117,19 @@ internal sealed class RuntimeOptions
public bool IsElevated { get => lazyElevated.Value; }
public bool IsToastAvailable
{
get
{
return LazyInitializer.EnsureInitialized(ref isToastAvailable, ref isToastAvailableInitialized, ref locker, () =>
{
return serviceProvider.GetRequiredService<ITaskContext>().InvokeOnMainThread(() =>
{
return ToastNotificationManagerCompat.CreateToastNotifier().Setting is Windows.UI.Notifications.NotificationSetting.Enabled;
});
});
}
}
public DateTimeOffset AppLaunchTime { get; }
}

View File

@@ -48,4 +48,45 @@ internal static class DispatcherQueueExtension
exceptionDispatchInfo?.Throw();
}
}
/// <summary>
/// 在调度器队列同步调用,直到执行结束,会持续阻塞当前线程
/// </summary>
/// <param name="dispatcherQueue">调度器队列</param>
/// <param name="action">执行的回调</param>
/// <typeparam name="T">返回类型</typeparam>
/// <returns>回调返回值</returns>
public static T Invoke<T>(this DispatcherQueue dispatcherQueue, Func<T> action)
{
T result = default!;
if (dispatcherQueue.HasThreadAccess)
{
return action();
}
ExceptionDispatchInfo? exceptionDispatchInfo = null;
using (ManualResetEventSlim blockEvent = new(false))
{
dispatcherQueue.TryEnqueue(() =>
{
try
{
result = action();
}
catch (Exception ex)
{
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
}
finally
{
blockEvent.Set();
}
});
blockEvent.Wait();
exceptionDispatchInfo?.Throw();
return result;
}
}
}

View File

@@ -12,6 +12,8 @@ internal interface ITaskContext
void InvokeOnMainThread(Action action);
T InvokeOnMainThread<T>(Func<T> action);
ThreadPoolSwitchOperation SwitchToBackgroundAsync();
DispatcherQueueSwitchOperation SwitchToMainThreadAsync();

View File

@@ -44,6 +44,12 @@ internal sealed class TaskContext : ITaskContext, ITaskContextUnsafe
dispatcherQueue.Invoke(action);
}
/// <inheritdoc/>
public T InvokeOnMainThread<T>(Func<T> action)
{
return dispatcherQueue.Invoke(action);
}
public void BeginInvokeOnMainThread(Action action)
{
dispatcherQueue.TryEnqueue(() => action());

View File

@@ -27,9 +27,15 @@ internal sealed partial class DailyNoteNotificationOperation
private readonly IGameServiceFacade gameService;
private readonly BindingClient bindingClient;
private readonly DailyNoteOptions options;
private readonly RuntimeOptions runtimeOptions;
public async ValueTask SendAsync(DailyNoteEntry entry)
{
if (!runtimeOptions.IsToastAvailable)
{
return;
}
if (entry.DailyNote is null)
{
return;

View File

@@ -492,7 +492,20 @@
</StackPanel>
</cwc:HeaderedContentControl>
<cwc:HeaderedContentControl
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsEnabled="{Binding RuntimeOptions.IsToastAvailable}">
<cwc:HeaderedContentControl.Header>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteNotificationHeader}"/>
</cwc:HeaderedContentControl.Header>
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<InfoBar
Title="胡桃的通知权限已被关闭"
IsClosable="False"
IsOpen="True"
Severity="Warning"
Visibility="{Binding RuntimeOptions.IsToastAvailable, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
<cwc:SettingsCard
Description="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeDescription}"
Header="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeHeader}"
@@ -505,6 +518,8 @@
HeaderIcon="{shcm:FontIcon Glyph=&#xEA8F;}">
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding DailyNoteOptions.IsReminderNotification, Mode=TwoWay}"/>
</cwc:SettingsCard>
</StackPanel>
</cwc:HeaderedContentControl>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteDataInteropHeader}"/>
<cwc:SettingsCard