Flutter Engine
flutter::AccessibilityBridge Class Reference

#include <accessibility_bridge.h>

Inheritance diagram for flutter::AccessibilityBridge:
flutter::AccessibilityBridgeIos flutter::FlutterPlatformNodeDelegate::OwnerBridge ui::AXTreeObserver

Classes

class  AccessibilityBridgeDelegate
 
class  IosDelegate
 

Public Member Functions

 AccessibilityBridge (std::unique_ptr< AccessibilityBridgeDelegate > delegate)
 Creates a new instance of a accessibility bridge. More...
 
 ~AccessibilityBridge ()
 
void AddFlutterSemanticsNodeUpdate (const FlutterSemanticsNode *node)
 Adds a semantics node update to the pending semantics update. Calling this method alone will NOT update the semantics tree. To flush the pending updates, call the CommitUpdates(). More...
 
void AddFlutterSemanticsCustomActionUpdate (const FlutterSemanticsCustomAction *action)
 Adds a custom semantics action update to the pending semantics update. Calling this method alone will NOT update the semantics tree. To flush the pending updates, call the CommitUpdates(). More...
 
void CommitUpdates ()
 Flushes the pending updates and applies them to this accessibility bridge. Calling this with no pending updates does nothing, and callers should call this method at the end of an atomic batch to avoid leaving the tree in a unstable state. For example if a node reparents from A to B, callers should only call this method when both removal from A and addition to B are in the pending updates. More...
 
std::weak_ptr< FlutterPlatformNodeDelegateGetFlutterPlatformNodeDelegateFromID (AccessibilityNodeId id) const
 Get the flutter platform node delegate with the given id from this accessibility bridge. Returns expired weak_ptr if the delegate associated with the id does not exist or has been removed from the accessibility tree. More...
 
const ui::AXTreeDataGetAXTreeData () const
 Get the ax tree data from this accessibility bridge. The tree data contains information such as the id of the node that has the keyboard focus or the text selection range. More...
 
const std::vector< ui::AXEventGenerator::TargetedEventGetPendingEvents ()
 Gets all pending accessibility events generated during semantics updates. This is useful when deciding how to handle events in AccessibilityBridgeDelegate::OnAccessibilityEvent in case one may decide to handle an event differently based on all pending events. More...
 
void UpdateDelegate (std::unique_ptr< AccessibilityBridgeDelegate > delegate)
 Update the AccessibilityBridgeDelegate stored in the accessibility bridge to a new one. More...
 
 AccessibilityBridge (FlutterViewController *view_controller, PlatformViewIOS *platform_view, std::shared_ptr< FlutterPlatformViewsController > platform_views_controller, std::unique_ptr< IosDelegate > ios_delegate=nullptr)
 
 ~AccessibilityBridge ()
 
void UpdateSemantics (flutter::SemanticsNodeUpdates nodes, flutter::CustomAccessibilityActionUpdates actions)
 
void DispatchSemanticsAction (int32_t id, flutter::SemanticsAction action) override
 
void DispatchSemanticsAction (int32_t id, flutter::SemanticsAction action, fml::MallocMapping args) override
 
void AccessibilityObjectDidBecomeFocused (int32_t id) override
 
void AccessibilityObjectDidLoseFocus (int32_t id) override
 
UIView< UITextInput > * textInputView () override
 
UIView * view () const override
 
bool isVoiceOverRunning () const override
 
fml::WeakPtr< AccessibilityBridgeGetWeakPtr ()
 
std::shared_ptr< FlutterPlatformViewsControllerGetPlatformViewsController () const override
 
void clearState ()
 
- Public Member Functions inherited from flutter::AccessibilityBridgeIos
virtual ~AccessibilityBridgeIos ()=default
 
- Public Member Functions inherited from flutter::FlutterPlatformNodeDelegate::OwnerBridge
virtual ~OwnerBridge ()=default
 

Additional Inherited Members

Detailed Description

Use this class to maintain an accessibility tree. This class consumes semantics updates from the embedder API and produces an accessibility tree in the native format.

The bridge creates an AXTree to hold the semantics data that comes from Flutter semantics updates. The tree holds AXNode[s] which contain the semantics information for semantics node. The AXTree resemble the Flutter semantics tree in the Flutter framework. The bridge also uses FlutterPlatformNodeDelegate to wrap each AXNode in order to provide an accessibility tree in the native format.

