-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Semantics framework updates #5601
Conversation
lib/ui/semantics.dart
Outdated
@@ -146,6 +147,14 @@ class SemanticsAction { | |||
/// Accessibility focus and input focus can be held by two different nodes! | |||
static const SemanticsAction didLoseAccessibilityFocus = const SemanticsAction._(_kDidLoseAccessibilityFocusIndex); | |||
|
|||
/// A request that the node should be dismissed. | |||
/// | |||
/// A Snackbar, for example, may have a dismiss action to indicate to the user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Snackbar]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/ui/semantics.dart
Outdated
/// A request that the node should be dismissed. | ||
/// | ||
/// A Snackbar, for example, may have a dismiss action to indicate to the user | ||
/// that it can removed after it is no longer relevant. on Android, TalkBack |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on -> On
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lib/ui/semantics.dart
Outdated
/// Whether the semantics node is a live region. | ||
/// | ||
/// A live region indicates that updates to semantics node are important. Platforms | ||
/// are free to use this information to make polite updates to the user to inform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove "are free to", or maybe replace it with "typically" or some such. We're not giving them permission here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
The change to the FlutterAppDelegate was necessary to build using the latest xcode beta |
Revert FltuterAppDelegate change, determined that it is not actually from iOS 8.0+ |
WIP bot won't go away... |
Because of the issues that have come up with the implementation of live region, I am going to remove them from this PR and follow up with a better implementation later. |
I got everything working (on textfield, snackbar, and progress indicators). Some explanation of the behavior and fixes needed: When the semantics object is passed through the a11y bridge after being marked dirty, there is no corresponding accessibility node info created until 1) a TYPE_WINDOW_CONTENT_CHANGED event is dispatched with the id in question as the target or 2) touch exploration hits the parent semantics object. When a TYPE_WINDOW_CONTENT_CHANGED is received where the target is already known to be a live region, a verbal announcement is made. But if the accessibility node hasn't been created yet, then no announcement. So the fix is, I need to track Created, Dirty, and Clean states (done with an enum) so that I know when I need to send announcements. I've confirmed that this works as expected for text fields, progress indicators (where I imperatively send events), and snackbars *
|
PTAL @goderbauer |
OKAY, that is not whaat is happening. What is happening is a transparency effect on the snackbar means it has no text when it first appears |
Update text field handling - we will always mark it as a live region and trigger and update when the label (only) changes. On the framework side, we would need to ensure that 1) the semantics labels from the various affordances of the material text field are combined in a reasonable way and 2) the typing text doesn't trigger a secondary announcement (because this hides the hint). Also during my testing of this I discovered two bugs in our current text field implementation - the dash in the phone number edit box is read out as a minus, and the password edit box is inexplicably doing math. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The format changes make this really hard to review. Maybe do those in a separate PR if you're forced to do them?
@@ -248,7 +265,13 @@ class SemanticsFlag { | |||
|
|||
/// The semantics node has the quality of either being "checked" or "unchecked". | |||
/// | |||
/// This flag is mutually exclusive with [hasToggledState]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should enforce this with an assert on the framework side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning to do this in the same place with handle the scopesRoute assert
private final FlutterView mOwner; | ||
private boolean mAccessibilityEnabled = false; | ||
private SemanticsObject mA11yFocusedObject; | ||
private SemanticsObject mInputFocusedObject; | ||
private SemanticsObject mHoveredObject; | ||
private int previousRouteId = ROOT_NODE_ID; | ||
private List<Integer> previousRoutes; | ||
private String mPackageName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this used for? It looks like it's only set in the constructor, but never set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using it to allow talkback to label images, but we actually need a stable id like the url or asset path also. Removed for now
@@ -118,22 +135,19 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { | |||
if (virtualViewId == View.NO_ID) { | |||
AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mOwner); | |||
mOwner.onInitializeAccessibilityNodeInfo(result); | |||
if (mObjects.containsKey(ROOT_NODE_ID)) | |||
result.addChild(mOwner, ROOT_NODE_ID); | |||
if (mObjects.containsKey(ROOT_NODE_ID)) result.addChild(mOwner, ROOT_NODE_ID); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with our other java code, I'd keep the line break after the if condition. Maybe add { } if it makes the formatter happy.
Here and all other ifs below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added back most of the {}, that seems to make clang format happy
@@ -146,6 +160,7 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { | |||
if (object.textSelectionBase != -1 && object.textSelectionExtent != -1) { | |||
result.setTextSelection(object.textSelectionBase, object.textSelectionExtent); | |||
} | |||
result.setLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably deserves an explaining comment for the next guy looking at this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment
Regarding formatting changes - I can't submit without them but in the future I will make sure it is done in a separate commit. I don't think it would be reasonable to make a separate initial PR just to reformat |
@@ -602,6 +657,28 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { | |||
} | |||
sendAccessibilityEvent(event); | |||
} | |||
if (object.hasFlag(Flag.IS_LIVE_REGION)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we document that live region and text field are mutually exclusive?
Made the updates as discussed offline - instead of tracking a Map of liveRegions and textFields I use has/had flag and a comparison of labels using a previousLabel field. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -362,6 +368,7 @@ - (UIAccessibilityTraits)accessibilityTraits { | |||
traits |= UIAccessibilityTraitAdjustable; | |||
} | |||
if ([self node].HasFlag(blink::SemanticsFlags::kIsSelected) || | |||
[self node].HasFlag(blink::SemanticsFlags::kIsToggled) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a TODO that this doesn't give us the on/off announcements that we really want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); | ||
} else if (object.hasFlag(Flag.IS_TEXT_FIELD) && object.didChangeLabel() | ||
&& mInputFocusedObject != null && mInputFocusedObject.id == object.id) { | ||
sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment here explaining why this is here for textfield?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
e.getText().add((String) data.get("message")); | ||
sendAccessibilityEvent(e); | ||
} | ||
// Requires that the node id provided corresponds to a live region, or TalkBack will | ||
// ignore the event. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add: "The event will cause talkback to read out the new label even if node is not focused" or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Unfortunately the image semantics tag does not seem to effect iOS smart invert. I will need to more investigation into this.
Fixes flutter/flutter#18759