diff options
author | Samantha Houts <samantha@teamredwall.com> | 2017-01-31 11:49:15 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-31 11:49:15 -0800 |
commit | ae59382c9046501edb37882ad1c065aacce60319 (patch) | |
tree | 3578ce8e0396a38aeb8323d4051f50a19a5340fb /Xamarin.Forms.Platform.Android | |
parent | 23d228039acc504049f6a5153f5839d4c714930a (diff) | |
download | xamarin-forms-ae59382c9046501edb37882ad1c065aacce60319.tar.gz xamarin-forms-ae59382c9046501edb37882ad1c065aacce60319.tar.bz2 xamarin-forms-ae59382c9046501edb37882ad1c065aacce60319.zip |
[All] Basic Accessibility Support (#713)
* [Core] Add accessibility properties
* [Controls] Add accessibility gallery
* [iOS] Implement accessibility properties
* [Android] Implement accessibilty properties
* [Win] Implement accessibility properties
* [Win] Select ListView item on selected for a11y
* Update docs
* TODO: macOS accessibility
* [iOS] Fix failing UI Tests
Diffstat (limited to 'Xamarin.Forms.Platform.Android')
8 files changed, 211 insertions, 1 deletions
diff --git a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs index 1092f767..6954654b 100644 --- a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs +++ b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs @@ -21,6 +21,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat float _defaultElevation = -1f; float _defaultCornerRadius = -1f; + int? _defaultLabelFor; bool _clickable; bool _disposed; @@ -106,6 +107,14 @@ namespace Xamarin.Forms.Platform.Android.AppCompat ContentDescription = Element.AutomationId; } + void IVisualElementRenderer.SetLabelFor(int? id) + { + if (_defaultLabelFor == null) + _defaultLabelFor = LabelFor; + + LabelFor = (int)(id ?? _defaultLabelFor); + } + VisualElementTracker IVisualElementRenderer.Tracker => _visualElementTracker; void IVisualElementRenderer.UpdateLayout() diff --git a/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs index 757a98b4..85ab6b73 100644 --- a/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs +++ b/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs @@ -165,6 +165,10 @@ namespace Xamarin.Forms.Platform.Android.AppCompat _tracker = new VisualElementTracker(this); } + void IVisualElementRenderer.SetLabelFor(int? id) + { + } + VisualElementTracker IVisualElementRenderer.Tracker => _tracker; void IVisualElementRenderer.UpdateLayout() diff --git a/Xamarin.Forms.Platform.Android/IVisualElementRenderer.cs b/Xamarin.Forms.Platform.Android/IVisualElementRenderer.cs index 99393516..da565ce2 100644 --- a/Xamarin.Forms.Platform.Android/IVisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.Android/IVisualElementRenderer.cs @@ -17,6 +17,9 @@ namespace Xamarin.Forms.Platform.Android SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint); void SetElement(VisualElement element); + + void SetLabelFor(int? id); + void UpdateLayout(); } }
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs index ec9c620e..d18781a7 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs @@ -287,6 +287,10 @@ namespace Xamarin.Forms.Platform.Android SetDrawerLockMode(_page.IsGestureEnabled ? LockModeUnlocked : LockModeLockedClosed); } + void IVisualElementRenderer.SetLabelFor(int? id) + { + } + void SetLockMode(int lockMode) { if (_currentLockMode != lockMode) diff --git a/Xamarin.Forms.Platform.Android/Renderers/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/ScrollViewRenderer.cs index 6db51c23..a2e3ddb3 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/ScrollViewRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/ScrollViewRenderer.cs @@ -328,6 +328,10 @@ namespace Xamarin.Forms.Platform.Android } } + void IVisualElementRenderer.SetLabelFor(int? id) + { + } + void UpdateBackgroundColor() { SetBackgroundColor(Element.BackgroundColor.ToAndroid(Color.Transparent)); diff --git a/Xamarin.Forms.Platform.Android/ViewRenderer.cs b/Xamarin.Forms.Platform.Android/ViewRenderer.cs index c4bd3fa0..31a9c282 100644 --- a/Xamarin.Forms.Platform.Android/ViewRenderer.cs +++ b/Xamarin.Forms.Platform.Android/ViewRenderer.cs @@ -19,6 +19,9 @@ namespace Xamarin.Forms.Platform.Android } ViewGroup _container; + string _defaultContentDescription; + bool? _defaultFocusable; + string _defaultHint; bool _disposed; EventHandler<VisualElement.FocusRequestArgs> _focusChangeHandler; @@ -66,7 +69,7 @@ namespace Xamarin.Forms.Platform.Android { if (Control == null) return (base.GetDesiredSize(widthConstraint, heightConstraint)); - + AView view = _container == this ? (AView)Control : _container; view.Measure(widthConstraint, heightConstraint); @@ -124,6 +127,8 @@ namespace Xamarin.Forms.Platform.Android if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) UpdateIsEnabled(); + else if (e.PropertyName == Accessibility.LabeledByProperty.PropertyName) + SetLabeledBy(); } protected override void OnLayout(bool changed, int l, int t, int r, int b) @@ -155,6 +160,79 @@ namespace Xamarin.Forms.Platform.Android } } + protected override void SetContentDescription() + { + if (Control == null) + { + base.SetContentDescription(); + return; + } + + if (Element == null) + return; + + if (SetHint()) + return; + + if (_defaultContentDescription == null) + _defaultContentDescription = Control.ContentDescription; + + var elemValue = string.Join(" ", (string)Element.GetValue(Accessibility.NameProperty), (string)Element.GetValue(Accessibility.HintProperty)); + + if (!string.IsNullOrWhiteSpace(elemValue)) + Control.ContentDescription = elemValue; + else + Control.ContentDescription = _defaultContentDescription; + } + + protected override void SetFocusable() + { + if (Control == null) + { + base.SetFocusable(); + return; + } + + if (Element == null) + return; + + if (!_defaultFocusable.HasValue) + _defaultFocusable = Control.Focusable; + + Control.Focusable = (bool)((bool?)Element.GetValue(Accessibility.IsInAccessibleTreeProperty) ?? _defaultFocusable); + } + + protected override bool SetHint() + { + if (Control == null) + { + return base.SetHint(); + } + + if (Element == null) + return false; + + var textView = Control as global::Android.Widget.TextView; + if (textView == null) + return false; + + // Let the specified Title/Placeholder take precedence, but don't set the ContentDescription (won't work anyway) + if (((Element as Picker)?.Title ?? (Element as Entry)?.Placeholder ?? (Element as EntryCell)?.Placeholder) != null) + return true; + + if (_defaultHint == null) + _defaultHint = textView.Hint; + + var elemValue = string.Join(". ", (string)Element.GetValue(Accessibility.NameProperty), (string)Element.GetValue(Accessibility.HintProperty)); + + if (!string.IsNullOrWhiteSpace(elemValue)) + textView.Hint = elemValue; + else + textView.Hint = _defaultHint; + + return true; + } + protected void SetNativeControl(TNativeView control) { SetNativeControl(control, this); @@ -219,6 +297,25 @@ namespace Xamarin.Forms.Platform.Android Control.OnFocusChangeListener = this; UpdateIsEnabled(); + SetLabeledBy(); + } + + void SetLabeledBy() + { + if (Element == null || Control == null) + return; + + var elemValue = (VisualElement)Element.GetValue(Accessibility.LabeledByProperty); + + if (elemValue != null) + { + var id = Control.Id; + if (id == -1) + id = Control.Id = FormsAppCompatActivity.GetUniqueId(); + + var renderer = elemValue?.GetRenderer(); + renderer?.SetLabelFor(id); + } } void UpdateIsEnabled() diff --git a/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs b/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs index 13f2fffa..800a967e 100644 --- a/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs +++ b/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs @@ -1,7 +1,19 @@ +using System; + namespace Xamarin.Forms.Platform.Android { public static class VisualElementExtensions { + public static IVisualElementRenderer GetRenderer(this VisualElement self) + { + if (self == null) + throw new ArgumentNullException("self"); + + IVisualElementRenderer renderer = Platform.GetRenderer(self); + + return renderer; + } + public static bool ShouldBeMadeClickable(this View view) { var shouldBeClickable = false; diff --git a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs index 19b05e2e..46bab02e 100644 --- a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs @@ -23,6 +23,10 @@ namespace Xamarin.Forms.Platform.Android VisualElementRendererFlags _flags = VisualElementRendererFlags.AutoPackage | VisualElementRendererFlags.AutoTrack; + string _defaultContentDescription; + bool? _defaultFocusable; + string _defaultHint; + int? _defaultLabelFor; InnerGestureListener _gestureListener; VisualElementPackager _packager; PropertyChangedEventHandler _propertyChangeHandler; @@ -209,6 +213,9 @@ namespace Xamarin.Forms.Platform.Android if (element != null && !string.IsNullOrEmpty(element.AutomationId)) SetAutomationId(element.AutomationId); + SetContentDescription(); + SetFocusable(); + Performance.Stop(); } @@ -302,6 +309,12 @@ namespace Xamarin.Forms.Platform.Android UpdateBackgroundColor(); else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName) InputTransparent = Element.InputTransparent; + else if (e.PropertyName == Accessibility.HintProperty.PropertyName) + SetContentDescription(); + else if (e.PropertyName == Accessibility.NameProperty.PropertyName) + SetContentDescription(); + else if (e.PropertyName == Accessibility.IsInAccessibleTreeProperty.PropertyName) + SetFocusable(); } protected override void OnLayout(bool changed, int l, int t, int r, int b) @@ -331,6 +344,62 @@ namespace Xamarin.Forms.Platform.Android ContentDescription = id; } + protected virtual void SetContentDescription() + { + if (Element == null) + return; + + if (SetHint()) + return; + + if (_defaultContentDescription == null) + _defaultContentDescription = ContentDescription; + + var elemValue = string.Join(" ", (string)Element.GetValue(Accessibility.NameProperty), (string)Element.GetValue(Accessibility.HintProperty)); + + if (!string.IsNullOrWhiteSpace(elemValue)) + ContentDescription = elemValue; + else + ContentDescription = _defaultContentDescription; + } + + protected virtual void SetFocusable() + { + if (Element == null) + return; + + if (!_defaultFocusable.HasValue) + _defaultFocusable = Focusable; + + Focusable = (bool)((bool?)Element.GetValue(Accessibility.IsInAccessibleTreeProperty) ?? _defaultFocusable); + } + + protected virtual bool SetHint() + { + if (Element == null) + return false; + + var textView = this as global::Android.Widget.TextView; + if (textView == null) + return false; + + // Let the specified Title/Placeholder take precedence, but don't set the ContentDescription (won't work anyway) + if (((Element as Picker)?.Title ?? (Element as Entry)?.Placeholder ?? (Element as EntryCell)?.Placeholder) != null) + return true; + + if (_defaultHint == null) + _defaultHint = textView.Hint; + + var elemValue = string.Join(". ", (string)Element.GetValue(Accessibility.NameProperty), (string)Element.GetValue(Accessibility.HintProperty)); + + if (!string.IsNullOrWhiteSpace(elemValue)) + textView.Hint = elemValue; + else + textView.Hint = _defaultHint; + + return true; + } + protected void SetPackager(VisualElementPackager packager) { _packager = packager; @@ -357,6 +426,14 @@ namespace Xamarin.Forms.Platform.Android UpdateGestureRecognizers(); } + void IVisualElementRenderer.SetLabelFor(int? id) + { + if (_defaultLabelFor == null) + _defaultLabelFor = LabelFor; + + LabelFor = (int)(id ?? _defaultLabelFor); + } + void SubscribeGestureRecognizers(VisualElement element) { var view = element as View; |