This class takes in a AccessibilityBridgeDelegate instance and is in charge of its lifecycle. The delegate are used to handle the accessibility events and actions.

To use this class, you must provide your own implementation of FlutterPlatformNodeDelegate and AccessibilityBridgeDelegate.

An accessibility instance is bound to one FlutterViewController and FlutterView instance.

It helps populate the UIView's accessibilityElements property from Flutter's semantics nodes.

Definition at line 40 of file accessibility_bridge.h.

Constructor & Destructor Documentation

◆ AccessibilityBridge() [1/2]

flutter::AccessibilityBridge::AccessibilityBridge ( std::unique_ptr< AccessibilityBridgeDelegate delegate)

Creates a new instance of a accessibility bridge.

Parameters
[in]user_dataA custom pointer to the data of your choice. This pointer can be retrieve later through GetUserData().

Definition at line 22 of file accessibility_bridge.cc.

References ui::AXTree::AddObserver(), and ui::AXEventGenerator::SetTree().

24  : delegate_(std::move(delegate)) {
25  event_generator_.SetTree(&tree_);
26  tree_.AddObserver(static_cast<ui::AXTreeObserver*>(this));
27 }
void SetTree(AXTree *new_tree)
void AddObserver(AXTreeObserver *observer)
Definition: ax_tree.cc:708

◆ ~AccessibilityBridge() [1/2]

flutter::AccessibilityBridge::~AccessibilityBridge ( )

Definition at line 29 of file accessibility_bridge.cc.

References ui::AXEventGenerator::ReleaseTree(), and ui::AXTree::RemoveObserver().

Referenced by AccessibilityBridge().

29  {
30  event_generator_.ReleaseTree();
31  tree_.RemoveObserver(static_cast<ui::AXTreeObserver*>(this));
32 }
void RemoveObserver(AXTreeObserver *observer)
Definition: ax_tree.cc:717

◆ AccessibilityBridge() [2/2]

flutter::AccessibilityBridge::AccessibilityBridge ( FlutterViewController view_controller,
PlatformViewIOS platform_view,
std::shared_ptr< FlutterPlatformViewsController platform_views_controller,
std::unique_ptr< IosDelegate ios_delegate = nullptr 
)

Definition at line 39 of file accessibility_bridge.mm.

References clearState(), FlutterReply, fml::scoped_nsprotocol< NST >::get(), platform_view, fml::scoped_nsprotocol< NST >::reset(), flutter::FlutterViewController::view(), and ~AccessibilityBridge().

44  : view_controller_(view_controller),
45  platform_view_(platform_view),
46  platform_views_controller_(platform_views_controller),
47  last_focused_semantics_object_id_(kSemanticObjectIdInvalid),
48  objects_([[NSMutableDictionary alloc] init]),
49  weak_factory_(this),
50  previous_route_id_(0),
51  previous_routes_({}),
52  ios_delegate_(ios_delegate ? std::move(ios_delegate)
53  : std::make_unique<DefaultIosDelegate>()) {
54  accessibility_channel_.reset([[FlutterBasicMessageChannel alloc]
55  initWithName:@"flutter/accessibility"
56  binaryMessenger:platform_view->GetOwnerViewController().get().engine.binaryMessenger
57  codec:[FlutterStandardMessageCodec sharedInstance]]);
58  [accessibility_channel_.get() setMessageHandler:^(id message, FlutterReply reply) {
59  HandleEvent((NSDictionary*)message);
60  }];
61 }
std::unique_ptr< flutter::PlatformViewIOS > platform_view
void reset(NST object=nil)
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)

◆ ~AccessibilityBridge() [2/2]

flutter::AccessibilityBridge::~AccessibilityBridge ( )

Member Function Documentation

◆ AccessibilityObjectDidBecomeFocused()

void flutter::AccessibilityBridge::AccessibilityObjectDidBecomeFocused ( int32_t  id)
overridevirtual

A callback that is called when a SemanticObject receives focus.

The input id is the uid of the newly focused SemanticObject.

Implements flutter::AccessibilityBridgeIos.

