Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Fix up Android Accessibility behind a flag (#14089)
Browse files Browse the repository at this point in the history
* Fix up Android Accessibility behind a flag

* - automation id

* - set back button to standard "Navigate Up"

* - view compat

* - add flag check

* - fix assigning of delegate

* - add flag to forms appcompat activity

* - fix nav drawer
  • Loading branch information
PureWeen authored Jun 9, 2021
1 parent 0e06946 commit 5209ec5
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 34 deletions.
1 change: 1 addition & 0 deletions .nuspec/Xamarin.Forms.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<dependency id="Xamarin.Google.Android.Material" version="1.2.1.1" />
<dependency id="Xamarin.AndroidX.Legacy.Support.V4" version="1.0.0.6" />
<dependency id="Xamarin.AndroidX.Browser" version="1.3.0" />
<dependency id="Xamarin.AndroidX.Navigation.UI" version="2.3.2.1" />
</group>
<group targetFramework="uap10.0.14393">
<dependency id="NETStandard.Library" version="2.0.1"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ protected override void OnCreate(Bundle bundle)
#else
Forms.SetFlags("UseLegacyRenderers");
#endif
// Forms.SetFlags("AccessibilityExperimental");
Forms.Init(this, bundle);



// null out the assembly on the Resource Manager
// so all of our tests run without using the reflection APIs
// At some point the Resources class types will go away so
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.Views.Accessibility;
using AndroidX.AppCompat.Widget;
using AndroidX.Core.View;
using AndroidX.Core.View.Accessibiity;
using Xamarin.Forms.Platform.Android.FastRenderers;

namespace Xamarin.Forms.Platform.Android
{
class AccessibilityDelegateAutomationId : AccessibilityDelegateCompat

{
BindableObject _element;

public AccessibilityDelegateAutomationId(BindableObject element) : base()
{
_element = element;
}


public override void OnInitializeAccessibilityNodeInfo(global::Android.Views.View host, AccessibilityNodeInfoCompat info)
{
base.OnInitializeAccessibilityNodeInfo(host, info);

if (_element == null)
return;

if(Flags.IsAccessibilityExperimentalSet())
{
var value = AutomationPropertiesProvider.ConcatenateNameAndHelpText(_element);
if (!string.IsNullOrWhiteSpace(value))
{
host.ContentDescription = value;
}
else if(host.ContentDescription == (_element as VisualElement)?.AutomationId)
{
host.ContentDescription = null;
}
}
}

protected override void Dispose(bool disposing)
{
_element = null;
base.Dispose(disposing);
}

}
}
5 changes: 3 additions & 2 deletions Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Android.Text.Style;
using Android.Util;
using Android.Widget;
using AndroidX.Core.View;

namespace Xamarin.Forms.Platform.Android.AppCompat
{
Expand Down Expand Up @@ -60,8 +61,8 @@ protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
var textField = CreateNativeControl();

SetNativeControl(textField);

ControlUsedForAutomation.SetAccessibilityDelegate(_pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));
ViewCompat.SetAccessibilityDelegate(
ControlUsedForAutomation, _pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));
}
UpdateFont();
UpdatePicker();
Expand Down
9 changes: 5 additions & 4 deletions Xamarin.Forms.Platform.Android/EntryAccessibilityDelegate.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Android.Views.Accessibility;
using AndroidX.Core.View.Accessibiity;
using Xamarin.Forms.Platform.Android.FastRenderers;

