Fuchsia a11y actions (flutter/engine#16321)

This commit is contained in:
Dan Field 2020-02-04 19:01:19 -08:00 committed by GitHub
parent d319ee469d
commit 8e419f3347
5 changed files with 144 additions and 7 deletions

View File

@ -281,12 +281,65 @@ void AccessibilityBridge::UpdateScreenRects(
}
}
std::optional<flutter::SemanticsAction>
AccessibilityBridge::GetFlutterSemanticsAction(
fuchsia::accessibility::semantics::Action fuchsia_action,
uint32_t node_id) {
switch (fuchsia_action) {
// The default action associated with the element.
case fuchsia::accessibility::semantics::Action::DEFAULT:
return flutter::SemanticsAction::kTap;
// The secondary action associated with the element. This may correspond to
// a long press (touchscreens) or right click (mouse).
case fuchsia::accessibility::semantics::Action::SECONDARY:
return flutter::SemanticsAction::kLongPress;
// Set (input/non-accessibility) focus on this element.
case fuchsia::accessibility::semantics::Action::SET_FOCUS:
FML_DLOG(WARNING)
<< "Unsupported action SET_FOCUS sent for accessibility node "
<< node_id;
return {};
// Set the element's value.
case fuchsia::accessibility::semantics::Action::SET_VALUE:
FML_DLOG(WARNING)
<< "Unsupported action SET_VALUE sent for accessibility node "
<< node_id;
return {};
// Scroll node to make it visible.
case fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN:
return flutter::SemanticsAction::kShowOnScreen;
default:
FML_DLOG(WARNING) << "Unexpected action "
<< static_cast<int32_t>(fuchsia_action)
<< " sent for accessibility node " << node_id;
return {};
}
}
// |fuchsia::accessibility::semantics::SemanticListener|
void AccessibilityBridge::OnAccessibilityActionRequested(
uint32_t node_id,
fuchsia::accessibility::semantics::Action action,
fuchsia::accessibility::semantics::SemanticListener::
OnAccessibilityActionRequestedCallback callback) {}
OnAccessibilityActionRequestedCallback callback) {
if (nodes_.find(node_id) == nodes_.end()) {
FML_LOG(ERROR) << "Attempted to send accessibility action "
<< static_cast<int32_t>(action)
<< " to unkonwn node id: " << node_id;
callback(false);
return;
}
std::optional<flutter::SemanticsAction> flutter_action =
GetFlutterSemanticsAction(action, node_id);
if (!flutter_action.has_value()) {
callback(false);
return;
}
delegate_.DispatchSemanticsAction(static_cast<int32_t>(node_id),
flutter_action.value());
callback(true);
}
// |fuchsia::accessibility::semantics::SemanticListener|
void AccessibilityBridge::HitTest(

View File

@ -44,6 +44,8 @@ class AccessibilityBridge
class Delegate {
public:
virtual void SetSemanticsEnabled(bool enabled) = 0;
virtual void DispatchSemanticsAction(int32_t node_id,
flutter::SemanticsAction action) = 0;
};
// TODO(MI4-2531, FIDL-718): Remove this. We shouldn't be worried about
@ -91,6 +93,13 @@ class AccessibilityBridge
fuchsia::accessibility::semantics::SemanticListener::HitTestCallback
callback) override;
// |fuchsia::accessibility::semantics::SemanticListener|
void OnAccessibilityActionRequested(
uint32_t node_id,
fuchsia::accessibility::semantics::Action action,
fuchsia::accessibility::semantics::SemanticListener::
OnAccessibilityActionRequestedCallback callback) override;
private:
// Holds only the fields we need for hit testing.
// In particular, it adds a screen_rect field to flutter::SemanticsNode.
@ -164,12 +173,14 @@ class AccessibilityBridge
// Assumes that SemanticsNode::screen_rect is up to date.
std::optional<int32_t> GetHitNode(int32_t node_id, float x, float y);
// |fuchsia::accessibility::semantics::SemanticListener|
void OnAccessibilityActionRequested(
uint32_t node_id,
fuchsia::accessibility::semantics::Action action,
fuchsia::accessibility::semantics::SemanticListener::
OnAccessibilityActionRequestedCallback callback) override;
// Converts a fuchsia::accessibility::semantics::Action to a
// flutter::SemanticsAction.
//
// The node_id parameter is used for printing warnings about unsupported
// action types.
std::optional<flutter::SemanticsAction> GetFlutterSemanticsAction(
fuchsia::accessibility::semantics::Action fuchsia_action,
uint32_t node_id);
// |fuchsia::accessibility::semantics::SemanticListener|
void OnSemanticsModeChanged(bool enabled,

View File

@ -23,7 +23,13 @@ class AccessibilityBridgeTestDelegate
: public flutter_runner::AccessibilityBridge::Delegate {
public:
void SetSemanticsEnabled(bool enabled) override { enabled_ = enabled; }
void DispatchSemanticsAction(int32_t node_id,
flutter::SemanticsAction action) override {
actions.push_back(std::make_pair(node_id, action));
}
bool enabled() { return enabled_; }
std::vector<std::pair<int32_t, flutter::SemanticsAction>> actions;
private:
bool enabled_;
@ -50,6 +56,7 @@ class AccessibilityBridgeTest : public testing::Test {
/*flags*/ 0u, &view_ref_control_.reference, &view_ref_.reference);
EXPECT_EQ(status, ZX_OK);
accessibility_delegate_.actions.clear();
accessibility_bridge_ =
std::make_unique<flutter_runner::AccessibilityBridge>(
accessibility_delegate_, services_provider_.service_directory(),
@ -364,4 +371,59 @@ TEST_F(AccessibilityBridgeTest, HitTest) {
accessibility_bridge_->HitTest({30, 30}, callback);
EXPECT_EQ(hit_node_id, 4u);
}
TEST_F(AccessibilityBridgeTest, Actions) {
flutter::SemanticsNode node0;
node0.id = 0;
flutter::SemanticsNode node1;
node1.id = 1;
node0.childrenInTraversalOrder = {1};
node0.childrenInHitTestOrder = {1};
accessibility_bridge_->AddSemanticsNodeUpdate({
{0, node0},
{1, node1},
});
RunLoopUntilIdle();
auto handled_callback = [](bool handled) { EXPECT_TRUE(handled); };
auto unhandled_callback = [](bool handled) { EXPECT_FALSE(handled); };
accessibility_bridge_->OnAccessibilityActionRequested(
0u, fuchsia::accessibility::semantics::Action::DEFAULT, handled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 1u);
EXPECT_EQ(accessibility_delegate_.actions.back(),
std::make_pair(0, flutter::SemanticsAction::kTap));
accessibility_bridge_->OnAccessibilityActionRequested(
0u, fuchsia::accessibility::semantics::Action::SECONDARY,
handled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
EXPECT_EQ(accessibility_delegate_.actions.back(),
std::make_pair(0, flutter::SemanticsAction::kLongPress));
accessibility_bridge_->OnAccessibilityActionRequested(
0u, fuchsia::accessibility::semantics::Action::SET_FOCUS,
unhandled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
accessibility_bridge_->OnAccessibilityActionRequested(
0u, fuchsia::accessibility::semantics::Action::SET_VALUE,
unhandled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
accessibility_bridge_->OnAccessibilityActionRequested(
0u, fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN,
handled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 3u);
EXPECT_EQ(accessibility_delegate_.actions.back(),
std::make_pair(0, flutter::SemanticsAction::kShowOnScreen));
accessibility_bridge_->OnAccessibilityActionRequested(
2u, fuchsia::accessibility::semantics::Action::DEFAULT,
unhandled_callback);
EXPECT_EQ(accessibility_delegate_.actions.size(), 3u);
}
} // namespace flutter_runner_test

View File

@ -602,6 +602,13 @@ void PlatformView::SetSemanticsEnabled(bool enabled) {
}
}
// |flutter::PlatformView|
// |flutter_runner::AccessibilityBridge::Delegate|
void PlatformView::DispatchSemanticsAction(int32_t node_id,
flutter::SemanticsAction action) {
flutter::PlatformView::DispatchSemanticsAction(node_id, action, {});
}
// |flutter::PlatformView|
void PlatformView::UpdateSemantics(
flutter::SemanticsNodeUpdates update,

View File

@ -69,6 +69,10 @@ class PlatformView final : public flutter::PlatformView,
// |flutter_runner::AccessibilityBridge::Delegate|
void SetSemanticsEnabled(bool enabled) override;
// |flutter_runner::AccessibilityBridge::Delegate|
void DispatchSemanticsAction(int32_t node_id,
flutter::SemanticsAction action) override;
// |PlatformView|
flutter::PointerDataDispatcherMaker GetDispatcherMaker() override;