Definition at line 73 of file accessibility_bridge.mm.

References id.

73  {
74  last_focused_semantics_object_id_ = id;
75 }
int32_t id

◆ AccessibilityObjectDidLoseFocus()

void flutter::AccessibilityBridge::AccessibilityObjectDidLoseFocus ( int32_t  id)
overridevirtual

A callback that is called when a SemanticObject loses focus

The input id is the uid of the newly focused SemanticObject.

Implements flutter::AccessibilityBridgeIos.

Definition at line 77 of file accessibility_bridge.mm.

77  {
78  if (last_focused_semantics_object_id_ == id) {
79  last_focused_semantics_object_id_ = kSemanticObjectIdInvalid;
80  }
81 }

◆ AddFlutterSemanticsCustomActionUpdate()

void flutter::AccessibilityBridge::AddFlutterSemanticsCustomActionUpdate ( const FlutterSemanticsCustomAction action)

Adds a custom semantics action update to the pending semantics update. Calling this method alone will NOT update the semantics tree. To flush the pending updates, call the CommitUpdates().

Parameters
[in]actionA pointer to the custom semantics action update.

Definition at line 39 of file accessibility_bridge.cc.

References FlutterSemanticsCustomAction::id.

40  {
41  pending_semantics_custom_action_updates_[action->id] =
42  FromFlutterSemanticsCustomAction(action);
43 }
int32_t id
The unique custom action or action override ID.
Definition: embedder.h:877

◆ AddFlutterSemanticsNodeUpdate()

void flutter::AccessibilityBridge::AddFlutterSemanticsNodeUpdate ( const FlutterSemanticsNode node)

Adds a semantics node update to the pending semantics update. Calling this method alone will NOT update the semantics tree. To flush the pending updates, call the CommitUpdates().

Parameters
[in]nodeA pointer to the semantics node update.

Definition at line 34 of file accessibility_bridge.cc.

References FlutterSemanticsNode::id.

35  {
36  pending_semantics_node_updates_[node->id] = FromFlutterSemanticsNode(node);
37 }
int32_t id
The unique identifier for this node.
Definition: embedder.h:798

◆ clearState()

void flutter::AccessibilityBridge::clearState ( )

Definition at line 360 of file accessibility_bridge.mm.

Referenced by AccessibilityBridge(), and GetPlatformViewsController().

360  {
361  [objects_ removeAllObjects];
362  previous_route_id_ = 0;
363  previous_routes_.clear();
364 }

◆ CommitUpdates()

void flutter::AccessibilityBridge::CommitUpdates ( )

Flushes the pending updates and applies them to this accessibility bridge. Calling this with no pending updates does nothing, and callers should call this method at the end of an atomic batch to avoid leaving the tree in a unstable state. For example if a node reparents from A to B, callers should only call this method when both removal from A and addition to B are in the pending updates.

Definition at line 45 of file accessibility_bridge.cc.

References BASE_LOG, ui::AXTree::data(), error, ui::AXTree::error(), GetFlutterPlatformNodeDelegateFromID(), node, target, ui::AXTreeUpdateBase< AXNodeData, AXTreeData >::tree_data, and ui::AXTree::Unserialize().

