blob: db170fec98f9cdc3e5e8789c5d0901ca30994786 [file] [log] [blame]
// Copyright 2019 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 "src/ui/a11y/lib/view/view_manager.h"
#include <lib/async/default.h>
#include <lib/fit/bridge.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include "src/ui/a11y/lib/semantics/semantics_event.h"
#include "src/ui/a11y/lib/util/util.h"
namespace a11y {
ViewManager::ViewManager(std::unique_ptr<SemanticTreeServiceFactory> factory,
std::unique_ptr<ViewSemanticsFactory> view_semantics_factory,
std::unique_ptr<AnnotationViewFactoryInterface> annotation_view_factory,
std::unique_ptr<SemanticsEventManager> semantics_event_manager,
sys::ComponentContext* context, vfs::PseudoDir* debug_dir)
: factory_(std::move(factory)),
view_semantics_factory_(std::move(view_semantics_factory)),
annotation_view_factory_(std::move(annotation_view_factory)),
semantics_event_manager_(std::move(semantics_event_manager)),
context_(context),
debug_dir_(debug_dir) {}
ViewManager::~ViewManager() {
for (auto& iterator : wait_map_) {
iterator.second->Cancel();
}
wait_map_.clear();
view_wrapper_map_.clear();
}
void ViewManager::RegisterViewForSemantics(
fuchsia::ui::views::ViewRef view_ref,
fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticListener> handle,
fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree> semantic_tree_request) {
// Clients should register every view that gets created irrespective of the
// state(enabled/disabled) of screen reader.
// TODO(fxbug.dev/36199): Check if ViewRef is Valid.
// TODO(fxbug.dev/36199): When ViewRef is no longer valid, then all the holders of ViewRef will
// get a signal, and Semantics Manager should then delete the binding for that ViewRef.
zx_koid_t koid = GetKoid(view_ref);
auto close_channel_callback = [this, koid](zx_status_t status) {
if (auto it = view_wrapper_map_.find(koid); it != view_wrapper_map_.end()) {
FX_LOGS(INFO) << "View Manager is closing channel with koid:" << koid;
it->second->CloseChannel(status);
}
wait_map_.erase(koid);
view_wrapper_map_.erase(koid);
};
auto semantics_event_callback = [this, koid](SemanticsEventType event_type) {
SemanticsEventInfo event;
event.event_type = event_type;
event.view_ref_koid = koid;
if (semantics_event_manager_) {
semantics_event_manager_->OnEvent(event);
}
};
fuchsia::accessibility::semantics::SemanticListenerPtr semantic_listener = handle.Bind();
semantic_listener.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Semantic Provider disconnected with status: "
<< zx_status_get_string(status);
});
auto service =
factory_->NewService(koid, std::move(semantic_listener), debug_dir_,
std::move(close_channel_callback), std::move(semantics_event_callback));
// As part of the registration, client should get notified about the current Semantics Manager
// enable settings.
service->EnableSemanticsUpdates(semantics_enabled_);
// Start listening for signals on the view ref so that we can clean up associated state.
auto wait_ptr = std::make_unique<async::WaitMethod<ViewManager, &ViewManager::ViewSignalHandler>>(
this, view_ref.reference.get(), ZX_EVENTPAIR_PEER_CLOSED);
FX_CHECK(wait_ptr->Begin(async_get_default_dispatcher()) == ZX_OK);
wait_map_[koid] = std::move(wait_ptr);
auto view_semantics = view_semantics_factory_->CreateViewSemantics(
std::move(service), std::move(semantic_tree_request));
auto annotation_view = annotation_view_factory_->CreateAndInitAnnotationView(
fidl::Clone(view_ref), context_,
// TODO: add callbacks
[]() {}, []() {}, []() {});
view_wrapper_map_[koid] = std::make_unique<ViewWrapper>(
std::move(view_ref), std::move(view_semantics), std::move(annotation_view));
}
const fxl::WeakPtr<::a11y::SemanticTree> ViewManager::GetTreeByKoid(const zx_koid_t koid) const {
auto it = view_wrapper_map_.find(koid);
return it != view_wrapper_map_.end() ? it->second->GetTree() : nullptr;
}
void ViewManager::SetSemanticsEnabled(bool enabled) {
semantics_enabled_ = enabled;
// Notify all the Views about change in Semantics Enabled.
for (auto& view_wrapper : view_wrapper_map_) {
view_wrapper.second->EnableSemanticUpdates(enabled);
}
}
void ViewManager::SetAnnotationsEnabled(bool annotations_enabled) {
// This function call should be a noop if annotation state is not changing.
if (annotations_enabled_ == annotations_enabled) {
return;
}
// If we are disabling annotations, then we should clear the existing
// highlight (if any).
if (!annotations_enabled) {
ClearHighlight();
}
annotations_enabled_ = annotations_enabled;
}
void ViewManager::ViewSignalHandler(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal* signal) {
zx_koid_t koid = fsl::GetKoid(wait->object());
wait_map_.erase(koid);
view_wrapper_map_.erase(koid);
}
bool ViewManager::ViewHasSemantics(zx_koid_t view_ref_koid) {
auto it = view_wrapper_map_.find(view_ref_koid);
return it != view_wrapper_map_.end();
}
std::optional<fuchsia::ui::views::ViewRef> ViewManager::ViewRefClone(zx_koid_t view_ref_koid) {
auto it = view_wrapper_map_.find(view_ref_koid);
if (it != view_wrapper_map_.end()) {
return it->second->ViewRefClone();
}
return std::nullopt;
}
const fuchsia::accessibility::semantics::Node* ViewManager::GetSemanticNode(
zx_koid_t koid, uint32_t node_id) const {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
return nullptr;
}
return tree_weak_ptr->GetNode(node_id);
}
const fuchsia::accessibility::semantics::Node* ViewManager::GetParentNode(zx_koid_t koid,
uint32_t node_id) const {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
FX_LOGS(WARNING) << "ViewManager::GetSemanticNode: No semantic tree found for koid: " << koid;
return nullptr;
}
return tree_weak_ptr->GetParentNode(node_id);
}
const fuchsia::accessibility::semantics::Node* ViewManager::GetNextNode(
zx_koid_t koid, uint32_t node_id,
fit::function<bool(const fuchsia::accessibility::semantics::Node*)> filter) const {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
FX_LOGS(WARNING) << "ViewManager::GetSemanticNode: No semantic tree found for koid: " << koid;
return nullptr;
}
return tree_weak_ptr->GetNextNode(node_id, std::move(filter));
}
const fuchsia::accessibility::semantics::Node* ViewManager::GetPreviousNode(
zx_koid_t koid, uint32_t node_id,
fit::function<bool(const fuchsia::accessibility::semantics::Node*)> filter) const {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
FX_LOGS(WARNING) << "ViewManager::GetSemanticNode: No semantic tree found for koid: " << koid;
return nullptr;
}
return tree_weak_ptr->GetPreviousNode(node_id, std::move(filter));
}
void ViewManager::ClearHighlight() {
if (!annotations_enabled_) {
return;
}
if (RemoveHighlight()) {
highlighted_node_ = std::nullopt;
}
}
bool ViewManager::RemoveHighlight() {
if (!highlighted_node_.has_value()) {
return false;
}
auto it = view_wrapper_map_.find(highlighted_node_->koid);
if (it == view_wrapper_map_.end()) {
FX_LOGS(WARNING) << "ViewManager::UpdateHighlights: Invalid previously highlighted view koid: "
<< highlighted_node_->koid;
return false;
}
FX_DCHECK(it->second);
it->second->ClearHighlights();
return true;
}
void ViewManager::UpdateHighlight(SemanticNodeIdentifier newly_highlighted_node) {
if (!annotations_enabled_) {
return;
}
ClearHighlight();
if (DrawHighlight(newly_highlighted_node)) {
highlighted_node_ = std::make_optional<SemanticNodeIdentifier>(newly_highlighted_node);
}
}
bool ViewManager::DrawHighlight(SemanticNodeIdentifier newly_highlighted_node) {
auto it = view_wrapper_map_.find(newly_highlighted_node.koid);
if (it == view_wrapper_map_.end()) {
FX_LOGS(WARNING) << "ViewManager::UpdateHighlights: Invalid newly highlighted view koid: "
<< newly_highlighted_node.koid;
return false;
}
FX_DCHECK(it->second);
it->second->HighlightNode(newly_highlighted_node.node_id);
return true;
}
void ViewManager::ExecuteHitTesting(
zx_koid_t koid, fuchsia::math::PointF local_point,
fuchsia::accessibility::semantics::SemanticListener::HitTestCallback callback) {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
FX_LOGS(WARNING) << "ViewManager::ExecuteHitTesting: No semantic tree found for koid: " << koid;
return;
}
tree_weak_ptr->PerformHitTesting(local_point, std::move(callback));
}
void ViewManager::PerformAccessibilityAction(
zx_koid_t koid, uint32_t node_id, fuchsia::accessibility::semantics::Action action,
fuchsia::accessibility::semantics::SemanticListener::OnAccessibilityActionRequestedCallback
callback) {
auto tree_weak_ptr = GetTreeByKoid(koid);
if (!tree_weak_ptr) {
FX_LOGS(WARNING) << "ViewManager::PerformAccessibilityAction: No semantic tree found for koid: "
<< koid;
callback(false);
return;
}
tree_weak_ptr->PerformAccessibilityAction(node_id, action, std::move(callback));
}
} // namespace a11y