diff --git a/A11YTools.sln b/A11YTools.sln index d985a23..a1b101b 100644 --- a/A11YTools.sln +++ b/A11YTools.sln @@ -141,6 +141,8 @@ Global {DC93383E-CE5B-4B18-B3CD-39FFFA8B41DD}.Release|x86.Build.0 = Release|Any CPU {DC93383E-CE5B-4B18-B3CD-39FFFA8B41DD}.Release|x86.Deploy.0 = Release|Any CPU {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|Any CPU.ActiveCfg = Debug|x86 + {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|Any CPU.Build.0 = Debug|x86 + {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|Any CPU.Deploy.0 = Debug|x86 {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|ARM.ActiveCfg = Debug|ARM {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|ARM.Build.0 = Debug|ARM {365D53AD-E0C1-4119-8A2A-CCBF30E4440F}.Debug|ARM.Deploy.0 = Debug|ARM diff --git a/A11YTools/A11YTools.Android/A11YTools.Android.csproj b/A11YTools/A11YTools.Android/A11YTools.Android.csproj index ce6d04c..4f32562 100644 --- a/A11YTools/A11YTools.Android/A11YTools.Android.csproj +++ b/A11YTools/A11YTools.Android/A11YTools.Android.csproj @@ -58,6 +58,7 @@ + diff --git a/A11YTools/A11YTools.Android/AccessibleFocusBlockRenderer.cs b/A11YTools/A11YTools.Android/AccessibleFocusBlockRenderer.cs new file mode 100644 index 0000000..bd32436 --- /dev/null +++ b/A11YTools/A11YTools.Android/AccessibleFocusBlockRenderer.cs @@ -0,0 +1,72 @@ +using A11YTools.UWP; +using A11YTools.Views; +using Android.Content; +using Android.Views.Accessibility; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; + +[assembly: ExportRenderer(typeof(AccessibleFocusBlock), typeof(AccessibleFocusBlockRenderer))] +namespace A11YTools.UWP +{ + public class AccessibleFocusBlockRenderer : ViewRenderer + { + private bool isFocused = false; + + public AccessibleFocusBlockRenderer(Context context) : base(context) + { + + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + { + FocusChange += Control_FocusChange; + } + else + { + FocusChange -= Control_FocusChange; + } + } + + private void Control_FocusChange(object sender, FocusChangeEventArgs e) + { + if (e.HasFocus) + { + if (!isFocused) + Element?.OnAccessibilityGotFocus(); + + isFocused = true; + } + else + { + if (isFocused) + Element?.OnAccessibilityLostFocus(); + + isFocused = false; + } + } + + public override void OnInitializeAccessibilityEvent(AccessibilityEvent e) + { + base.OnInitializeAccessibilityEvent(e); + + if (e.EventType == EventTypes.ViewAccessibilityFocused) + { + if (!isFocused) + Element?.OnAccessibilityGotFocus(); + + isFocused = true; + } + else if (e.EventType == EventTypes.ViewAccessibilityFocusCleared) + { + if (isFocused) + Element?.OnAccessibilityLostFocus(); + + isFocused = false; + } + } + } +} \ No newline at end of file diff --git a/A11YTools/A11YTools.Android/AndroidA11yService.cs b/A11YTools/A11YTools.Android/AndroidA11yService.cs index bfc649e..495b79c 100644 --- a/A11YTools/A11YTools.Android/AndroidA11yService.cs +++ b/A11YTools/A11YTools.Android/AndroidA11yService.cs @@ -65,7 +65,7 @@ public void SetFocus(VisualElement element) var renderer = Platform.GetRenderer(element); var view = element.GetViewForAccessibility(); - view?.SendAccessibilityEvent(EventTypes.ViewAccessibilityFocused); + view?.SendAccessibilityEvent(EventTypes.ViewFocused); } public void SetAnnouncement(string text) diff --git a/A11YTools/A11YTools.UWP/A11YTools.UWP.csproj b/A11YTools/A11YTools.UWP/A11YTools.UWP.csproj index 1a165f8..968f7cd 100644 --- a/A11YTools/A11YTools.UWP/A11YTools.UWP.csproj +++ b/A11YTools/A11YTools.UWP/A11YTools.UWP.csproj @@ -90,6 +90,7 @@ true + App.xaml @@ -98,6 +99,7 @@ + @@ -162,4 +164,9 @@ 14.0 + + + + + \ No newline at end of file diff --git a/A11YTools/A11YTools.UWP/AccessibleFocusBlockRenderer.cs b/A11YTools/A11YTools.UWP/AccessibleFocusBlockRenderer.cs new file mode 100644 index 0000000..57c4953 --- /dev/null +++ b/A11YTools/A11YTools.UWP/AccessibleFocusBlockRenderer.cs @@ -0,0 +1,81 @@ +using A11YTools.UWP; +using A11YTools.Views; +using System.ComponentModel; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Xamarin.Forms.Platform.UWP; + +[assembly: ExportRenderer(typeof(AccessibleFocusBlock), typeof(AccessibleFocusBlockRenderer))] +namespace A11YTools.UWP +{ + public class AccessibleFocusBlockRenderer : ViewRenderer + { + private Windows.UI.Xaml.Controls.Button accessibilityHiddenBtn = null; + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.OldElement != null && accessibilityHiddenBtn != null) + { + accessibilityHiddenBtn.GotFocus -= AccessibilityHiddenBtn_GotFocus; + accessibilityHiddenBtn.LostFocus -= AccessibilityHiddenBtn_LostFocus; + accessibilityHiddenBtn = null; + + SetNativeControl(null); + } + + if (e.NewElement != null) + { + accessibilityHiddenBtn = new Windows.UI.Xaml.Controls.Button(); + accessibilityHiddenBtn.IsTabStop = e.NewElement.IsTabStop; + accessibilityHiddenBtn.IsHitTestVisible = false; + accessibilityHiddenBtn.IsEnabled = false; + accessibilityHiddenBtn.Visibility = e.NewElement.IsTabStop && e.NewElement.IsEnabled && e.NewElement.IsVisible ? Visibility.Visible : Visibility.Collapsed; + accessibilityHiddenBtn.UseSystemFocusVisuals = false; + accessibilityHiddenBtn.GotFocus += AccessibilityHiddenBtn_GotFocus; + accessibilityHiddenBtn.LostFocus += AccessibilityHiddenBtn_LostFocus; + Canvas.SetZIndex(accessibilityHiddenBtn, -1); + + SetNativeControl(accessibilityHiddenBtn); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing && Control != null) + { + accessibilityHiddenBtn.GotFocus -= AccessibilityHiddenBtn_GotFocus; + accessibilityHiddenBtn.LostFocus -= AccessibilityHiddenBtn_LostFocus; + accessibilityHiddenBtn = null; + + SetNativeControl(null); + } + base.Dispose(disposing); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + switch (e.PropertyName) + { + case nameof(Element.IsEnabled): + case nameof(Element.IsTabStop): + case nameof(Element.IsVisible): + accessibilityHiddenBtn.Visibility = Element.IsTabStop && Element.IsEnabled && Element.IsVisible ? Visibility.Visible : Visibility.Collapsed; + break; + } + } + + private void AccessibilityHiddenBtn_GotFocus(object sender, RoutedEventArgs e) + { + Element?.OnAccessibilityGotFocus(); + } + + private void AccessibilityHiddenBtn_LostFocus(object sender, RoutedEventArgs e) + { + Element?.OnAccessibilityLostFocus(); + } + } +} \ No newline at end of file diff --git a/A11YTools/A11YTools.UWP/UWPA11yService.cs b/A11YTools/A11YTools.UWP/UWPA11yService.cs new file mode 100644 index 0000000..27e88b0 --- /dev/null +++ b/A11YTools/A11YTools.UWP/UWPA11yService.cs @@ -0,0 +1,52 @@ +using A11YTools.UWP; +using A11YTools.Views; +using System; +using System.Diagnostics; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Input; +using Xamarin.Forms; +using Xamarin.Forms.Platform.UWP; + +[assembly: Dependency(typeof(UWPA11yService))] +namespace A11YTools.UWP +{ + public class UWPA11yService : IA11yService + { + public void SetAnnouncement(string text) + { + throw new NotImplementedException(); + } + + public void SetControlType(VisualElement element, ControlType controlType) + { + throw new NotImplementedException(); + } + + public async void SetFocus(VisualElement element) + { + var containerElement = Platform.GetRenderer(element).ContainerElement; + + FrameworkElement view = null; + if (containerElement is AccessibleFocusBlockRenderer) + { + view = ((Xamarin.Forms.Platform.UWP.VisualElementRenderer)containerElement).Control; + } + if (containerElement is ButtonRenderer) + { + view = ((VisualElementRenderer)containerElement).Control; + } + + if (view == null) + throw new NotImplementedException(); + + var result = await FocusManager.TryFocusAsync(view, FocusState.Programmatic); + + Debug.WriteLine($"Set focus result {result.Succeeded}"); + } + + public void SetIsClickable(VisualElement element, bool isClickable, Action clickActionThatOnlyRunsOnAndroid) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/A11YTools/A11YTools/Views/AccessibleFocusBlock.cs b/A11YTools/A11YTools/Views/AccessibleFocusBlock.cs new file mode 100644 index 0000000..88a92ea --- /dev/null +++ b/A11YTools/A11YTools/Views/AccessibleFocusBlock.cs @@ -0,0 +1,17 @@ +using Xamarin.Forms; + +namespace A11YTools.Views +{ + public class AccessibleFocusBlock : ContentView + { + public virtual void OnAccessibilityGotFocus() + { + + } + + public virtual void OnAccessibilityLostFocus() + { + + } + } +} \ No newline at end of file diff --git a/A11YTools/A11YTools/Views/SetFocus.xaml b/A11YTools/A11YTools/Views/SetFocus.xaml index 55044e5..aea26c0 100644 --- a/A11YTools/A11YTools/Views/SetFocus.xaml +++ b/A11YTools/A11YTools/Views/SetFocus.xaml @@ -1,15 +1,45 @@  + x:Class="A11YTools.Views.SetFocus" + xmlns:local="clr-namespace:A11YTools.Views"> + - -