45  {
46  ui::AXTreeUpdate update{.tree_data = tree_.data()};
47  // Figure out update order, ui::AXTree only accepts update in tree order,
48  // where parent node must come before the child node in
49  // ui::AXTreeUpdate.nodes. We start with picking a random node and turn the
50  // entire subtree into a list. We pick another node from the remaining update,
51  // and keep doing so until the update map is empty. We then concatenate the
52  // lists in the reversed order, this guarantees parent updates always come
53  // before child updates.
54  std::vector<std::vector<SemanticsNode>> results;
55  while (!pending_semantics_node_updates_.empty()) {
56  auto begin = pending_semantics_node_updates_.begin();
57  SemanticsNode target = begin->second;
58  std::vector<SemanticsNode> sub_tree_list;
59  GetSubTreeList(target, sub_tree_list);
60  results.push_back(sub_tree_list);
61  pending_semantics_node_updates_.erase(begin);
62  }
63 
64  for (size_t i = results.size(); i > 0; i--) {
65  for (SemanticsNode node : results[i - 1]) {
66  ConvertFluterUpdate(node, update);
67  }
68  }
69 
70  tree_.Unserialize(update);
71  pending_semantics_node_updates_.clear();
72  pending_semantics_custom_action_updates_.clear();
73 
74  std::string error = tree_.error();
75  if (!error.empty()) {
76  BASE_LOG() << "Failed to update ui::AXTree, error: " << error;
77  return;
78  }
79  // Handles accessibility events as the result of the semantics update.
80  for (const auto& targeted_event : event_generator_) {
81  auto event_target =
82  GetFlutterPlatformNodeDelegateFromID(targeted_event.node->id());
83  if (event_target.expired()) {
84  continue;
85  }
86 
87  delegate_->OnAccessibilityEvent(targeted_event);
88  }
89  event_generator_.ClearEvents();
90 }
const uint8_t uint32_t uint32_t GError ** error
const FlutterSemanticsNode * node
Definition: fl_view.cc:83
uint32_t * target
const std::string & error() const
Definition: ax_tree.h:134
#define BASE_LOG()
Definition: logging.h:54
virtual bool Unserialize(const AXTreeUpdate &update)
Definition: ax_tree.cc:969
std::weak_ptr< FlutterPlatformNodeDelegate > GetFlutterPlatformNodeDelegateFromID(AccessibilityNodeId id) const
Get the flutter platform node delegate with the given id from this accessibility bridge. Returns expired weak_ptr if the delegate associated with the id does not exist or has been removed from the accessibility tree.
const AXTreeData & data() const
Definition: ax_tree.h:59

◆ DispatchSemanticsAction() [1/2]

void flutter::AccessibilityBridge::DispatchSemanticsAction ( int32_t  id,
flutter::SemanticsAction  action 
)
overridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 237 of file accessibility_bridge.mm.

References flutter::PlatformView::DispatchSemanticsAction().

237  {
238  platform_view_->DispatchSemanticsAction(uid, action, {});
239 }
void DispatchSemanticsAction(int32_t id, SemanticsAction action, fml::MallocMapping args)
Used by embedders to dispatch an accessibility action to a running isolate hosted by the engine...
SemanticsAction action

◆ DispatchSemanticsAction() [2/2]

void flutter::AccessibilityBridge::DispatchSemanticsAction ( int32_t  id,
flutter::SemanticsAction  action,
fml::MallocMapping  args 
)
overridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 241 of file accessibility_bridge.mm.

References flutter::PlatformView::DispatchSemanticsAction().

243  {
244  platform_view_->DispatchSemanticsAction(uid, action, std::move(args));
245 }
void DispatchSemanticsAction(int32_t id, SemanticsAction action, fml::MallocMapping args)
Used by embedders to dispatch an accessibility action to a running isolate hosted by the engine...
SemanticsAction action

◆ GetAXTreeData()

const ui::AXTreeData & flutter::AccessibilityBridge::GetAXTreeData ( ) const

Get the ax tree data from this accessibility bridge. The tree data contains information such as the id of the node that has the keyboard focus or the text selection range.

Definition at line 103 of file accessibility_bridge.cc.

References ui::AXTree::data().

103  {
104  return tree_.data();
105 }
const AXTreeData & data() const
Definition: ax_tree.h:59

◆ GetFlutterPlatformNodeDelegateFromID()

std::weak_ptr< FlutterPlatformNodeDelegate > flutter::AccessibilityBridge::GetFlutterPlatformNodeDelegateFromID ( AccessibilityNodeId  id) const

Get the flutter platform node delegate with the given id from this accessibility bridge. Returns expired weak_ptr if the delegate associated with the id does not exist or has been removed from the accessibility tree.

Parameters
[in]idThe id of the flutter accessibility node you want to retrieve.

Definition at line 93 of file accessibility_bridge.cc.

Referenced by CommitUpdates(), and UpdateDelegate().

94  {
95  const auto iter = id_wrapper_map_.find(id);
96  if (iter != id_wrapper_map_.end()) {
97  return iter->second;
98  }
99 
100  return std::weak_ptr<FlutterPlatformNodeDelegate>();
101 }

◆ GetPendingEvents()

