Fix up Android Accessibility behind a flag (#14089)
* 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
This commit is contained in:
Родитель
0e06946961
Коммит
5209ec5a4e
|
@ -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"/>
|
||||
|
|
|
@ -49,8 +49,11 @@ namespace Xamarin.Forms.ControlGallery.Android
|
|||
#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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using Android.Text;
|
|||
using Android.Text.Style;
|
||||
using Android.Util;
|
||||
using Android.Widget;
|
||||
using AndroidX.Core.View;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android.AppCompat
|
||||
{
|
||||
|
@ -60,8 +61,8 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
var textField = CreateNativeControl();
|
||||
|
||||
SetNativeControl(textField);
|
||||
|
||||
ControlUsedForAutomation.SetAccessibilityDelegate(_pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));
|
||||
ViewCompat.SetAccessibilityDelegate(
|
||||
ControlUsedForAutomation, _pickerAccessibilityDelegate = new EntryAccessibilityDelegate(Element));
|
||||
}
|
||||
UpdateFont();
|
||||
UpdatePicker();
|
||||
|
|
|
@ -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)
|
||||
|
@ -22,7 +23,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
@ -30,52 +32,102 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
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)
|
||||
{
|
||||
|
@ -96,12 +148,21 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
@ -253,12 +314,16 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,8 +17,11 @@ namespace Xamarin.Forms.Platform.Android
|
|||
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)
|
||||
|
|
|
@ -80,7 +80,8 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
UpdateBackground(false);
|
||||
|
||||
Clickable = true;
|
||||
if(!Flags.IsAccessibilityExperimentalSet())
|
||||
Clickable = true;
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
|
|
|
@ -9,6 +9,7 @@ using Android.Text.Style;
|
|||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AndroidX.Core.View;
|
||||
using AColor = Android.Graphics.Color;
|
||||
using Orientation = Android.Widget.Orientation;
|
||||
|
||||
|
@ -67,7 +68,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
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);
|
||||
|
|
|
@ -117,8 +117,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
FastRenderers
|
||||
.AutomationPropertiesProvider
|
||||
.SetAutomationId(_editText, _searchHandler?.AutomationId);
|
||||
|
||||
.SetAutomationId(_editText, null, _searchHandler?.AutomationId);
|
||||
}
|
||||
|
||||
void UpdateFont()
|
||||
|
|
|
@ -144,6 +144,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
_toolbar = root.FindViewById<Toolbar>(Resource.Id.main_toolbar);
|
||||
_viewPager = root.FindViewById<FormsViewPager>(Resource.Id.main_viewpager);
|
||||
|
||||
_tablayout = root.FindViewById<TabLayout>(Resource.Id.main_tablayout);
|
||||
|
||||
_viewPager.EnableGesture = false;
|
||||
|
@ -177,7 +178,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
if (SectionController.GetItems().Count == 1)
|
||||
{
|
||||
_tablayout.Visibility = ViewStates.Gone;
|
||||
UpdateTablayoutVisibility();
|
||||
}
|
||||
|
||||
_tablayout.LayoutChange += OnTabLayoutChange;
|
||||
|
@ -264,8 +265,22 @@ namespace Xamarin.Forms.Platform.Android
|
|||
AnimationFinished?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnItemsCollectionChagned(object sender, NotifyCollectionChangedEventArgs e) =>
|
||||
protected virtual void OnItemsCollectionChagned(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateTablayoutVisibility();
|
||||
}
|
||||
|
||||
void UpdateTablayoutVisibility()
|
||||
{
|
||||
_tablayout.Visibility = (SectionController.GetItems().Count > 1) ? ViewStates.Visible : ViewStates.Gone;
|
||||
if (Flags.IsAccessibilityExperimentalSet())
|
||||
{
|
||||
if (_tablayout.Visibility == ViewStates.Gone)
|
||||
_viewPager.ImportantForAccessibility = ImportantForAccessibility.No;
|
||||
else
|
||||
_viewPager.ImportantForAccessibility = ImportantForAccessibility.Auto;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnShellItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
|
|
|
@ -333,7 +333,11 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
if (_drawerToggle == null && !context.IsDesignerContext())
|
||||
{
|
||||
_drawerToggle = new ActionBarDrawerToggle(context.GetActivity(), drawerLayout, toolbar, R.String.Ok, R.String.Ok)
|
||||
var openId = R.String.Ok;
|
||||
if (Flags.IsAccessibilityExperimentalSet())
|
||||
openId = Resource.String.nav_app_bar_open_drawer_description;
|
||||
|
||||
_drawerToggle = new ActionBarDrawerToggle(context.GetActivity(), drawerLayout, toolbar, openId, R.String.Ok)
|
||||
{
|
||||
ToolbarNavigationClickListener = this,
|
||||
};
|
||||
|
@ -451,7 +455,15 @@ namespace Xamarin.Forms.Platform.Android
|
|||
else if (image == null ||
|
||||
toolbar.SetNavigationContentDescription(image) == null)
|
||||
{
|
||||
toolbar.SetNavigationContentDescription(R.String.Ok);
|
||||
if (Flags.IsAccessibilityExperimentalSet())
|
||||
{
|
||||
if(CanNavigateBack)
|
||||
toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_navigate_up_description);
|
||||
else
|
||||
toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_open_drawer_description);
|
||||
}
|
||||
else
|
||||
toolbar.SetNavigationContentDescription(R.String.Ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.2.0.4" />
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.2.1.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Navigation.UI" Version="2.3.2.1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче