blob: 3848ef63b80170a55a82f38464cbd852956ed454 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "topaz/runtime/flutter_runner/semantics_bridge.h"
#include <lib/syslog/global.h>
#include "topaz/runtime/dart/utils/inlines.h"
#include "topaz/runtime/flutter_runner/logging.h"
namespace flutter_runner {
// Helper function to convert SkRect, Flutter semantics node bounding box
// format to fuchsia::ui::gfx::BoundingBox, the Fidl equivalent.
fuchsia::ui::gfx::BoundingBox WrapBoundingBox(SkRect& rect) {
fuchsia::ui::gfx::BoundingBox box;
box.min.x = rect.fLeft;
box.min.y = rect.fTop;
box.min.z = 0;
box.max.x = rect.fRight;
box.max.y = rect.fBottom;
box.max.z = 0;
return box;
}
// Helper function to convert SkMatrix44, Flutter semantics node transform
// format to fuchsia::ui::gfx::mat4, the Fidl equivalent.
fuchsia::ui::gfx::mat4 WrapSkMatrix(SkMatrix44& args) {
fuchsia::ui::gfx::mat4 value;
DEBUG_CHECK(value.matrix.size() == 16, LOG_TAG, "");
float* m = value.matrix.data();
args.asColMajorf(m);
return value;
}
// Helper function to convert Flutter SemanticsNode to fuchsia Fidl
// accessibility node format.
fuchsia::accessibility::Node SerializeNode(flutter::SemanticsNode node,
float scale) {
fuchsia::accessibility::Node s_node = fuchsia::accessibility::Node();
s_node.node_id = node.id;
s_node.children_hit_test_order =
fidl::VectorPtr<int32_t>(node.childrenInHitTestOrder);
s_node.children_traversal_order =
fidl::VectorPtr<int32_t>(node.childrenInTraversalOrder);
s_node.data = fuchsia::accessibility::Data();
s_node.data.role = fuchsia::accessibility::Role::NONE;
s_node.data.label = node.label;
s_node.data.location = WrapBoundingBox(node.rect);
SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
node.transform.invert(&inverse);
if (s_node.node_id == 0) {
SkMatrix44 scaling(SkMatrix44::kIdentity_Constructor);
scaling.setScale(scale, scale, 1);
inverse = inverse * scaling;
}
s_node.data.transform = WrapSkMatrix(inverse);
return s_node;
}
SemanticsBridge::SemanticsBridge(flutter::PlatformView* platform_view,
flutter::LogicalMetrics* metrics)
: binding_(this), platform_view_(platform_view), metrics_(metrics) {
root_.set_error_handler([this](zx_status_t status) {
FX_LOG(INFO, LOG_TAG, "A11y bridge disconnected from a11y manager");
binding_.Unbind();
root_.Unbind();
platform_view_->SetSemanticsEnabled(false);
});
// Set up |a11y_toggle_| to listen for turning on/off a11y support.
// If this disconnects, we shut down all other connections and disable
// a11y support.
a11y_toggle_.events().OnAccessibilityToggle =
fit::bind_member(this, &SemanticsBridge::OnAccessibilityToggle);
a11y_toggle_.set_error_handler([this](zx_status_t status) {
FX_LOG(INFO, LOG_TAG, "Disconnected from a11y toggle broadcaster.");
binding_.Unbind();
root_.Unbind();
environment_set_ = false;
platform_view_->SetSemanticsEnabled(false);
});
}
void SemanticsBridge::SetupEnvironment(
uint32_t view_id,
fuchsia::sys::ServiceProvider* environment_service_provider) {
view_id_ = view_id;
environment_service_provider_ = environment_service_provider;
environment_service_provider->ConnectToService(
fuchsia::accessibility::ToggleBroadcaster::Name_,
a11y_toggle_.NewRequest().TakeChannel());
environment_set_ = true;
// Starts up accessibility support if accessibility was toggled before
// the environment was set.
if (enabled_) {
OnAccessibilityToggle(enabled_);
}
}
void SemanticsBridge::UpdateSemantics(
const flutter::SemanticsNodeUpdates& update) {
fidl::VectorPtr<int32_t> delete_nodes;
fidl::VectorPtr<fuchsia::accessibility::Node> update_nodes;
for (auto it = update.begin(); it != update.end(); ++it) {
flutter::SemanticsNode node = it->second;
// We delete nodes that are hidden from the screen.
if (node.HasFlag(flutter::SemanticsFlags::kIsHidden)) {
delete_nodes.push_back(node.id);
} else {
update_nodes.push_back(SerializeNode(node, (float)metrics_->scale));
}
}
// TODO(MI4-1539): re-enable after changes to semantics root API
/*
if (!delete_nodes.get().empty()) {
root_->DeleteSemanticNodes(view_id_, std::move(delete_nodes));
}
if (!update_nodes.get().empty()) {
root_->UpdateSemanticNodes(view_id_, std::move(update_nodes));
}
root_->Commit(view_id_);
*/
}
void SemanticsBridge::PerformAccessibilityAction(
int32_t node_id, fuchsia::accessibility::Action action) {
std::vector<uint8_t> args = {};
switch (action) {
case fuchsia::accessibility::Action::GAIN_ACCESSIBILITY_FOCUS:
platform_view_->DispatchSemanticsAction(
node_id, flutter::SemanticsAction::kDidGainAccessibilityFocus, args);
break;
case fuchsia::accessibility::Action::LOSE_ACCESSIBILITY_FOCUS:
platform_view_->DispatchSemanticsAction(
node_id, flutter::SemanticsAction::kDidLoseAccessibilityFocus, args);
break;
case fuchsia::accessibility::Action::TAP:
platform_view_->DispatchSemanticsAction(
node_id, flutter::SemanticsAction::kTap, args);
break;
default:
FX_LOG(ERROR, LOG_TAG, "Accessibility action not supported");
}
}
void SemanticsBridge::OnAccessibilityToggle(bool enabled) {
if (enabled == enabled_ && root_.is_bound()) {
return;
}
enabled_ = enabled;
if (enabled && environment_set_) {
// Reconnect if the a11y manager connection is not bound.
if (!root_.is_bound()) {
environment_service_provider_->ConnectToService(
fuchsia::accessibility::SemanticsRoot::Name_,
root_.NewRequest().TakeChannel());
}
// TODO(MI4-1539): re-enable after changes to semantics root API
/*
if (root_.is_bound()) {
root_->RegisterSemanticsProvider(view_id_, binding_.NewBinding());
platform_view_->SetSemanticsEnabled(true);
return;
}
*/
}
root_.Unbind();
// Disable if fall through to here.
platform_view_->SetSemanticsEnabled(false);
}
} // namespace flutter_runner