const std::vector< ui::AXEventGenerator::TargetedEvent > flutter::AccessibilityBridge::GetPendingEvents ( )

Gets all pending accessibility events generated during semantics updates. This is useful when deciding how to handle events in AccessibilityBridgeDelegate::OnAccessibilityEvent in case one may decide to handle an event differently based on all pending events.

Definition at line 108 of file accessibility_bridge.cc.

References ui::AXEventGenerator::begin(), ui::AXEventGenerator::end(), and result.

108  {
109  std::vector<ui::AXEventGenerator::TargetedEvent> result(
110  event_generator_.begin(), event_generator_.end());
111  return result;
112 }
GAsyncResult * result
Iterator begin() const

◆ GetPlatformViewsController()

std::shared_ptr<FlutterPlatformViewsController> flutter::AccessibilityBridge::GetPlatformViewsController ( ) const
inlineoverridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 75 of file accessibility_bridge.h.

References clearState(), and FML_DISALLOW_COPY_AND_ASSIGN.

Referenced by UpdateSemantics().

75  {
76  return platform_views_controller_;
77  };

◆ GetWeakPtr()

fml::WeakPtr< AccessibilityBridge > flutter::AccessibilityBridge::GetWeakPtr ( )

Definition at line 356 of file accessibility_bridge.mm.

Referenced by flutter::DidFlagChange(), and isVoiceOverRunning().

356  {
357  return weak_factory_.GetWeakPtr();
358 }

◆ isVoiceOverRunning()

bool flutter::AccessibilityBridge::isVoiceOverRunning ( ) const
inlineoverridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 71 of file accessibility_bridge.h.

References GetWeakPtr().

71 { return view_controller_.isVoiceOverRunning; }

◆ textInputView()

UIView< UITextInput > * flutter::AccessibilityBridge::textInputView ( )
overridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 69 of file accessibility_bridge.mm.

References flutter::PlatformViewIOS::GetOwnerViewController(), and textInputPlugin.

69  {
70  return [[platform_view_->GetOwnerViewController().get().engine textInputPlugin] textInputView];
71 }
FlutterTextInputPlugin * textInputPlugin
UIView< UITextInput > * textInputView() override
fml::WeakPtr< FlutterViewController > GetOwnerViewController() const

◆ UpdateDelegate()

void flutter::AccessibilityBridge::UpdateDelegate ( std::unique_ptr< AccessibilityBridgeDelegate delegate)

Update the AccessibilityBridgeDelegate stored in the accessibility bridge to a new one.

Definition at line 114 of file accessibility_bridge.cc.

