blob: beed11cdd6f3933cf6cab0772c928217e5cd5713 [file] [log] [blame]
// Copyright 2020 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/screen_reader/focus/a11y_focus_manager.h"
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/async/default.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/syslog/cpp/macros.h>
#include <optional>
#include "src/ui/a11y/lib/util/util.h"
namespace a11y {
A11yFocusManager::A11yFocusManager(AccessibilityFocusChainRequester* focus_chain_requester,
AccessibilityFocusChainRegistry* registry,
FocusHighlightManager* focus_highlight_manager,
inspect::Node inspect_node)
: focus_chain_requester_(focus_chain_requester),
focus_highlight_manager_(focus_highlight_manager),
weak_ptr_factory_(this),
inspect_node_(std::move(inspect_node)),
inspect_property_current_focus_koid_(
inspect_node_.CreateUint(kCurrentlyFocusedKoidInspectNodeName, 0)),
inspect_property_current_focus_node_id_(
inspect_node_.CreateUint(kCurrentlyFocusedNodeIdInspectNodeName, 0)) {
FX_DCHECK(registry);
FX_DCHECK(focus_highlight_manager_);
registry->Register(weak_ptr_factory_.GetWeakPtr());
}
A11yFocusManager::A11yFocusManager() : weak_ptr_factory_(this) {}
A11yFocusManager::~A11yFocusManager() = default;
std::optional<A11yFocusManager::A11yFocusInfo> A11yFocusManager::GetA11yFocus() {
const auto iterator = focused_node_in_view_map_.find(currently_focused_view_);
if (iterator == focused_node_in_view_map_.end()) {
FX_LOGS(INFO) << "No view is currently in a11y-focus.";
return std::nullopt;
}
A11yFocusInfo focus_info;
focus_info.view_ref_koid = currently_focused_view_;
focus_info.node_id = iterator->second;
return focus_info;
}
void A11yFocusManager::SetA11yFocus(zx_koid_t koid, uint32_t node_id,
SetA11yFocusCallback set_focus_callback) {
if (koid == currently_focused_view_) {
// Same view a11y focus change.
focused_node_in_view_map_[koid] = node_id;
UpdateHighlights();
set_focus_callback(true);
UpdateInspectProperties();
return;
}
// Different view, a Focus Chain Update is necessary.
focus_chain_requester_->ChangeFocusToView(
koid, [this, koid, node_id, callback = std::move(set_focus_callback)](bool success) {
if (!success) {
callback(false);
} else {
// Update current a11y focus to the given viewref and node_id.
focused_node_in_view_map_[koid] = node_id;
currently_focused_view_ = koid;
UpdateHighlights();
UpdateInspectProperties();
callback(true);
}
});
}
void A11yFocusManager::OnViewFocus(zx_koid_t view_ref_koid) {
uint32_t newly_focused_node_id = kRootNodeId;
if (focused_node_in_view_map_.find(view_ref_koid) != focused_node_in_view_map_.end()) {
newly_focused_node_id = focused_node_in_view_map_[view_ref_koid];
}
currently_focused_view_ = view_ref_koid;
focused_node_in_view_map_[currently_focused_view_] = newly_focused_node_id;
UpdateHighlights();
UpdateInspectProperties();
}
void A11yFocusManager::UpdateInspectProperties() {
// It's possible that the inspector could attempt to read these properties
// while we are updating them. By setting inspect_property_current_focus_koid_
// to a nonsense value of UINT64_MAX prior to updating, we ensure that we can
// recognize instances in which the inspector reads the properties during an
// update.
inspect_property_current_focus_koid_.Set(UINT64_MAX);
inspect_property_current_focus_node_id_.Set(focused_node_in_view_map_[currently_focused_view_]);
inspect_property_current_focus_koid_.Set(currently_focused_view_);
}
void A11yFocusManager::UpdateHighlights() {
FocusHighlightManager::SemanticNodeIdentifier newly_focused_node;
if (currently_focused_view_ == ZX_KOID_INVALID) {
return;
}
newly_focused_node.koid = currently_focused_view_;
newly_focused_node.node_id = focused_node_in_view_map_[currently_focused_view_];
focus_highlight_manager_->UpdateHighlight(newly_focused_node);
}
void A11yFocusManager::ClearA11yFocus() {
currently_focused_view_ = ZX_KOID_INVALID;
focus_highlight_manager_->ClearHighlight();
}
} // namespace a11y