blob: 84c4c6e2a2e51be838f14d7ca12036c5629c7d97 [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.h"
#include "src/ui/a11y/lib/screen_reader/screen_reader_context.h"
namespace a11y_manager {
const float kDefaultMagnificationZoomFactor = 1.0;
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)
: 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)) {
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(magnifier_bindings_.GetHandler(&magnifier_));
context->outgoing()->AddPublicService(
gesture_listener_registry_bindings_.GetHandler(gesture_listener_registry_));
// Connect to Root presenter service.
pointer_event_registry_ =
context->svc()->Connect<fuchsia::ui::input::accessibility::PointerEventRegistry>();
pointer_event_registry_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::ui::input::accessibility::PointerEventRegistry"
<< zx_status_get_string(status);
});
// Inits Focus Chain focuser support / listening Focus Chain updates.
focuser_registry_ = context->svc()->Connect<fuchsia::ui::views::accessibility::FocuserRegistry>();
focuser_registry_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::ui::views::accessibility::FocuserRegistry"
<< zx_status_get_string(status);
});
fuchsia::ui::views::FocuserPtr focuser;
focuser_registry_->RegisterFocuser(focuser.NewRequest());
focus_chain_manager_ =
std::make_unique<a11y::FocusChainManager>(std::move(focuser), view_manager_);
// |focus_chain_manager_| listens for Focus Chain updates. Connects to the listener registry and
// start listening.
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);
});
// 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(ERROR) << "Error from fuchsia::intl::PropertyProvider" << zx_status_get_string(status);
if (status == ZX_ERR_PEER_CLOSED) {
FX_LOGS(ERROR) << "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 (!is_initialized_) {
FinishSetUp();
}
}
});
property_provider_.events().OnChange =
fit::bind_member(this, &App::PropertyProviderOnChangeHandler);
// 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);
// Note: FinishSetUp() is invoked here because having the locale is the only must-have
// information at the moment we need before we can proceed with regular flow. This should be
// moved elsewhere if ever comes a new condition that needs to be met to consider this object
// fully-initialized.
FinishSetUp();
});
}
App::~App() = default;
void App::FinishSetUp() {
FX_DCHECK(i18n_profile_) << "App is being initialized without i18n profile from user.";
// Start watching setui for current settings
WatchSetui();
is_initialized_ = true;
}
void App::SetState(A11yManagerState state) {
state_ = state;
UpdateScreenReaderState();
UpdateMagnifierState();
UpdateColorTransformState();
// May rely on screen reader existence.
UpdateGestureManagerState();
// 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());
view_manager_->SetAnnotationsEnabled(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 rebuild
// the gesture manager when the gestures change.
if (!gesture_state_.has_any()) {
// Shut down and clean up if no users
gesture_manager_.reset();
} else {
gesture_manager_ = std::make_unique<a11y::GestureManager>();
pointer_event_registry_->Register(gesture_manager_->binding().NewBinding());
// The ordering of these recognizers is significant, as it signifies priority.
if (gesture_state_.magnifier_gestures) {
gesture_manager_->arena()->Add(&magnifier_);
}
if (gesture_state_.screen_reader_gestures) {
screen_reader_->BindGestures(gesture_manager_->gesture_handler());
gesture_manager_->gesture_handler()->ConsumeAll();
}
}
}
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(this, &App::SetuiWatchCallback)); }
// 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::A11yFocusManager>(
focus_chain_manager_.get(), focus_chain_manager_.get(), view_manager_,
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_, locale_id);
auto screen_reader = std::make_unique<a11y::ScreenReader>(
std::move(screen_reader_context), 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()) {
if (i18n_profile_ && i18n_profile_->has_locales() && !i18n_profile_->locales().empty()) {
const auto& locale_id = i18n_profile_->locales()[0].id;
screen_reader_->context()->set_locale_id(locale_id);
}
}
});
}
} // namespace a11y_manager