References action, FlutterSemanticsNode::actions, ui::AXNodeData::AddAction(), ui::AXNodeData::AddBoolAttribute(), ui::AXNodeData::AddIntAttribute(), ui::AXNodeData::AddIntListAttribute(), ui::AXNodeData::AddState(), ui::AXNodeData::AddStringListAttribute(), BASE_DCHECK, ui::AXRelativeBounds::bounds, FlutterSemanticsNode::child_count, ui::AXNodeData::child_ids, FlutterSemanticsNode::children_in_traversal_order, FlutterSemanticsNode::custom_accessibility_actions, FlutterSemanticsNode::custom_accessibility_actions_count, ui::AXNode::data(), FlutterSemanticsNode::decreased_value, FlutterSemanticsNode::elevation, flutter::flags, FlutterSemanticsNode::flags, ui::AXTreeData::focus_id, GetFlutterPlatformNodeDelegateFromID(), ui::AXTreeUpdateBase< AXNodeData, AXTreeData >::has_tree_data, FlutterSemanticsNode::hint, FlutterSemanticsCustomAction::hint, ui::AXNode::id(), ui::AXNodeData::id, FlutterSemanticsNode::id, FlutterSemanticsCustomAction::id, FlutterSemanticsNode::increased_value, ax::mojom::kButton, ax::mojom::kCheckBox, ax::mojom::kCheckedState, ax::mojom::kClearAccessibilityFocus, ax::mojom::kClickable, ax::mojom::kClipsChildren, ax::mojom::kCustomAction, ax::mojom::kCustomActionDescriptions, ax::mojom::kCustomActionIds, ax::mojom::kDecrement, ax::mojom::kDoDefault, ax::mojom::kEditable, ax::mojom::kEditableRoot, ax::mojom::kFalse, kFlutterSemanticsActionCustomAction, kFlutterSemanticsActionDecrease, kFlutterSemanticsActionDidGainAccessibilityFocus, kFlutterSemanticsActionDidLoseAccessibilityFocus, kFlutterSemanticsActionIncrease, kFlutterSemanticsActionScrollDown, kFlutterSemanticsActionScrollLeft, kFlutterSemanticsActionScrollRight, kFlutterSemanticsActionScrollUp, kFlutterSemanticsActionSetSelection, kFlutterSemanticsActionTap, kFlutterSemanticsFlagHasCheckedState, kFlutterSemanticsFlagIsButton, kFlutterSemanticsFlagIsChecked, kFlutterSemanticsFlagIsFocused, kFlutterSemanticsFlagIsHeader, kFlutterSemanticsFlagIsImage, kFlutterSemanticsFlagIsInMutuallyExclusiveGroup, kFlutterSemanticsFlagIsLink, kFlutterSemanticsFlagIsReadOnly, kFlutterSemanticsFlagIsSelected, kFlutterSemanticsFlagIsTextField, ax::mojom::kFocusable, ax::mojom::kGroup, ax::mojom::kHeader, ax::mojom::kIgnored, ax::mojom::kImage, ax::mojom::kIncrement, ui::AXNode::kInvalidAXID, ax::mojom::kLink, ax::mojom::kRadioButton, ax::mojom::kScrollable, ax::mojom::kScrollDown, ax::mojom::kScrollLeft, ax::mojom::kScrollRight, ax::mojom::kScrollToMakeVisible, ax::mojom::kScrollUp, ax::mojom::kSelected, ax::mojom::kSetAccessibilityFocus, ax::mojom::kSetSelection, ax::mojom::kStaticText, ax::mojom::kTextDirection, ax::mojom::kTextField, ax::mojom::kTextSelEnd, ax::mojom::kTextSelStart, ax::mojom::kTrue, FlutterSemanticsNode::label, FlutterSemanticsCustomAction::label, node, ui::AXTreeUpdateBase< AXNodeData, AXTreeData >::nodes, FlutterSemanticsCustomAction::override_action, ui::AXNode::parent(), FlutterSemanticsNode::rect, ui::AXNodeData::relative_bounds, ui::AXTree::RelativeToTreeBounds(), result, ui::AXNodeData::role, FlutterSemanticsNode::scroll_child_count, FlutterSemanticsNode::scroll_extent_max, FlutterSemanticsNode::scroll_extent_min, FlutterSemanticsNode::scroll_index, FlutterSemanticsNode::scroll_position, ui::AXTreeData::sel_anchor_object_id, ui::AXTreeData::sel_anchor_offset, ui::AXTreeData::sel_focus_object_id, ui::AXTreeData::sel_focus_offset, ui::AXNode::SetLocation(), ui::AXNodeData::SetName(), gfx::RectF::SetRect(), ui::AXNodeData::SetValue(), target, FlutterSemanticsNode::text_direction, FlutterSemanticsNode::text_selection_base, FlutterSemanticsNode::text_selection_extent, FlutterSemanticsNode::thickness, ui::AXRelativeBounds::transform, FlutterSemanticsNode::transform, ui::AXTreeUpdateBase< AXNodeData, AXTreeData >::tree_data, and FlutterSemanticsNode::value.

115  {
116  delegate_ = std::move(delegate);
117  // Recreate FlutterPlatformNodeDelegates since they may contain stale state
118  // from the previous AccessibilityBridgeDelegate.
119  for (const auto& [node_id, old_platform_node_delegate] : id_wrapper_map_) {
120  std::shared_ptr<FlutterPlatformNodeDelegate> platform_node_delegate =
121  delegate_->CreateFlutterPlatformNodeDelegate();
122  platform_node_delegate->Init(
123  std::static_pointer_cast<FlutterPlatformNodeDelegate::OwnerBridge>(
124  shared_from_this()),
125  old_platform_node_delegate->GetAXNode());
126  id_wrapper_map_[node_id] = platform_node_delegate;
127  }
128 }

◆ UpdateSemantics()

