Forum Xamarin Xamarin.Forms

Xamarin Forms - Accessibility - Screen reader to read each page name/header on page load/appearing

I am currently developing a Xamarin Forms application with strict requirements on the Accessibility. We have implemented the basic Accessibility using the AutomationProperties's Name and HelpText attributes in most of our ContentPages (Page) and PopupPages. However, the current implementation does not meet the standards we look for.

For example, we want the screen name to be read (when VoiceOver or TalkBack is ON) when the user navigates to each page or say when a new page appears. There is no easy way to do it (as far as I know) from the Forms app directly, without using Custom Renderers. I was able to achieve it in iOS by defining the AutomationProperties.Name on the ContentPage level and using it in the custom PageRenderer's OnAppearing event. But, the same does not work in Android for some reason.

Following is the Custom Renderer I implemented in iOS,

[assembly: ExportRenderer(typeof(ContentPage), typeof(IosPageRenderer))]
namespace Project.iOS.CustomRenderers
{
    /// <summary>
    /// This renderer is invoked for all pages in the iOS project. It applies changes to the page as required by the designers.
    /// </summary>
    public class IosPageRenderer : PageRenderer
    {
        /// <summary>
        /// Delegate method which gets invoked on view appearing.
        /// </summary>
        /// <param name="animated"></param>
        public override void ViewDidAppear(bool animated)
        {
            /// To read out the current screen on loading it.
            var accessibilityScreenName = AutomationProperties.GetName(this.Element);
            if (!string.IsNullOrEmpty(accessibilityScreenName))
                UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, new NSString($"{accessibilityScreenName} screen"));

            base.ViewDidAppear(animated);
        }
    }
}

This makes the application to announce the screen name every time a ContentPage appears.

How can I achieve the same in Android and UWP? Our main focus is iOS and Android, so if anything on Android would be so much appreciated.

I tried to implement a similar Page renderer for Android as well, but it does not work right now, Maybe my implementation is wrong, anyone have experience with this, please help.

[assembly: ExportRenderer(typeof(ContentPage), typeof(AndroidPageRenderer))]
namespace Project.Droid.CustomRenderers
{
    /// <summary>
    /// This renderer is invoked for all pages in the Android project.
    /// It applies changes to the page to make the accessibility working for screen change..
    /// </summary>
    public class AndroidPageRenderer : PageRenderer
    {
        /// <summary>
        /// Creates new instance of the renderer.
        /// </summary>
        /// <param name="context">The activity context.</param>
        public AndroidPageRenderer(Android.Content.Context context) : base(context)
        {
        }

        protected override void OnAttachedToWindow()
        {
            base.OnAttachedToWindow();

            var accessibilityScreenName = AutomationProperties.GetName(this.Element);
            if (!string.IsNullOrEmpty(accessibilityScreenName) && this.ViewGroup != null)
                this.ViewGroup.AnnounceForAccessibility($"{accessibilityScreenName} screen");

        //SendAccessibilityEvent(Android.Views.Accessibility.EventTypes.WindowContentChanged);
        }
    }
}

Anything on this would be highly appreciated.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    I tried your renderer on my side. It worked.
    However, it will only be triggered for the new page. When we come back to the previous page, OnAttachedToWindow won't be called.
    We could achieve this using a dependency service.
    Firstly, create an interface in Forms:

    public interface IAccessibilityManager
    {
        void sendAccessibility(string speakText);
    }
    

    Implement it in Android:

    [assembly: Dependency(typeof(AndroidAccessibility))]
    namespace Sample.Droid
    {
        public class AndroidAccessibility : IAccessibilityManager
        {
            public void sendAccessibility(string speakText)
            {
                AccessibilityManager manager = (AccessibilityManager)Android.App.Application.Context.GetSystemService(Context.AccessibilityService);
                if (manager.IsEnabled)
                {
                    AccessibilityEvent e = AccessibilityEvent.Obtain();
                    e.EventType = EventTypes.Announcement;
                    e.Text.Add(new Java.Lang.String(speakText));
                    manager.SendAccessibilityEvent(e);
                }
            }
        }
    }
    

    Fire this dependency service in any lifecycle event as you want:

    protected override void OnAppearing()
    {
        base.OnAppearing();
    
        DependencyService.Get<IAccessibilityManager>().sendAccessibility("MainPage screen");
    }
    
  • skadookkunnanskadookkunnan Member ✭✭

    @LandLu Thank you for the response and answer.

    Did the android app clearly announce the page with "{given automationproperties.name} screen"? It used to announce the page (but not with the 'screen' in Android) if there is a Title property defined for the ContentPage.

    And Yes, thank you for that solution on the page appearing on back navigation. I suppose I would have to create a base page and implement it there instead of adding this to all individual pages in my application.

    I will try and keep you posted on this thread.

    Sincerely,
    Sagar S. Kadookkunnan

  • LandLuLandLu Member, Xamarin Team Xamurai

    (but not with the 'screen' in Android)

    I could hear it when using renderer but not very clearly.

    I will try and keep you posted on this thread.

    waiting for your update.

Sign In or Register to comment.