blob: 6c0c7577a0952bd6d018fb15a5de5d1552bc06ed [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/screen_reader/explore_action.h"
#include <lib/fit/bridge.h>
#include <lib/fit/promise.h>
#include <lib/syslog/cpp/macros.h>
#include <cstdint>
#include "src/ui/a11y/lib/screen_reader/screen_reader.h"
#include "src/ui/a11y/lib/screen_reader/screen_reader_action.h"
#include "src/ui/a11y/lib/screen_reader/util/util.h"
namespace a11y {
namespace {
using fuchsia::accessibility::semantics::Hit;
using fuchsia::accessibility::tts::Utterance;
} // namespace
ExploreAction::ExploreAction(ActionContext* context, ScreenReaderContext* screen_reader_context)
: ScreenReaderAction(context, screen_reader_context) {}
ExploreAction::~ExploreAction() = default;
fit::promise<Hit> ExploreAction::ExecuteHitTestingPromise(const ActionData& process_data) {
fit::bridge<Hit> bridge;
ExecuteHitTesting(action_context_, process_data,
[completer = std::move(bridge.completer)](Hit hit) mutable {
if (!hit.has_node_id()) {
return completer.complete_error();
}
completer.complete_ok(std::move(hit));
});
return bridge.consumer.promise_or(fit::error());
}
fit::result<uint32_t> ExploreAction::SelectDescribableNodePromise(zx_koid_t view_koid, Hit& hit) {
if (!hit.has_node_id()) {
return fit::error();
}
auto hit_test_result_node =
action_context_->semantics_source->GetSemanticNode(view_koid, hit.node_id());
if (!hit_test_result_node || !hit_test_result_node->has_node_id()) {
FX_LOGS(WARNING) << "Invalid hit test result.";
return fit::error();
}
auto node_to_return = hit_test_result_node;
while (node_to_return && !NodeIsDescribable(node_to_return)) {
FX_DCHECK(node_to_return->has_node_id());
node_to_return =
action_context_->semantics_source->GetParentNode(view_koid, node_to_return->node_id());
}
if (node_to_return && node_to_return->has_node_id()) {
return fit::ok(node_to_return->node_id());
}
FX_LOGS(WARNING) << "No describable ancestor found for node " << hit_test_result_node->node_id();
return fit::error();
}
fit::promise<> ExploreAction::SetA11yFocusOrStopPromise(ScreenReaderContext::ScreenReaderMode mode,
zx_koid_t view_koid, uint32_t node_id) {
return fit::make_promise([this, mode, view_koid, node_id]() mutable -> fit::promise<> {
if (mode == ScreenReaderContext::ScreenReaderMode::kContinuousExploration) {
// If the new a11y focus to be set is the same as the existing one during a
// continuous exploration, this means that the same node would be spoken
// multiple times. Check if the focus is new before continuing.
auto* a11y_focus_manager = screen_reader_context_->GetA11yFocusManager();
auto focus = a11y_focus_manager->GetA11yFocus();
if (!focus) {
return fit::make_error_promise();
}
if (focus->view_ref_koid == view_koid && focus->node_id == node_id) {
return fit::make_error_promise();
}
}
return SetA11yFocusPromise(node_id, view_koid);
});
}
void ExploreAction::Run(ActionData process_data) {
auto promise = ExecuteHitTestingPromise(process_data)
.and_then([this, view_koid = process_data.current_view_koid](
Hit& hit) mutable -> fit::result<uint32_t> {
return SelectDescribableNodePromise(view_koid, hit);
})
.and_then([this, view_koid = process_data.current_view_koid,
mode = screen_reader_context_->mode()](
uint32_t& node_id) mutable -> fit::promise<> {
return SetA11yFocusOrStopPromise(mode, view_koid, node_id);
})
.and_then([this]() mutable -> fit::result<A11yFocusManager::A11yFocusInfo> {
auto* a11y_focus_manager = screen_reader_context_->GetA11yFocusManager();
auto focus = a11y_focus_manager->GetA11yFocus();
if (!focus) {
return fit::error();
}
return fit::ok(std::move(*focus));
})
.and_then([this](const A11yFocusManager::A11yFocusInfo& focus) mutable {
return BuildSpeechTaskFromNodePromise(focus.view_ref_koid, focus.node_id);
})
// Cancel any promises if this class goes out of scope.
.wrap_with(scope_);
auto* executor = screen_reader_context_->executor();
executor->schedule_task(std::move(promise));
}
} // namespace a11y