Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Actions/DoSpeechAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using ClassIsland.Core.Abstractions.Automation;
using ClassIsland.Core.Abstractions.Services.SpeechService;
using ClassIsland.Core.Attributes;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using SystemTools.Settings;

namespace SystemTools.Actions
{
[ActionInfo("SystemTools.DoSpeech", "语音播报", "\uE5C7", false)]
public class DoSpeechAction(ILogger<DoSpeechAction> logger, ISpeechService speechService) : ActionBase<DoSpeechSettings>
{
private readonly ILogger<DoSpeechAction> _logger = logger;
private readonly ISpeechService _speechService = speechService;

protected override async Task OnInvoke()
{
_logger.LogDebug("DoSpeechAction OnInvoke 开始");
var text = Settings?.Text;
if (string.IsNullOrWhiteSpace(text))
{
_logger.LogError("语音播报内容不能为空");
throw new InvalidOperationException("语音播报内容不能为空");
}
else
{
_speechService.EnqueueSpeechQueue(text);
}
await base.OnInvoke();
_logger.LogDebug("DoSpeechAction OnInvoke 完成");
}
}
}
47 changes: 31 additions & 16 deletions Actions/ImmediateRestartAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,53 @@
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Win32;

namespace SystemTools.Actions;

[ActionInfo("SystemTools.ImmediateRestart", "立即重启", "\uE0BD", false)]
public class ImmediateRestartAction(ILogger<ImmediateRestartAction> logger) : ActionBase
{
private readonly ILogger<ImmediateRestartAction> _logger = logger;

[DllImport("ntdll.dll", EntryPoint = "RtlAdjustPrivilege")] // 此API无法使用CsWin32包生成,因为这是个不公开的函数
internal static extern uint W32_RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
internal const int SE_SHUTDOWN_PRIVILEGE = 19;
protected override async Task OnInvoke()
{
_logger.LogDebug("ImmediateRestartAction OnInvoke 开始");

try
if(W32_RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, false, out _) != 0x00000000) // STATUS_SUCCESS
{
var psi = new ProcessStartInfo
{
FileName = "shutdown",
Arguments = "-r -t 0",
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};

Process.Start(psi);
_logger.LogInformation("已执行立即重启命令");
_logger.LogError("执行立即重启失败:获取关机权限失败");
throw new InvalidOperationException("执行立即重启失败");
}
catch (Exception ex)
if(!PInvoke.ExitWindowsEx(Windows.Win32.System.Shutdown.EXIT_WINDOWS_FLAGS.EWX_REBOOT,Windows.Win32.System.Shutdown.SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED))
{
_logger.LogError(ex, "执行立即重启失败");
throw;
_logger.LogError("执行立即重启失败");
throw new InvalidOperationException("执行立即重启失败");
}
else { _logger.LogInformation("已执行立即重启命令"); }
//try
//{
// var psi = new ProcessStartInfo
// {
// FileName = "shutdown",
// Arguments = "-r -t 0",
// UseShellExecute = false,
// CreateNoWindow = true,
// WindowStyle = ProcessWindowStyle.Hidden
// };

// Process.Start(psi);
// _logger.LogInformation("已执行立即重启命令");
//}
//catch (Exception ex)
//{
// _logger.LogError(ex, "执行立即重启失败");
// throw;
//}

await base.OnInvoke();
_logger.LogDebug("ImmediateRestartAction OnInvoke 完成");
Expand Down
47 changes: 31 additions & 16 deletions Actions/ImmediateShutdownAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,53 @@
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Win32;

namespace SystemTools.Actions;

[ActionInfo("SystemTools.ImmediateShutdown", "立即关机", "\uEDE9", false)]
public class ImmediateShutdownAction(ILogger<ImmediateShutdownAction> logger) : ActionBase
{
private readonly ILogger<ImmediateShutdownAction> _logger = logger;

[DllImport("ntdll.dll", EntryPoint = "RtlAdjustPrivilege")] // 此API无法使用CsWin32包生成,因为这是个不公开的函数
internal static extern uint W32_RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
internal const int SE_SHUTDOWN_PRIVILEGE = 19;
protected override async Task OnInvoke()
{
_logger.LogDebug("ImmediateShutdownAction OnInvoke 开始");

try
if (W32_RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, false, out _) != 0x00000000) // STATUS_SUCCESS
{
var psi = new ProcessStartInfo
{
FileName = "shutdown",
Arguments = "-s -t 0",
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};

Process.Start(psi);
_logger.LogInformation("已执行立即关机命令");
_logger.LogError("执行立即关机失败:获取关机权限失败");
throw new InvalidOperationException("执行立即关机失败");
}
catch (Exception ex)
if (!PInvoke.ExitWindowsEx(Windows.Win32.System.Shutdown.EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | Windows.Win32.System.Shutdown.EXIT_WINDOWS_FLAGS.EWX_POWEROFF, Windows.Win32.System.Shutdown.SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED))
{
_logger.LogError(ex, "执行立即关机失败");
throw;
_logger.LogError("执行立即关机失败");
throw new InvalidOperationException("执行立即关机失败");
}
else { _logger.LogInformation("已执行立即关机命令"); }
//try
//{
// var psi = new ProcessStartInfo
// {
// FileName = "shutdown",
// Arguments = "-s -t 0",
// UseShellExecute = false,
// CreateNoWindow = true,
// WindowStyle = ProcessWindowStyle.Hidden
// };

// Process.Start(psi);
// _logger.LogInformation("已执行立即关机命令");
//}
//catch (Exception ex)
//{
// _logger.LogError(ex, "执行立即关机失败");
// throw;
//}

await base.OnInvoke();
_logger.LogDebug("ImmediateShutdownAction OnInvoke 完成");
Expand Down
40 changes: 40 additions & 0 deletions Controls/DoSpeechSettingsControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Avalonia.Controls;
using ClassIsland.Core.Abstractions.Controls;
using SystemTools.Settings;

namespace SystemTools.Controls;

public class DoSpeechSettingsControl : ActionSettingsControlBase<DoSpeechSettings>
{
private readonly TextBox _textBox;

public DoSpeechSettingsControl()
{
var panel = new StackPanel { Spacing = 8, Margin = new(10) };

panel.Children.Add(new TextBlock
{
Text = "语音播报",
FontWeight = Avalonia.Media.FontWeight.Bold,
FontSize = 14
});

_textBox = new TextBox
{
Watermark = "请输入要播报的文字",
AcceptsReturn = true,
Height = 120,
Width = 420
};
_textBox.TextChanged += (_, _) => { Settings.Text = _textBox.Text ?? string.Empty; };
panel.Children.Add(_textBox);

Content = panel;
}

protected override void OnInitialized()
{
base.OnInitialized();
_textBox.Text = Settings.Text;
}
}
3 changes: 2 additions & 1 deletion NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ EnumWindows
GetClassName
GetWindowText
DEV_BROADCAST_DEVICEINTERFACE_W
DEV_BROADCAST_HDR
DEV_BROADCAST_HDR
ExitWindowsEx
6 changes: 5 additions & 1 deletion Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
_logger?.LogWarning("[SystemTools]FFmpeg 功能已自动关闭:缺少依赖文件 ffmpeg.exe。");
}

if (GlobalConstants.MainConfig.Data.EnableFaceRecognition)

Check warning on line 132 in Plugin.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
{
if (_faceRecognitionRegistered)
{
Expand Down Expand Up @@ -278,6 +278,8 @@
RegisterActionIfEnabled<BackgroundPlayAudioAction, BackgroundPlayAudioSettingsControl>(services, config,
"SystemTools.BackgroundPlayAudio");
RegisterActionIfEnabled<ShowDesktopAction>(services, config, "SystemTools.ShowDesktop");
// 语音播报
RegisterActionIfEnabled<DoSpeechAction, DoSpeechSettingsControl>(services, config, "SystemTools.DoSpeech");

// 悬浮窗设置
if (config.EnableFloatingWindowFeature)
Expand Down Expand Up @@ -514,7 +516,7 @@

if (HasAnyActionEnabled(config, "SystemTools.ClearAllNotifications", "SystemTools.RestartAsAdmin",
"SystemTools.LoadTemporaryClassPlan", "SystemTools.OpenAppSettings",
"SystemTools.OpenProfileEditor", "SystemTools.OpenClassSwapWindow"))
"SystemTools.OpenProfileEditor", "SystemTools.OpenClassSwapWindow","SystemTools.DoSpeech"))
{
IActionService.ActionMenuTree["SystemTools 行动"].Add(new ActionMenuTreeGroup("ClassIsland…", "\uE5CB"));
BuildClassIslandMenu(config);
Expand Down Expand Up @@ -861,6 +863,8 @@
items.Add(new ActionMenuTreeItem("SystemTools.OpenProfileEditor", "打开档案编辑", "\uE699"));
if (config.IsActionEnabled("SystemTools.OpenClassSwapWindow"))
items.Add(new ActionMenuTreeItem("SystemTools.OpenClassSwapWindow", "打开换课窗口", "\uE13B"));
if (config.IsActionEnabled("SystemTools.DoSpeech"))
items.Add(new ActionMenuTreeItem("SystemTools.DoSpeech", "语音播报", "\uE5C7"));

if (items.Count > 0)
{
Expand Down
9 changes: 9 additions & 0 deletions Settings/DoSpeechSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace SystemTools.Settings;

public class DoSpeechSettings
{
[JsonPropertyName("text")]
public string Text { get; set; } = string.Empty;
}
1 change: 1 addition & 0 deletions SettingsPage/SystemToolsSettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ public void InitializeFeatureItems()
("SystemTools.OpenAppSettings", "打开应用设置", "ClassIsland"),
("SystemTools.OpenProfileEditor", "打开档案编辑", "ClassIsland"),
("SystemTools.OpenClassSwapWindow", "打开换课窗口", "ClassIsland"),
("SystemTools.DoSpeech", "语音播报", "ClassIsland"),
};

if (Settings.EnableFloatingWindowFeature)
Expand Down
2 changes: 2 additions & 0 deletions manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ icon: icon.png
repoOwner: Programmer-MrWang
repoName: SystemTools
assetsRoot: main
supportedOSPlatforms:
- Windows
Loading