Add inactivity safeguard and speaker diagnostics
PR and Push Build/Test / build-and-test (push) Failing after 8m31s

This commit is contained in:
2026-06-02 13:16:02 +02:00
parent c56ecb6ab3
commit 0e9d525b63
24 changed files with 1780 additions and 117 deletions
@@ -0,0 +1,74 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using MeetingAssistant.Recording;
namespace MeetingAssistant.Taskbar;
[SupportedOSPlatform("windows")]
internal static class TaskbarIconRenderer
{
private const int IconSize = 16;
private const float GlyphPixelGridOffsetX = -0.5f;
public static Icon CreateIcon(RecordingProcessState state)
{
using var bitmap = RenderBitmap(state);
var handle = bitmap.GetHicon();
try
{
return (Icon)Icon.FromHandle(handle).Clone();
}
finally
{
DestroyIcon(handle);
}
}
internal static Bitmap RenderBitmap(RecordingProcessState state)
{
var (color, text) = state switch
{
RecordingProcessState.Recording => (Color.FromArgb(220, 38, 38), "R"),
RecordingProcessState.Summarizing => (Color.FromArgb(37, 99, 235), "S"),
_ => (Color.FromArgb(75, 85, 99), "I")
};
var bitmap = new Bitmap(IconSize, IconSize, PixelFormat.Format32bppArgb);
using var graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.Transparent);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
using var brush = new SolidBrush(color);
graphics.FillEllipse(brush, 1, 1, 14, 14);
DrawCenteredGlyph(graphics, text);
return bitmap;
}
private static void DrawCenteredGlyph(Graphics graphics, string text)
{
using var path = new GraphicsPath();
using var format = StringFormat.GenericTypographic;
path.AddString(
text,
FontFamily.GenericSansSerif,
(int)FontStyle.Bold,
9,
Point.Empty,
format);
var bounds = path.GetBounds();
using var transform = new Matrix();
transform.Translate(
(IconSize - bounds.Width) / 2 - bounds.Left + GlyphPixelGridOffsetX,
(IconSize - bounds.Height) / 2 - bounds.Top);
path.Transform(transform);
using var textBrush = new SolidBrush(Color.White);
graphics.FillPath(textBrush, path);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
}
@@ -1,6 +1,5 @@
#if WINDOWS
using System.Drawing;
using System.Runtime.InteropServices;
using H.NotifyIcon.Core;
using MeetingAssistant.LaunchProfiles;
using MeetingAssistant.Recording;
@@ -47,7 +46,7 @@ public sealed class UnoTaskbarIconService : IHostedService, IDisposable
var menu = BuildMenu();
currentMenuSignature = BuildMenuSignature(menu);
currentIcon = CreateIcon(menu.State);
currentIcon = TaskbarIconRenderer.CreateIcon(menu.State);
currentState = menu.State;
trayIcon = new TrayIconWithContextMenu(TrayIconId)
{
@@ -126,7 +125,7 @@ public sealed class UnoTaskbarIconService : IHostedService, IDisposable
if (currentState != menu.State)
{
var previousIcon = currentIcon;
var nextIcon = CreateIcon(menu.State);
var nextIcon = TaskbarIconRenderer.CreateIcon(menu.State);
if (!trayIcon.UpdateIcon(nextIcon.Handle))
{
logger.LogWarning("Taskbar icon update returned false for state {State}", menu.State);
@@ -248,45 +247,5 @@ public sealed class UnoTaskbarIconService : IHostedService, IDisposable
menu.Items.Select(item => $"{item.Action}:{item.ProfileName}:{item.Text}"));
}
private static Icon CreateIcon(RecordingProcessState state)
{
var (color, text) = state switch
{
RecordingProcessState.Recording => (Color.FromArgb(220, 38, 38), "R"),
RecordingProcessState.Summarizing => (Color.FromArgb(37, 99, 235), "S"),
_ => (Color.FromArgb(75, 85, 99), "I")
};
using var bitmap = new Bitmap(16, 16);
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Transparent);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
using var brush = new SolidBrush(color);
graphics.FillEllipse(brush, 1, 1, 14, 14);
using var font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold, GraphicsUnit.Pixel);
using var textBrush = new SolidBrush(Color.White);
var size = graphics.MeasureString(text, font);
graphics.DrawString(
text,
font,
textBrush,
(16 - size.Width) / 2,
(16 - size.Height) / 2);
}
var handle = bitmap.GetHicon();
try
{
return (Icon)Icon.FromHandle(handle).Clone();
}
finally
{
DestroyIcon(handle);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
}
#endif