void flutter::AccessibilityBridge::UpdateSemantics ( flutter::SemanticsNodeUpdates  nodes,
flutter::CustomAccessibilityActionUpdates  actions 
)

Definition at line 83 of file accessibility_bridge.mm.

References action, flutter::SemanticsNode::childrenInTraversalOrder, flutter::SemanticsNode::customAccessibilityActions, fml::scoped_nsprotocol< NST >::get(), GetPlatformViewsController(), flutter::CustomAccessibilityAction::id, flutter::SemanticsNode::id, flutter::SemanticsNode::IsPlatformViewNode(), kRootNodeId, flutter::CustomAccessibilityAction::label, node, flutter::CustomAccessibilityAction::overrideId, target, FlutterCustomAccessibilityAction::uid, and flutter::FlutterViewController::view().

84  {
85  BOOL layoutChanged = NO;
86  BOOL scrollOccured = NO;
87  BOOL needsAnnouncement = NO;
88  for (const auto& entry : actions) {
89  const flutter::CustomAccessibilityAction& action = entry.second;
90  actions_[action.id] = action;
91  }
92  for (const auto& entry : nodes) {
93  const flutter::SemanticsNode& node = entry.second;
94  SemanticsObject* object = GetOrCreateObject(node.id, nodes);
95  layoutChanged = layoutChanged || [object nodeWillCauseLayoutChange:&node];
96  scrollOccured = scrollOccured || [object nodeWillCauseScroll:&node];
97  needsAnnouncement = [object nodeShouldTriggerAnnouncement:&node];
98  [object setSemanticsNode:&node];
99  NSUInteger newChildCount = node.childrenInTraversalOrder.size();
100  NSMutableArray* newChildren =
101  [[[NSMutableArray alloc] initWithCapacity:newChildCount] autorelease];
102  for (NSUInteger i = 0; i < newChildCount; ++i) {
103  SemanticsObject* child = GetOrCreateObject(node.childrenInTraversalOrder[i], nodes);
104  [newChildren addObject:child];
105  }
106  object.children = newChildren;
107  if (node.customAccessibilityActions.size() > 0) {
108  NSMutableArray<FlutterCustomAccessibilityAction*>* accessibilityCustomActions =
109  [[[NSMutableArray alloc] init] autorelease];
110  for (int32_t action_id : node.customAccessibilityActions) {
111  flutter::CustomAccessibilityAction& action = actions_[action_id];
112  if (action.overrideId != -1) {
113  // iOS does not support overriding standard actions, so we ignore any
114  // custom actions that have an override id provided.
115  continue;
116  }
117  NSString* label = @(action.label.data());
118  SEL selector = @selector(onCustomAccessibilityAction:);
119  FlutterCustomAccessibilityAction* customAction =
120  [[[FlutterCustomAccessibilityAction alloc] initWithName:label
121  target:object
122  selector:selector] autorelease];
123  customAction.uid = action_id;
124  [accessibilityCustomActions addObject:customAction];
125  }
126  object.accessibilityCustomActions = accessibilityCustomActions;
127  }
128 
129  if (object.node.IsPlatformViewNode()) {
130  auto controller = GetPlatformViewsController();
131  if (controller) {
132  object.platformViewSemanticsContainer = [[[FlutterPlatformViewSemanticsContainer alloc]
133  initWithSemanticsObject:object] autorelease];
134  }
135  } else if (object.platformViewSemanticsContainer) {
136  object.platformViewSemanticsContainer = nil;
137  }
138  if (needsAnnouncement) {
139  // Try to be more polite - iOS 11+ supports
140  // UIAccessibilitySpeechAttributeQueueAnnouncement which should avoid
141  // interrupting system notifications or other elements.
142  // Expectation: roughly match the behavior of polite announcements on
143  // Android.
144  NSString* announcement =
145  [[[NSString alloc] initWithUTF8String:object.node.label.c_str()] autorelease];
146  if (@available(iOS 11.0, *)) {
147  UIAccessibilityPostNotification(
148  UIAccessibilityAnnouncementNotification,
149  [[[NSAttributedString alloc]
150  initWithString:announcement
151  attributes:@{
152  UIAccessibilitySpeechAttributeQueueAnnouncement : @YES
153  }] autorelease]);
154  } else {
155  UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement);
156  }
157  }
158  }
159 
160  SemanticsObject* root = objects_.get()[@(kRootNodeId)];
161 
162  bool routeChanged = false;
163  SemanticsObject* lastAdded = nil;
164 
165  if (root) {
166  if (!view_controller_.view.accessibilityElements) {
167  view_controller_.view.accessibilityElements = @[ [root accessibilityContainer] ];
168  }
169  NSMutableArray<SemanticsObject*>* newRoutes = [[[NSMutableArray alloc] init] autorelease];
170  [root collectRoutes:newRoutes];
171  // Finds the last route that is not in the previous routes.
172  for (SemanticsObject* route in newRoutes) {
173  if (std::find(previous_routes_.begin(), previous_routes_.end(), [route uid]) ==
174  previous_routes_.end()) {
175  lastAdded = route;
176  }
177  }
178  // If all the routes are in the previous route, get the last route.
179  if (lastAdded == nil && [newRoutes count] > 0) {
180  int index = [newRoutes count] - 1;
181  lastAdded = [newRoutes objectAtIndex:index];
182  }
183  // There are two cases if lastAdded != nil
184  // 1. lastAdded is not in previous routes. In this case,
185  // [lastAdded uid] != previous_route_id_
186  // 2. All new routes are in previous routes and
187  // lastAdded = newRoutes.last.
188  // In the first case, we need to announce new route. In the second case,
189  // we need to announce if one list is shorter than the other.
190  if (lastAdded != nil &&
191  ([lastAdded uid] != previous_route_id_ || [newRoutes count] != previous_routes_.size())) {
192  previous_route_id_ = [lastAdded uid];
193  routeChanged = true;
194  }
195  previous_routes_.clear();
196  for (SemanticsObject* route in newRoutes) {
197  previous_routes_.push_back([route uid]);
198  }
199  } else {
200  view_controller_.view.accessibilityElements = nil;
201  }
202 
203  NSMutableArray<NSNumber*>* doomed_uids = [NSMutableArray arrayWithArray:[objects_ allKeys]];
204  if (root) {
205  VisitObjectsRecursivelyAndRemove(root, doomed_uids);
206  }
207  [objects_ removeObjectsForKeys:doomed_uids];
208 
209  for (SemanticsObject* object in [objects_ allValues]) {
210  [object accessibilityBridgeDidFinishUpdate];
211  }
212 
213  if (!ios_delegate_->IsFlutterViewControllerPresentingModalViewController(view_controller_)) {
214  layoutChanged = layoutChanged || [doomed_uids count] > 0;
215 
216  if (routeChanged) {
217  NSString* routeName = [lastAdded routeName];
218  ios_delegate_->PostAccessibilityNotification(UIAccessibilityScreenChangedNotification,
219  routeName);
220  }
221 
222  if (layoutChanged) {
223  ios_delegate_->PostAccessibilityNotification(
224  UIAccessibilityLayoutChangedNotification,
225  FindNextFocusableIfNecessary().nativeAccessibility);
226  } else if (scrollOccured) {
227  // TODO(chunhtai): figure out what string to use for notification. At this
228  // point, it is guarantee the previous focused object is still in the tree
229  // so that we don't need to worry about focus lost. (e.g. "Screen 0 of 3")
230  ios_delegate_->PostAccessibilityNotification(
231  UIAccessibilityPageScrolledNotification,
232  FindNextFocusableIfNecessary().nativeAccessibility);
233  }
234  }
235 }
bool IsPlatformViewNode() const
std::vector< int32_t > customAccessibilityActions
std::vector< int32_t > childrenInTraversalOrder
const FlutterSemanticsNode * node
Definition: fl_view.cc:83
uint32_t * target
std::shared_ptr< FlutterPlatformViewsController > GetPlatformViewsController() const override
SemanticsAction action
int BOOL
Definition: windows_types.h:37
constexpr int32_t kRootNodeId

◆ view()

UIView* flutter::AccessibilityBridge::view ( ) const
inlineoverridevirtual

Implements flutter::AccessibilityBridgeIos.

Definition at line 69 of file accessibility_bridge.h.

69 { return view_controller_.view; }

The documentation for this class was generated from the following files: