blob: 220027464911efc0d33fec9e54baf47567162359 [file] [log] [blame]
// Copyright 2018 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/bin/a11y_manager/app.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include "src/ui/a11y/lib/screen_reader/focus/a11y_focus_manager_impl.h"
#include "src/ui/a11y/lib/screen_reader/screen_reader_context.h"
#include "src/ui/a11y/lib/util/util.h"
#include "src/ui/a11y/lib/view/view_coordinate_converter.h"
namespace {
template <typename T>
auto to_underlying(T value) {
return static_cast<std::underlying_type_t<T>>(value);
}
} // namespace
namespace a11y_manager {
void A11yManagerInitializationState ::SetI18nProfileReady() {
has_i18N_profile_ = true;
if (IsInitialized() && callback_) {
callback_();
}
}
void A11yManagerInitializationState ::SetA11yViewReady() {
is_a11y_view_initialized_ = true;
if (IsInitialized() && callback_) {
callback_();
}
}
App::App(sys::ComponentContext* context, a11y::ViewManager* view_manager,
a11y::TtsManager* tts_manager, a11y::ColorTransformManager* color_transform_manager,
a11y::GestureListenerRegistry* gesture_listener_registry,
a11y::BootInfoManager* boot_info_manager,
a11y::ScreenReaderContextFactory* screen_reader_context_factory,
inspect::Node inspect_node)
: context_(context),
view_manager_(view_manager),
tts_manager_(tts_manager),
color_transform_manager_(color_transform_manager),
gesture_listener_registry_(gesture_listener_registry),
screen_reader_context_factory_(screen_reader_context_factory),
inspect_node_(std::move(inspect_node)),
inspect_property_intl_property_provider_disconnected_(
inspect_node_.CreateBool(kIntlPropertyProviderDisconnectedInspectName, false)),
screen_reader_enabled_(inspect_node_.CreateBool(kScreenReaderEnabledInspectName,
state_.screen_reader_enabled())),
magnifier_enabled_(
inspect_node_.CreateBool(kMagnifierEnabledInspectName, state_.magnifier_enabled())),
color_inversion_enabled_(inspect_node_.CreateBool(kColorInversionEnabledInspectName,
state_.color_inversion_enabled())),
color_correction_mode_(inspect_node_.CreateUint(
kColorCorrectionModeInspectName, to_underlying(state_.color_correction_mode()))) {
FX_DCHECK(context);
FX_DCHECK(view_manager);
FX_DCHECK(tts_manager);
FX_DCHECK(color_transform_manager);
FX_DCHECK(gesture_listener_registry_);
FX_DCHECK(boot_info_manager);
// The screen reader should announce that it is on at boot iff the boot was
// user-initiated.
state_.set_announce_screen_reader_enabled(boot_info_manager->LastRebootWasUserInitiated());
context->outgoing()->AddPublicService(semantics_manager_bindings_.GetHandler(view_manager_));
context->outgoing()->AddPublicService(
virtualkeyboard_registry_bindings_.GetHandler(view_manager_));
context->outgoing()->AddPublicService(
gesture_listener_registry_bindings_.GetHandler(gesture_listener_registry_));
auto magnifier_delegate =
std::static_pointer_cast<a11y::Magnifier2::Delegate>(view_manager->flatland_a11y_view());
FX_CHECK(magnifier_delegate);
magnifier_ = std::make_unique<a11y::Magnifier2>(magnifier_delegate);
// Inits Focus Chain focuser support / listening Focus Chain updates.
focus_chain_manager_ = std::make_unique<a11y::FocusChainManager>(view_manager_->a11y_view());
// |focus_chain_manager_| listens for Focus Chain updates. Connects to the listener registry and
// start listening.
fuchsia::ui::focus::FocusChainListenerRegistryPtr focus_chain_listener_registry =
context->svc()->Connect<fuchsia::ui::focus::FocusChainListenerRegistry>();
focus_chain_listener_registry.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::ui::focus::FocusChainListenerRegistry: "
<< zx_status_get_string(status);
});
auto focus_chain_listener_handle =
focus_chain_listener_bindings_.AddBinding(focus_chain_manager_.get());
focus_chain_listener_registry->Register(focus_chain_listener_handle.Bind());
// Connect to setui.
setui_settings_ = context->svc()->Connect<fuchsia::settings::Accessibility>();
setui_settings_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::settings::Accessibility: "
<< zx_status_get_string(status);
});
initialization_state_.SetOnA11yManagerInitializedCallback([this]() { FinishSetUp(); });
// Connects to property provider to retrieve the current locale. Also adds a handler for the event
// to process when the locale changes.
property_provider_ = context->svc()->Connect<fuchsia::intl::PropertyProvider>();
property_provider_.set_error_handler([this](zx_status_t status) {
FX_LOGS(INFO) << "Error from fuchsia::intl::PropertyProvider: " << zx_status_get_string(status);
if (status == ZX_ERR_PEER_CLOSED) {
FX_LOGS(INFO) << "Using the default locale: en-US";
inspect_property_intl_property_provider_disconnected_.Set(true);
fuchsia::intl::Profile default_profile;
this->i18n_profile_ = std::move(default_profile);
this->i18n_profile_->mutable_locales()->push_back({.id = "en-US"});
if (!this->initialization_state_.IsInitialized()) {
this->initialization_state_.SetI18nProfileReady();
}
}
});
property_provider_.events().OnChange =
fit::bind_member<&App::PropertyProviderOnChangeHandler>(this);
// Fetches the initial locale.
// When the locale is returned, marks this object as initialized and ready to process requests.
// This is necessary because the Locale is a must-have information that needs to be present to
// build some elements.
property_provider_->GetProfile([this](fuchsia::intl::Profile profile) mutable {
this->i18n_profile_ = std::move(profile);
if (!this->initialization_state_.IsInitialized()) {
this->initialization_state_.SetI18nProfileReady();
}
});
auto a11y_view = view_manager_->a11y_view();
FX_DCHECK(a11y_view);
a11y_view->add_scene_ready_callback([this]() {
this->initialization_state_.SetA11yViewReady();
return true;
});
}
App::~App() = default;
void App::FinishSetUp() {
FX_DCHECK(initialization_state_.IsInitialized());
FX_DCHECK(i18n_profile_) << "App is being initialized without i18n profile from user.";
// Configures a View Coordinate Converter. Done at this point because the a11y view is guaranteed
// to be initialized.
auto a11y_view = view_manager_->a11y_view();
FX_DCHECK(a11y_view);
auto view_ref = a11y_view->view_ref();
FX_DCHECK(view_ref);
auto view_ref_koid = a11y::GetKoid(*view_ref);
auto view_coordinate_converter = std::make_unique<a11y::ViewCoordinateConverter>(
context_->svc()->Connect<fuchsia::ui::observation::scope::Registry>(), view_ref_koid);
view_manager_->SetViewCoordinateConverter(std::move(view_coordinate_converter));
// Note that initializing the gesture manager here is important:
// 1. It is done when the a11y view is initialized for sure;
// 2. It obtains the touch source, and starts watching for pointer events, even when a11y features
// are not on. This is **critical**: a11y may block touch input if it does not start participating
// right away in gesture disambiguation.
gesture_manager_ = std::make_unique<a11y::GestureManagerV2>(a11y_view->TakeTouchSource());
// Start watching setui for current settings. Must be the last step in the initialization because
// this may turn on / off a11y features, and these features depend on the things initialized
// above.
WatchSetui();
}
void App::SetState(A11yManagerState state) {
state_ = state;
UpdateScreenReaderState();
UpdateMagnifierState();
UpdateColorTransformState();
// May rely on screen reader existence.
UpdateGestureManagerState();
UpdateInspectState();
// The first call to SetState() will set the screen reader enabled setting to its
// value at boot time. This first call to SetState() should result in screen
// reader output iff the screen reader is enabled at boot AND the boot is
// user-initiated. Once this initial value has been set, all subsequent
// enables of the screen reader should be announced.
state_.set_announce_screen_reader_enabled(true);
}
void App::UpdateScreenReaderState() {
// If this is used elsewhere, it should be moved into its own function.
view_manager_->SetSemanticsEnabled(state_.screen_reader_enabled());
if (state_.screen_reader_enabled()) {
if (!screen_reader_) {
screen_reader_ = InitializeScreenReader();
}
} else {
screen_reader_.reset();
}
}
void App::UpdateMagnifierState() {
if (!state_.magnifier_enabled()) {
magnifier_->ZoomOutIfMagnified();
}
}
void App::UpdateColorTransformState() {
bool color_inversion = state_.color_inversion_enabled();
fuchsia::accessibility::ColorCorrectionMode color_blindness_type = state_.color_correction_mode();
color_transform_manager_->ChangeColorTransform(color_inversion, color_blindness_type);
}
void App::UpdateGestureManagerState() {
GestureState new_state = {.screen_reader_gestures = state_.screen_reader_enabled(),
.magnifier_gestures = state_.magnifier_enabled()};
if (new_state == gesture_state_)
return;
gesture_state_ = new_state;
// For now the easiest way to properly set up all gestures with the right priorities is to clear
// the gesture manager when the gestures change.
gesture_manager_->Clear();
if (!gesture_state_.has_any()) {
return;
}
// The ordering of these recognizers is significant, as it signifies priority.
if (gesture_state_.magnifier_gestures) {
magnifier_->BindGestures(gesture_manager_->gesture_handler());
}
if (gesture_state_.screen_reader_gestures) {
screen_reader_->BindGestures(gesture_manager_->gesture_handler());
gesture_manager_->gesture_handler()->ConsumeAll();
}
}
void App::UpdateInspectState() {
screen_reader_enabled_.Set(state_.screen_reader_enabled());
magnifier_enabled_.Set(state_.magnifier_enabled());
color_inversion_enabled_.Set(state_.color_inversion_enabled());
color_correction_mode_.Set(to_underlying(state_.color_correction_mode()));
}
bool App::GestureState::operator==(GestureState o) const {
return screen_reader_gestures == o.screen_reader_gestures &&
magnifier_gestures == o.magnifier_gestures;
}
void App::SetuiWatchCallback(fuchsia::settings::AccessibilitySettings settings) {
SetState(state_.withSettings(settings));
WatchSetui();
}
void App::WatchSetui() { setui_settings_->Watch(fit::bind_member<&App::SetuiWatchCallback>(this)); }
// Converts setui color blindess type to the relevant accessibility color correction mode.
fuchsia::accessibility::ColorCorrectionMode ConvertColorCorrection(
fuchsia::settings::ColorBlindnessType color_blindness_type) {
switch (color_blindness_type) {
case fuchsia::settings::ColorBlindnessType::PROTANOMALY:
return fuchsia::accessibility::ColorCorrectionMode::CORRECT_PROTANOMALY;
case fuchsia::settings::ColorBlindnessType::DEUTERANOMALY:
return fuchsia::accessibility::ColorCorrectionMode::CORRECT_DEUTERANOMALY;
case fuchsia::settings::ColorBlindnessType::TRITANOMALY:
return fuchsia::accessibility::ColorCorrectionMode::CORRECT_TRITANOMALY;
case fuchsia::settings::ColorBlindnessType::NONE:
// fall through
default:
return fuchsia::accessibility::ColorCorrectionMode::DISABLED;
}
}
A11yManagerState A11yManagerState::withSettings(
const fuchsia::settings::AccessibilitySettings& systemSettings) {
A11yManagerState state = *this;
if (systemSettings.has_screen_reader()) {
state.screen_reader_enabled_ = systemSettings.screen_reader();
}
if (systemSettings.has_enable_magnification()) {
state.magnifier_enabled_ = systemSettings.enable_magnification();
}
if (systemSettings.has_color_inversion()) {
state.color_inversion_enabled_ = systemSettings.color_inversion();
}
if (systemSettings.has_color_correction()) {
state.color_correction_mode_ = ConvertColorCorrection(systemSettings.color_correction());
}
return state;
}
std::unique_ptr<a11y::ScreenReader> App::InitializeScreenReader() {
auto a11y_focus_manager = std::make_unique<a11y::A11yFocusManagerImpl>(
focus_chain_manager_.get(), focus_chain_manager_.get(), view_manager_, view_manager_,
view_manager_->flatland_a11y_view(), inspect_node_.CreateChild("focus_manager"));
std::string locale_id = "en-US";
if (i18n_profile_ && i18n_profile_->has_locales() && !i18n_profile_->locales().empty()) {
locale_id = i18n_profile_->locales()[0].id;
}
auto screen_reader_context = screen_reader_context_factory_->CreateScreenReaderContext(
std::move(a11y_focus_manager), tts_manager_, view_manager_, locale_id);
auto screen_reader = std::make_unique<a11y::ScreenReader>(
std::move(screen_reader_context), view_manager_, view_manager_, gesture_listener_registry_,
tts_manager_, state_.announce_screen_reader_enabled());
view_manager_->GetSemanticsEventManager()->Register(
screen_reader->GetSemanticsEventListenerWeakPtr());
return screen_reader;
}
void App::PropertyProviderOnChangeHandler() {
property_provider_->GetProfile([this](fuchsia::intl::Profile profile) {
this->i18n_profile_ = std::move(profile);
if (state_.screen_reader_enabled()) {
// Reset screen_reader_ to force re-initialization.
screen_reader_.reset();
// Close the old engine connection.
tts_manager_->CloseEngine();
UpdateScreenReaderState();
// Clear screen reader gesture state to force update.
gesture_state_.screen_reader_gestures = false;
UpdateGestureManagerState();
}
});
}
} // namespace a11y_manager