blob: 4e16894dec7909f88856670496bddad6c875bcce [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/screen_reader.h"
#include <lib/fit/promise.h>
#include <lib/syslog/cpp/macros.h>
#include <memory>
#include "fuchsia/accessibility/gesture/cpp/fidl.h"
#include "src/ui/a11y/lib/screen_reader/change_range_value_action.h"
#include "src/ui/a11y/lib/screen_reader/change_semantic_level_action.h"
#include "src/ui/a11y/lib/screen_reader/default_action.h"
#include "src/ui/a11y/lib/screen_reader/explore_action.h"
#include "src/ui/a11y/lib/screen_reader/linear_navigation_action.h"
#include "src/ui/a11y/lib/screen_reader/recover_a11y_focus_action.h"
#include "src/ui/a11y/lib/screen_reader/three_finger_swipe_action.h"
namespace a11y {
namespace {
constexpr char kNextActionLabel[] = "Next Action";
constexpr char kPreviousActionLabel[] = "Previous Action";
constexpr char kExploreActionLabel[] = "Explore Action";
constexpr char kDefaultActionLabel[] = "Default Action";
constexpr char kThreeFingerUpSwipeActionLabel[] = "Three finger Up Swipe Action";
constexpr char kThreeFingerDownSwipeActionLabel[] = "Three finger Down Swipe Action";
constexpr char kThreeFingerLeftSwipeActionLabel[] = "Three finger Left Swipe Action";
constexpr char kThreeFingerRightSwipeActionLabel[] = "Three finger Right Swipe Action";
constexpr char kPreviousSemanticLevelActionLabel[] = "Previous Semantic Level Action";
constexpr char kNextSemanticLevelActionLabel[] = "Next Semantic Level Action";
constexpr char kIncrementRangeValueActionLabel[] = "Increment Range Value Action";
constexpr char kDecrementRangeValueActionLabel[] = "Decrement Range Value Action";
constexpr char kRecoverA11YFocusActionLabel[] = "Recover A11Y Focus Action";
// Returns the appropriate next action based on the semantic level.
std::string NextActionFromSemanticLevel(ScreenReaderContext::SemanticLevel semantic_level) {
switch (semantic_level) {
case ScreenReaderContext::SemanticLevel::kNormalNavigation:
return std::string(kNextActionLabel);
case ScreenReaderContext::SemanticLevel::kAdjustValue:
return std::string(kIncrementRangeValueActionLabel);
default:
// Other semantic levels are not implemented yet, so return an empty action name.
return std::string("");
}
return std::string("");
}
// Returns the appropriate previous action based on the semantic level.
std::string PreviousActionFromSemanticLevel(ScreenReaderContext::SemanticLevel semantic_level) {
switch (semantic_level) {
case ScreenReaderContext::SemanticLevel::kNormalNavigation:
return std::string(kPreviousActionLabel);
case ScreenReaderContext::SemanticLevel::kAdjustValue:
return std::string(kDecrementRangeValueActionLabel);
default:
// Other semantic levels are not implemented yet, so return an empty action name.
return std::string("");
}
return std::string("");
}
} // namespace
// Private implementation of the registry for the Screen Reader use only. Note that only the Screen
// Reader will be able to access the methods implemented here.
class ScreenReader::ScreenReaderActionRegistryImpl : public ScreenReaderActionRegistry {
public:
ScreenReaderActionRegistryImpl() = default;
~ScreenReaderActionRegistryImpl() override = default;
void AddAction(std::string name, std::unique_ptr<ScreenReaderAction> action) override {
actions_.insert({std::move(name), std::move(action)});
}
ScreenReaderAction* GetActionByName(const std::string& name) override {
auto action_it = actions_.find(name);
if (action_it == actions_.end()) {
FX_LOGS(ERROR) << "No Screen Reader action found with string :" << name;
return nullptr;
}
return action_it->second.get();
}
private:
std::unordered_map<std::string, std::unique_ptr<ScreenReaderAction>> actions_;
};
ScreenReader::ScreenReader(std::unique_ptr<ScreenReaderContext> context,
SemanticsSource* semantics_source,
GestureListenerRegistry* gesture_listener_registry,
TtsManager* tts_manager, bool announce_screen_reader_enabled)
: ScreenReader(std::move(context), semantics_source, gesture_listener_registry, tts_manager,
announce_screen_reader_enabled,
std::make_unique<ScreenReaderActionRegistryImpl>()) {}
ScreenReader::ScreenReader(std::unique_ptr<ScreenReaderContext> context,
SemanticsSource* semantics_source,
GestureListenerRegistry* gesture_listener_registry,
TtsManager* tts_manager, bool announce_screen_reader_enabled,
std::unique_ptr<ScreenReaderActionRegistry> action_registry)
: context_(std::move(context)),
gesture_listener_registry_(gesture_listener_registry),
tts_manager_(tts_manager),
action_registry_(std::move(action_registry)),
weak_ptr_factory_(this) {
action_context_ = std::make_unique<ScreenReaderAction::ActionContext>();
action_context_->semantics_source = semantics_source;
InitializeActions();
FX_DCHECK(tts_manager_);
if (announce_screen_reader_enabled) {
tts_manager->RegisterTTSEngineReadyCallback(
[this]() { SpeakMessage(fuchsia::intl::l10n::MessageIds::SCREEN_READER_ON_HINT); });
}
context_->speaker()->set_epitaph(fuchsia::intl::l10n::MessageIds::SCREEN_READER_OFF_HINT);
}
ScreenReader::~ScreenReader() { tts_manager_->UnregisterTTSEngineReadyCallback(); }
void ScreenReader::BindGestures(a11y::GestureHandler* gesture_handler) {
// Add gestures with higher priority earlier than gestures with lower priority.
// Add a three finger Up swipe recognizer. This corresponds to a physical three finger Right
// swipe.
bool gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kThreeFingerRightSwipeActionLabel, action_data);
},
GestureHandler::kThreeFingerUpSwipe);
FX_DCHECK(gesture_bind_status);
// Add a three finger Down swipe recognizer. This corresponds to a physical three finger Left
// swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kThreeFingerLeftSwipeActionLabel, action_data);
},
GestureHandler::kThreeFingerDownSwipe);
FX_DCHECK(gesture_bind_status);
// Add a three finger Left swipe recognizer. This corresponds to a physical three finger Up swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kThreeFingerUpSwipeActionLabel, action_data);
},
GestureHandler::kThreeFingerLeftSwipe);
FX_DCHECK(gesture_bind_status);
// Add a three finger Right swipe recognizer. This corresponds to a physical three finger Down
// swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kThreeFingerDownSwipeActionLabel, action_data);
},
GestureHandler::kThreeFingerRightSwipe);
FX_DCHECK(gesture_bind_status);
// Add one finger Down swipe recognizer. This corresponds to a physical one finger Left swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
auto action_name = PreviousActionFromSemanticLevel(context_->semantic_level());
ExecuteAction(action_name, action_data);
},
GestureHandler::kOneFingerDownSwipe);
FX_DCHECK(gesture_bind_status);
// Add one finger Up swipe recognizer. This corresponds to a physical one finger Right swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
auto action_name = NextActionFromSemanticLevel(context_->semantic_level());
ExecuteAction(action_name, action_data);
},
GestureHandler::kOneFingerUpSwipe);
FX_DCHECK(gesture_bind_status);
// Add one finger Left swipe recognizer. This corresponds to a physical one finger Up swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kPreviousSemanticLevelActionLabel, action_data);
},
GestureHandler::kOneFingerLeftSwipe);
FX_DCHECK(gesture_bind_status);
// Add one finger Right swipe recognizer. This corresponds to a physical one finger Down swipe.
gesture_bind_status = gesture_handler->BindSwipeAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kNextSemanticLevelActionLabel, action_data);
},
GestureHandler::kOneFingerRightSwipe);
FX_DCHECK(gesture_bind_status);
// Add OneFingerDoubleTap recognizer.
gesture_bind_status = gesture_handler->BindOneFingerDoubleTapAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kDefaultActionLabel, action_data);
});
FX_DCHECK(gesture_bind_status);
// Add OneFingerSingleTap recognizer.
gesture_bind_status = gesture_handler->BindOneFingerSingleTapAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kExploreActionLabel, action_data);
});
FX_DCHECK(gesture_bind_status);
// Add OneFingerDrag recognizer.
gesture_bind_status = gesture_handler->BindOneFingerDragAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
context_->set_mode(ScreenReaderContext::ScreenReaderMode::kContinuousExploration);
}, /*on_start*/
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
FX_DCHECK(context_->mode() ==
ScreenReaderContext::ScreenReaderMode::kContinuousExploration);
ScreenReaderAction::ActionData action_data;
action_data.current_view_koid = viewref_koid;
action_data.local_point = point;
ExecuteAction(kExploreActionLabel, action_data);
}, /*on_update*/
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
FX_DCHECK(context_->mode() ==
ScreenReaderContext::ScreenReaderMode::kContinuousExploration);
context_->set_mode(ScreenReaderContext::ScreenReaderMode::kNormal);
} /*on_complete*/);
FX_DCHECK(gesture_bind_status);
// Add TwoFingerSingleTap recognizer.
gesture_bind_status = gesture_handler->BindTwoFingerSingleTapAction(
[this](zx_koid_t viewref_koid, fuchsia::math::PointF point) {
// Cancel any outstanding speech.
auto promise = context_->speaker()->CancelTts();
auto* executor = context_->executor();
executor->schedule_task(std::move(promise));
});
FX_DCHECK(gesture_bind_status);
}
void ScreenReader::InitializeActions() {
action_registry_->AddAction(kExploreActionLabel, std::make_unique<a11y::ExploreAction>(
action_context_.get(), context_.get()));
action_registry_->AddAction(kDefaultActionLabel, std::make_unique<a11y::DefaultAction>(
action_context_.get(), context_.get()));
action_registry_->AddAction(
kPreviousActionLabel,
std::make_unique<a11y::LinearNavigationAction>(
action_context_.get(), context_.get(), a11y::LinearNavigationAction::kPreviousAction));
action_registry_->AddAction(kNextActionLabel, std::make_unique<a11y::LinearNavigationAction>(
action_context_.get(), context_.get(),
a11y::LinearNavigationAction::kNextAction));
action_registry_->AddAction(
kNextSemanticLevelActionLabel,
std::make_unique<ChangeSemanticLevelAction>(ChangeSemanticLevelAction::Direction::kForward,
action_context_.get(), context_.get()));
action_registry_->AddAction(
kPreviousSemanticLevelActionLabel,
std::make_unique<ChangeSemanticLevelAction>(ChangeSemanticLevelAction::Direction::kBackward,
action_context_.get(), context_.get()));
action_registry_->AddAction(
kIncrementRangeValueActionLabel,
std::make_unique<ChangeRangeValueAction>(
action_context_.get(), context_.get(),
ChangeRangeValueAction::ChangeRangeValueActionType::kIncrementAction));
action_registry_->AddAction(
kDecrementRangeValueActionLabel,
std::make_unique<ChangeRangeValueAction>(
action_context_.get(), context_.get(),
ChangeRangeValueAction::ChangeRangeValueActionType::kDecrementAction));
action_registry_->AddAction(kThreeFingerUpSwipeActionLabel,
std::make_unique<a11y::ThreeFingerSwipeAction>(
action_context_.get(), context_.get(), gesture_listener_registry_,
fuchsia::accessibility::gesture::Type::THREE_FINGER_SWIPE_UP));
action_registry_->AddAction(kThreeFingerDownSwipeActionLabel,
std::make_unique<a11y::ThreeFingerSwipeAction>(
action_context_.get(), context_.get(), gesture_listener_registry_,
fuchsia::accessibility::gesture::Type::THREE_FINGER_SWIPE_DOWN));
action_registry_->AddAction(kThreeFingerLeftSwipeActionLabel,
std::make_unique<a11y::ThreeFingerSwipeAction>(
action_context_.get(), context_.get(), gesture_listener_registry_,
fuchsia::accessibility::gesture::Type::THREE_FINGER_SWIPE_LEFT));
action_registry_->AddAction(kThreeFingerRightSwipeActionLabel,
std::make_unique<a11y::ThreeFingerSwipeAction>(
action_context_.get(), context_.get(), gesture_listener_registry_,
fuchsia::accessibility::gesture::Type::THREE_FINGER_SWIPE_RIGHT));
action_registry_->AddAction(
kRecoverA11YFocusActionLabel,
std::make_unique<RecoverA11YFocusAction>(action_context_.get(), context_.get()));
}
bool ScreenReader::ExecuteAction(const std::string& action_name,
ScreenReaderAction::ActionData action_data) {
auto* action = action_registry_->GetActionByName(action_name);
if (!action) {
return false;
}
action->Run(action_data);
return true;
}
void ScreenReader::SpeakMessage(fuchsia::intl::l10n::MessageIds message_id) {
auto* speaker = context_->speaker();
auto promise =
speaker->SpeakMessageByIdPromise(message_id, {.interrupt = true, .save_utterance = false});
context_->executor()->schedule_task(std::move(promise));
}
fxl::WeakPtr<SemanticsEventListener> ScreenReader::GetSemanticsEventListenerWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void ScreenReader::OnEvent(SemanticsEventInfo event_info) {
switch (event_info.event_type) {
case SemanticsEventType::kSemanticTreeUpdated: {
ScreenReaderAction::ActionData action_data;
if (event_info.view_ref_koid) {
action_data.current_view_koid = *event_info.view_ref_koid;
}
ExecuteAction(kRecoverA11YFocusActionLabel, action_data);
break;
}
case SemanticsEventType::kUnknown:
break;
}
}
} // namespace a11y