Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update semantics #1039

Merged
merged 28 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3588d7a
Update semantics page
rachelkang May 17, 2021
521d804
Controls w/ semantics should automatically be in semantic tree
rachelkang May 17, 2021
8021dbd
iOS image is now accessible
rachelkang May 17, 2021
3d93fa0
Make iOS view accessible specifically w/ hint/desc
rachelkang May 18, 2021
e539147
code clean up
rachelkang May 19, 2021
e3ae219
Make importantForAccessibility conditional on hint/desc
rachelkang May 19, 2021
bf4eb2f
Make SemanticsPage heading read more accessibly
rachelkang May 19, 2021
d4b2b48
Update semantics page
rachelkang May 17, 2021
17edc9c
Controls w/ semantics should automatically be in semantic tree
rachelkang May 17, 2021
d6889d0
iOS image is now accessible
rachelkang May 17, 2021
2c34f0f
Make iOS view accessible specifically w/ hint/desc
rachelkang May 18, 2021
3abdd94
code clean up
rachelkang May 19, 2021
91ab624
Make importantForAccessibility conditional on hint/desc
rachelkang May 19, 2021
4e37082
Make SemanticsPage heading read more accessibly
rachelkang May 19, 2021
eb76f61
Merge branch 'audit-semantics-page' of github.com:dotnet/maui into au…
rachelkang Jun 2, 2021
8aabfcc
Update Android semantics to use accessibilityDelegate
rachelkang Jun 3, 2021
1bf9152
Make description read from Android EditText
rachelkang Jun 3, 2021
27c94c2
Fix conflicting accessibilityDelegate code weirdness
rachelkang Jun 4, 2021
ea15723
Update SemanticsPage to reflect EditText updates
rachelkang Jun 4, 2021
ebd5517
Update Picker text to reflect Android's identical behavior
rachelkang Jun 4, 2021
82e669e
Add sample StackLayouts with semantics
rachelkang Jun 8, 2021
9d4d0f9
Fix typo
rachelkang Jun 9, 2021
558be5f
- fix searchview
PureWeen Jun 9, 2021
b6679bc
Update SemanticsPage - reflect current SearchBar behavior
rachelkang Jun 9, 2021
3e28c30
- API 26 workarounds
PureWeen Jun 9, 2021
597a5fb
- fix pre-api 26 for non text views
PureWeen Jun 10, 2021
fe7e92b
Merge branch 'main' into audit-semantics-page
PureWeen Jun 10, 2021
26d8306
- skip android tests
PureWeen Jun 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 40 additions & 5 deletions src/Controls/samples/Controls.Sample/Pages/SemanticsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI Semantics!"
SemanticProperties.Description="Welcome to dotnet MAUI Semantics!"
TextColor="DodgerBlue"
FontSize="30"
HorizontalTextAlignment="Center"/>
Expand Down Expand Up @@ -55,18 +56,18 @@
SemanticProperties.Hint="Hint text"/>

<Entry
Text="Entry text TH"
Text="Entry text, DTH"
FontSize="14"
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>
SemanticProperties.Hint="Hint text" />

<Entry
FontSize="14"
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>

<Editor
Text="Editor text TH"
Text="Editor text, DTH"
FontSize="14"
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>
Expand All @@ -77,14 +78,21 @@
SemanticProperties.Hint="Hint text"/>

<SearchBar
Text="Search bar text"
Text="Search bar text, DH (Android reads T when focus is on entry)"
Placeholder="Placeholder text"
PlaceholderColor="DodgerBlue"
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>

<Label
Text="The next 3 controls read out DTH"
TextColor="RoyalBlue"
FontAttributes="Bold"
FontSize="16"
Margin="0,10"/>

<Picker
Title="Select a monkey TH"
Title="Select a monkey, DTH"
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text">
<Picker.ItemsSource>
Expand All @@ -108,6 +116,13 @@
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>

<Label
Text="The rest of the controls read out DH"
TextColor="RoyalBlue"
FontAttributes="Bold"
FontSize="16"
Margin="0,10"/>

<ActivityIndicator
IsRunning="True"
Color="DodgerBlue"
Expand All @@ -124,6 +139,7 @@
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>

<!-- iOS: currently not accessible as a whole; only focuses on increment/decrement buttons individually -->
<Stepper
SemanticProperties.Description="Description text"
SemanticProperties.Hint="Hint text"/>
Expand Down Expand Up @@ -179,7 +195,26 @@
FontSize="14"
SemanticProperties.HeadingLevel="Level3"/>

<VerticalStackLayout SemanticProperties.Description="Layout description text">
<Label
Text="Label in StackLayout with semantic description"
FontSize="14"/>
<Label
Text="Label in StackLayout with semantic description"
FontSize="14"/>
</VerticalStackLayout>

<VerticalStackLayout SemanticProperties.Description="Layout description text">
<Label
Text="Label in StackLayout with semantic description"
FontSize="14"/>
<Button
Text="Button in StackLayout with semantic description"
FontSize="14"/>
</VerticalStackLayout>

</VerticalStackLayout>

</ScrollView>

