Accessibility announcement - .NET MAUI

In MAUI, there is built-in support for posting accessibility announcements to the TalkBack or VoiceOver engine.

SemanticScreenReader.Default.Announce("Appt announcement");

If you want to use attributed strings or add a delay, you can create your own service, such as IA11yService.

IA11YService interface:

public interface IA11YService
{
  bool IsInVoiceOverMode { get; }
  Task Speak(string? text, int pauseInMs = 0);
}

IA11YService for Android:

private static AccessibilityManager? AccessibilityManager => Android.App.Application.Context.GetSystemService(Android.Content.Context.AccessibilityService) as AccessibilityManager;

public bool IsInVoiceOverMode => AccessibilityManager is { IsEnabled: true, IsTouchExplorationEnabled: true };

public async Task Speak(string? text, int pauseInMs = 0)
{
  if (IsInVoiceOverMode && !string.IsNullOrEmpty(text))
  {
    if (pauseInMs > 0)
      await Task.Delay(pauseInMs);
    try
    {
      SemanticScreenReader.Announce(text);
    }
    catch (Exception e)
    {
      // Sometimes we get an exception: MauiContext must be set on parent
      System.Diagnostics.Debug.WriteLine($"A11YService.Android.Speak exception: {e.Message}");
    }
  }
}

IA11YService for iOS:

public bool IsInVoiceOverMode => UIAccessibility.IsVoiceOverRunning;

public Task Speak(string? text, int pauseInMs = 0)
{
  if (IsInVoiceOverMode && !string.IsNullOrEmpty(text))
  {
    if (pauseInMs > 0)
    {
      var dict = new NSMutableDictionary
      {
        { UIView.SpeechAttributeQueueAnnouncement, new NSString("Yes") }
      };
      UIAccessibility.PostNotification(UIAccessibilityPostNotification.Announcement, new NSAttributedString(str: text, attributes: dict));
    }
    else
      UIAccessibility.PostNotification(UIAccessibilityPostNotification.Announcement, new NSString(text));
  }
  return Task.CompletedTask;
}