Files
Dave Friedel c60453ea31 Fix incomplete functionality, nullable warnings, and async issues
Incomplete functionality fixes:
- SkiaEditor: Wire up Completed event to fire on focus lost
- X11InputMethodService: Remove unused _commitCallback field
- SkiaWebView: Set _isProperlyReparented when reparenting succeeds,
  use _lastMainX/_lastMainY to track main window position,
  add _isEmbedded guard to prevent double embedding

Nullable reference fixes:
- Easing: Reorder BounceOut before BounceIn (static init order)
- GestureManager: Use local command variable instead of re-accessing
- SkiaShell: Handle null Title with ?? operator
- GLibNative: Use null! for closure pattern
- LinuxProgramHost: Default title if null
- SkiaWebView.LoadHtml: Add null/empty check for html
- SystemThemeService: Initialize Colors with default values
- DeviceDisplayService/AppInfoService: Use var for nullable env vars
- EmailService: Add null check for message parameter

Async fixes:
- SkiaImage: Use _ = for fire-and-forget async calls
- SystemTrayService: Convert async method without await to sync Task

Reduces warnings from 156 to 133 (remaining are P/Invoke structs
and obsolete MAUI API usage)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 17:20:11 -05:00

115 lines
3.0 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Text;
using Microsoft.Maui.ApplicationModel.Communication;
namespace Microsoft.Maui.Platform.Linux.Services;
/// <summary>
/// Linux email implementation using mailto: URI.
/// </summary>
public class EmailService : IEmail
{
public bool IsComposeSupported => true;
public async Task ComposeAsync()
{
await ComposeAsync(new EmailMessage());
}
public async Task ComposeAsync(string subject, string body, params string[] to)
{
var message = new EmailMessage
{
Subject = subject,
Body = body
};
if (to != null && to.Length > 0)
{
message.To = new List<string>(to);
}
await ComposeAsync(message);
}
public async Task ComposeAsync(EmailMessage? message)
{
if (message == null)
throw new ArgumentNullException(nameof(message));
var mailto = BuildMailtoUri(message);
try
{
var startInfo = new ProcessStartInfo
{
FileName = "xdg-open",
Arguments = $"\"{mailto}\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using var process = Process.Start(startInfo);
if (process != null)
{
await process.WaitForExitAsync();
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Failed to open email client", ex);
}
}
private static string BuildMailtoUri(EmailMessage? message)
{
var sb = new StringBuilder("mailto:");
if (message == null) return sb.ToString();
// Add recipients
if (message.To?.Count > 0)
{
sb.Append(string.Join(",", message.To.Select(Uri.EscapeDataString)));
}
var queryParams = new List<string>();
// Add subject
if (!string.IsNullOrEmpty(message.Subject))
{
queryParams.Add($"subject={Uri.EscapeDataString(message.Subject)}");
}
// Add body
if (!string.IsNullOrEmpty(message.Body))
{
queryParams.Add($"body={Uri.EscapeDataString(message.Body)}");
}
// Add CC
if (message.Cc?.Count > 0)
{
queryParams.Add($"cc={string.Join(",", message.Cc.Select(Uri.EscapeDataString))}");
}
// Add BCC
if (message.Bcc?.Count > 0)
{
queryParams.Add($"bcc={string.Join(",", message.Bcc.Select(Uri.EscapeDataString))}");
}
if (queryParams.Count > 0)
{
sb.Append('?');
sb.Append(string.Join("&", queryParams));
}
return sb.ToString();
}
}