</controls:BasePage>
86 changes: 78 additions & 8 deletions src/Core/src/Handlers/View/ViewHandler.Android.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Android.Widget;
using System;
using Android.Views;
using Android.Widget;
using AndroidX.Core.View;
using AndroidX.Core.View.Accessibility;
using NativeView = Android.Views.View;
Expand Down Expand Up @@ -77,35 +79,103 @@ public static void MapAnchorY(ViewHandler handler, IView view)

static partial void MappingSemantics(ViewHandler handler, IView view)
{
if (handler.NativeView == null)
return;

if (view.Semantics != null &&
handler is ViewHandler viewHandler &&
viewHandler.AccessibilityDelegate == null &&
ViewCompat.GetAccessibilityDelegate(handler.NativeView as NativeView) == null)
{
if (!string.IsNullOrEmpty(view.Semantics.Hint))
if (handler.NativeView is not NativeView nativeView)
return;

if (nativeView is AndroidX.AppCompat.Widget.SearchView sv)
nativeView = sv.FindViewById(Resource.Id.search_button)!;

if (!string.IsNullOrWhiteSpace(view.Semantics.Hint) || !string.IsNullOrWhiteSpace(view.Semantics.Description))
{
if (viewHandler.AccessibilityDelegate == null)
{
viewHandler.AccessibilityDelegate = new MauiAccessibilityDelegate() { Handler = viewHandler };
ViewCompat.SetAccessibilityDelegate(nativeView, viewHandler.AccessibilityDelegate);
}
}
else if (viewHandler.AccessibilityDelegate != null)
{
viewHandler.AccessibilityDelegate = new MauiAccessibilityDelegate() { Handler = viewHandler };
ViewCompat.SetAccessibilityDelegate(handler.NativeView as NativeView, viewHandler.AccessibilityDelegate);
viewHandler.AccessibilityDelegate = null;
ViewCompat.SetAccessibilityDelegate(nativeView, null);
}

if (viewHandler.AccessibilityDelegate != null)
{
nativeView.ImportantForAccessibility = ImportantForAccessibility.Yes;
}
}
}

public void OnInitializeAccessibilityNodeInfo(NativeView? host, AccessibilityNodeInfoCompat? info)
{
var semantics = VirtualView?.Semantics;

if (semantics == null)
return;

if (info == null)
return;

if (!string.IsNullOrEmpty(semantics.Hint))
{
info.HintText = semantics.Hint;
string? newText = null;
string? newContentDescription = null;

var desc = semantics.Description;
if (!string.IsNullOrEmpty(desc))
{
// Edit Text fields won't read anything for the content description
if (host is EditText)
info.ShowingHintText = false;
newText = $"{desc}, {((EditText)host).Text}";
else
newContentDescription = desc;
}

var hint = semantics.Hint;
if (!string.IsNullOrEmpty(hint))
{
// info HintText won't read anything back when using TalkBack pre API 26
if (NativeVersion.IsAtLeast(26))
{
info.HintText = hint;

if (host is EditText)
info.ShowingHintText = false;
}
else
{
if (host is TextView tv)
{
newText = newText ?? tv.Text;
newText = $"{newText}, {hint}";
}
else
{
if (newContentDescription != null)
{
newText = $"{newContentDescription}, {hint}";
}
else
{
newText = $"{hint}";
}
}

newContentDescription = null;
}
}

if (!String.IsNullOrWhiteSpace(newContentDescription))
info.ContentDescription = newContentDescription;

if (!String.IsNullOrWhiteSpace(newText))
info.Text = newText;
}

class MauiAccessibilityDelegate : AccessibilityDelegateCompat
Expand Down
3 changes: 2 additions & 1 deletion src/Core/src/Platform/Android/ViewExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Android.Graphics.Drawables;
using Android.Views;
using Android.Widget;
using AndroidX.Core.View;
using AndroidX.Core.View.Accessibility;
using Microsoft.Maui.Graphics;
using AView = Android.Views.View;

Expand Down Expand Up @@ -80,7 +82,6 @@ public static void UpdateSemantics(this AView nativeView, IView view)
if (semantics == null)
return;

nativeView.ContentDescription = semantics.Description;
ViewCompat.SetAccessibilityHeading(nativeView, semantics.IsHeading);
}

Expand Down
4 changes: 4 additions & 0 deletions src/Core/src/Platform/iOS/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public static void UpdateSemantics(this UIView nativeView, IView view)
nativeView.AccessibilityLabel = semantics.Description;
nativeView.AccessibilityHint = semantics.Hint;

// UIControl elements automatically have IsAccessibilityElement set to true
if (nativeView is not UIControl && (!string.IsNullOrWhiteSpace(semantics.Hint) || !string.IsNullOrWhiteSpace(semantics.Description)))
nativeView.IsAccessibilityElement = true;

if (semantics.IsHeading)
nativeView.AccessibilityTraits |= UIAccessibilityTrait.Header;
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public virtual async Task SetVisibility(Visibility visibility)
Assert.Equal(view.Visibility, id);
}

[Fact(DisplayName = "Semantic Description is set correctly")]
[Fact(DisplayName = "Semantic Description is set correctly"
#if MONOANDROID
, Skip = "This value can't be validated through automated tests"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we switched to using delegates for reading out the semantic description we can no longer verify these values through unit tests

#endif
)]
[InlineData()]
public async Task SetSemanticDescription()
{
Expand Down