| // 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/change_range_value_action.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include "fuchsia/accessibility/semantics/cpp/fidl.h" |
| #include "src/ui/a11y/lib/screen_reader/default_action.h" |
| #include "src/ui/a11y/lib/screen_reader/util/util.h" |
| |
| namespace a11y { |
| namespace { |
| |
| using fuchsia::accessibility::semantics::Action; |
| |
| } // namespace |
| |
| ChangeRangeValueAction::ChangeRangeValueAction(ActionContext* action_context, |
| ScreenReaderContext* screen_reader_context, |
| ChangeRangeValueActionType action) |
| |
| : ScreenReaderAction(action_context, screen_reader_context), range_value_action_(action) {} |
| |
| ChangeRangeValueAction::~ChangeRangeValueAction() = default; |
| |
| void ChangeRangeValueAction::Run(a11y::gesture_util_v2::GestureContext gesture_context) { |
| auto a11y_focus = screen_reader_context_->GetA11yFocusManager()->GetA11yFocus(); |
| if (!a11y_focus) { |
| FX_LOGS(INFO) << "Change Range Value Action: No view is in focus."; |
| return; |
| } |
| |
| FX_DCHECK(action_context_->semantics_source); |
| |
| // Get the node in focus. |
| const zx_koid_t focused_koid = a11y_focus->view_ref_koid; |
| const uint32_t focused_node_id = a11y_focus->node_id; |
| |
| const fuchsia::accessibility::semantics::Node* focused_node; |
| focused_node = action_context_->semantics_source->GetSemanticNode(focused_koid, focused_node_id); |
| |
| if (!focused_node || !focused_node->has_node_id()) { |
| return; |
| } |
| |
| Action semantic_action; |
| switch (range_value_action_) { |
| case ChangeRangeValueActionType::kDecrementAction: |
| semantic_action = Action::DECREMENT; |
| break; |
| case ChangeRangeValueActionType::kIncrementAction: |
| semantic_action = Action::INCREMENT; |
| break; |
| default: |
| break; |
| } |
| |
| auto old_value = GetSliderValue(*focused_node); |
| |
| auto promise = |
| ExecuteAccessibilityActionPromise(a11y_focus->view_ref_koid, a11y_focus->node_id, |
| semantic_action) |
| .and_then([this, focused_koid, focused_node_id, old_value]() { |
| // Ask the screen reader to read the next value of the slider. |
| screen_reader_context_->set_on_node_update_callback([this, focused_koid, |
| focused_node_id, old_value]() { |
| // Get current focus. |
| auto a11y_focus = screen_reader_context_->GetA11yFocusManager()->GetA11yFocus(); |
| if (!a11y_focus) { |
| return; |
| } |
| |
| // If the focused node has changed, then we shouldn't |
| // try to read the new slider value. |
| if (a11y_focus->view_ref_koid != focused_koid || |
| a11y_focus->node_id != focused_node_id) { |
| return; |
| } |
| |
| auto new_focused_node = action_context_->semantics_source->GetSemanticNode( |
| a11y_focus->view_ref_koid, a11y_focus->node_id); |
| |
| // If the focused node no longer exists, then we |
| // shouldn't try to read the new slider value. |
| if (!new_focused_node) { |
| return; |
| } |
| |
| // If the slider value hasn't changed, or is no longer |
| // valid, then we shouldn't try to read it. |
| auto new_value = GetSliderValue(*new_focused_node); |
| if (new_value == old_value || new_value.empty()) { |
| return; |
| } |
| |
| // Read the new slider value. |
| auto* speaker = screen_reader_context_->speaker(); |
| FX_DCHECK(speaker); |
| |
| fuchsia::accessibility::tts::Utterance utterance; |
| utterance.set_message(new_value); |
| auto promise = speaker->SpeakMessagePromise(std::move(utterance), {.interrupt = true}) |
| .wrap_with(scope_); |
| auto* executor = screen_reader_context_->executor(); |
| executor->schedule_task(std::move(promise)); |
| }); |
| return fpromise::make_ok_promise(); |
| }) |
| // 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 |