namespace Xamarin.Forms.Platform.Android
{
class EntryAccessibilityDelegate : global::Android.Views.View.AccessibilityDelegate
class EntryAccessibilityDelegate : AccessibilityDelegateAutomationId
{
BindableObject _element;

public EntryAccessibilityDelegate(BindableObject Element) : base()
public EntryAccessibilityDelegate(BindableObject element) : base(element)
{
_element = Element;
_element = element;
}

protected override void Dispose(bool disposing)
Expand All @@ -22,7 +23,7 @@ protected override void Dispose(bool disposing)

public string ClassName { get; set; } = "android.widget.Button";

public override void OnInitializeAccessibilityNodeInfo(global::Android.Views.View host, AccessibilityNodeInfo info)
public override void OnInitializeAccessibilityNodeInfo(global::Android.Views.View host, AccessibilityNodeInfoCompat info)
{
base.OnInitializeAccessibilityNodeInfo(host, info);
info.ClassName = ClassName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices.WindowsRuntime;
using Android.Views;
using Android.Widget;
using AndroidX.Core.View;
using AView = Android.Views.View;

namespace Xamarin.Forms.Platform.Android.FastRenderers
Expand Down Expand Up @@ -30,52 +32,102 @@ internal static void GetDrawerAccessibilityResources(global::Android.Content.Con
resourceIdClose = context.Resources.GetIdentifier($"{automationIdParent}{s_defaultDrawerIdCloseSuffix}", "string", context.ApplicationInfo.PackageName);
}

static bool ShoudISetImportantForAccessibilityToNoIfAutomationIdIsSet(AView control, Element element)
{
if (!Flags.IsAccessibilityExperimentalSet())
return false;

internal static void SetAutomationId(AView control, Element element, string value = null)
{
if (!control.IsAlive() || element == null)
if (element == null)
return false;

if (String.IsNullOrWhiteSpace(element.AutomationId))
return false;

// User has specifically said what they want
if (AutomationProperties.GetIsInAccessibleTree(element) == true)
return false;

// User has explicitly set name and help text so we honor that
if (!String.IsNullOrWhiteSpace(ConcatenateNameAndHelpText(element)))
return false;

if (element is Layout ||
element is ItemsView ||
element is BoxView ||
element is ScrollView ||
element is TableView ||
element is WebView ||
element is Page ||
element is Shapes.Path ||
element is Frame ||
element is ListView ||
element is Image)
{
return;
return true;
}

SetAutomationId(control, element.AutomationId, value);
return false;
}

internal static void SetAutomationId(AView control, string automationId, string value = null)
internal static void SetAutomationId(AView control, Element element, string value = null)
{
if (!control.IsAlive())
{
return;
}

automationId = value ?? automationId;
string automationId = value ?? element?.AutomationId;
if (!string.IsNullOrEmpty(automationId))
{
control.ContentDescription = automationId;

if (ShoudISetImportantForAccessibilityToNoIfAutomationIdIsSet(control, element))
{
control.ImportantForAccessibility = ImportantForAccessibility.No;
}
else if (Flags.IsAccessibilityExperimentalSet() && control.GetAccessibilityDelegate() == null)
ViewCompat.SetAccessibilityDelegate(control, new AccessibilityDelegateAutomationId(element));

}
}

internal static void SetBasicContentDescription(
AView control,
BindableObject bindableObject,
Element element,
string defaultContentDescription)
{
if (bindableObject == null || control == null)
if (element == null || control == null)
return;

string value = ConcatenateNameAndHelpText(bindableObject);
string value = ConcatenateNameAndHelpText(element);

var contentDescription = !string.IsNullOrWhiteSpace(value) ? value : defaultContentDescription;

if (String.IsNullOrWhiteSpace(contentDescription) && bindableObject is Element element)
if (String.IsNullOrWhiteSpace(contentDescription))
{
if(Flags.IsAccessibilityExperimentalSet())
{
SetAutomationId(control, element, element.AutomationId);
return;
}

contentDescription = element.AutomationId;
}

if (Flags.IsAccessibilityExperimentalSet())
{
if (!AutomationProperties.GetIsInAccessibleTree(element).HasValue)
{
control.ImportantForAccessibility = ImportantForAccessibility.Auto;
}
}

control.ContentDescription = contentDescription;
}

internal static void SetContentDescription(
AView control,
BindableObject element,
Element element,
string defaultContentDescription,
string defaultHint)
{
Expand All @@ -96,12 +148,21 @@ internal static void SetFocusable(AView control, Element element, ref bool? defa
{
defaultFocusable = control.Focusable;
}

if (!defaultImportantForAccessibility.HasValue)
{
defaultImportantForAccessibility = control.ImportantForAccessibility;
// Auto is the default just use that
if (Flags.IsAccessibilityExperimentalSet())
defaultImportantForAccessibility = ImportantForAccessibility.Auto;
else
defaultImportantForAccessibility = control.ImportantForAccessibility;
}

bool? isInAccessibleTree = (bool?)element.GetValue(AutomationProperties.IsInAccessibleTreeProperty);

if (!isInAccessibleTree.HasValue && Flags.IsAccessibilityExperimentalSet())
return;

control.Focusable = (bool)(isInAccessibleTree ?? defaultFocusable);
control.ImportantForAccessibility = !isInAccessibleTree.HasValue ? (ImportantForAccessibility)defaultImportantForAccessibility : (bool)isInAccessibleTree ? ImportantForAccessibility.Yes : ImportantForAccessibility.No;
}
Expand Down Expand Up @@ -253,12 +314,16 @@ internal static void SetupDefaults(AView control, ref string defaultContentDescr

internal static void SetupDefaults(AView control, ref string defaultContentDescription, ref string defaultHint)
{
if (defaultContentDescription == null)
defaultContentDescription = control.ContentDescription;

if (control is TextView textView && defaultHint == null)
// Setting defaults for these values makes no sense
if (!Flags.IsAccessibilityExperimentalSet())
{
defaultHint = textView.Hint;
if (defaultContentDescription == null)
defaultContentDescription = control.ContentDescription;

if (control is TextView textView && defaultHint == null)
{
defaultHint = textView.Hint;
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions Xamarin.Forms.Platform.Android/Flags.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
using System.Linq;

namespace Xamarin.Forms
{
internal static class Flags
{
internal const string UseLegacyRenderers = "UseLegacyRenderers";

internal const string AccessibilityExperimental = "Accessibility_Experimental";

public static bool IsFlagSet(string flagName)
{
return Device.Flags != null && Device.Flags.Contains(flagName);
}

public static bool IsAccessibilityExperimentalSet()
{
return IsFlagSet(AccessibilityExperimental);
}
}
}
7 changes: 5 additions & 2 deletions Xamarin.Forms.Platform.Android/PlatformRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ internal class PlatformRenderer : ViewGroup
public PlatformRenderer(Context context, IPlatformLayout canvas) : base(context)
{
_canvas = canvas;
Focusable = true;
FocusableInTouchMode = true;
if (!Flags.IsAccessibilityExperimentalSet())
{
Focusable = true;
FocusableInTouchMode = true;
}
}

public override bool DispatchTouchEvent(MotionEvent e)
Expand Down
3 changes: 2 additions & 1 deletion Xamarin.Forms.Platform.Android/Renderers/PageRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ protected override void OnElementChanged(ElementChangedEventArgs<Page> e)

UpdateBackground(false);

Clickable = true;
if(!Flags.IsAccessibilityExperimentalSet())
Clickable = true;
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
Expand Down
3 changes: 2 additions & 1 deletion Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Android.Util;
using Android.Views;
using Android.Widget;
using AndroidX.Core.View;
using AColor = Android.Graphics.Color;
using Orientation = Android.Widget.Orientation;

Expand Down Expand Up @@ -67,7 +68,7 @@ protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
var textField = CreateNativeControl();

textField.SetAccessibilityDelegate(_pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));
ViewCompat.SetAccessibilityDelegate(textField, _pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));

var useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
_textColorSwitcher = new TextColorSwitcher(textField.TextColors, useLegacyColorManagement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ void UpdateAutomationId()
{
FastRenderers
.AutomationPropertiesProvider
.SetAutomationId(_editText, _searchHandler?.AutomationId);

.SetAutomationId(_editText, null, _searchHandler?.AutomationId);
}

void UpdateFont()
Expand Down
Loading

0 comments on commit 5209ec5

Please sign in to comment.