blob: 8868f769a33dc34e57fa112a247fe54bb6dd0d75 [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 <zircon/status.h>
#include "src/lib/syslog/cpp/logger.h"
namespace a11y_manager {
const float kDefaultMagnificationZoomFactor = 1.0;
App::App(std::unique_ptr<sys::ComponentContext> context)
: startup_context_(std::move(context)),
// The following services publish themselves upon initialization.
semantics_manager_(startup_context_.get()),
tts_manager_(startup_context_.get()),
// For now, we use a simple Tts Engine which only logs the output.
// On initialization, it registers itself with the Tts manager.
color_transform_manager_(startup_context_.get()),
log_engine_(startup_context_.get()) {
startup_context_->outgoing()->AddPublicService(
settings_manager_bindings_.GetHandler(&settings_manager_));
startup_context_->outgoing()->AddPublicService(magnifier_bindings_.GetHandler(&magnifier_));
// Register a11y manager as a settings provider.
settings_manager_.RegisterSettingProvider(settings_provider_ptr_.NewRequest());
settings_provider_ptr_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::accessibility::settings::SettingsProvider"
<< zx_status_get_string(status);
});
// Connect to Root presenter service.
pointer_event_registry_ =
startup_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);
});
// Connect to setui.
setui_settings_ = startup_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);
});
// Start watching setui for current settings
WatchSetui();
}
App::~App() = default;
void App::SetState(A11yManagerState state) {
state_ = state;
// Screen Reader and Magnifier depend on gesture manager state being correct.
UpdateGestureManagerState();
UpdateScreenReaderState();
UpdateMagnifierState();
}
void App::UpdateScreenReaderState() {
// If this is used elsewhere, it should be moved into its own function.
semantics_manager_.SetSemanticsManagerEnabled(state_.screen_reader_enabled());
if (state_.screen_reader_enabled()) {
if (!screen_reader_) {
// TODO:(fxb/41769) We should move more of the enable/disable logic outside of screen reader.
screen_reader_ = std::make_unique<a11y::ScreenReader>(&semantics_manager_, &tts_manager_,
gesture_manager_.get());
}
} else {
screen_reader_.reset();
}
}
void App::UpdateMagnifierState() {
if (!state_.magnifier_enabled()) {
magnifier_.ZoomOutIfMagnified();
}
}
void App::UpdateGestureManagerState() {
bool no_active_users = !state_.magnifier_enabled() && !state_.screen_reader_enabled();
if (no_active_users) {
// Shut down and clean up if no users
gesture_manager_.reset();
} else {
// Initialize if not initialized
if (!gesture_manager_) {
gesture_manager_ = std::make_unique<a11y::GestureManager>();
pointer_event_registry_->Register(gesture_manager_->binding().NewBinding());
gesture_manager_->arena()->Add(&magnifier_);
}
// Current logic for event handling policy is as follows:
// Screen reader only: consume events
// Screen reader and magnifier enabled: consume events
// Just magnifier enabled: reject events
if (state_.screen_reader_enabled()) {
gesture_manager_->arena()->event_handling_policy(
a11y::GestureArena::EventHandlingPolicy::kConsumeEvents);
} else {
gesture_manager_->arena()->event_handling_policy(
a11y::GestureArena::EventHandlingPolicy::kRejectEvents);
}
}
}
void InternalSettingsCallback(fuchsia::accessibility::SettingsManagerStatus status) {
if (status == fuchsia::accessibility::SettingsManagerStatus::ERROR) {
FX_LOGS(ERROR) << "Error writing internal accessibility settings.";
}
}
// This currently ignores errors in the internal settings API. That API is being removed in favor of
// smaller feature-oriented APIs.
void App::UpdateInternalSettings(const fuchsia::settings::AccessibilitySettings& systemSettings) {
// New codepath for color transforms.
bool color_inversion =
systemSettings.has_color_inversion() ? systemSettings.color_inversion() : false;
fuchsia::accessibility::ColorCorrectionMode color_blindness_type =
systemSettings.has_color_correction()
? ConvertColorCorrection(systemSettings.color_correction())
: fuchsia::accessibility::ColorCorrectionMode::DISABLED;
color_transform_manager_.ChangeColorTransform(color_inversion, color_blindness_type);
// Everything below here in this method is old code for the legacy settings API.
// TODO(17180): Remove this code when nothing else depends on it.
if (systemSettings.has_color_inversion()) {
settings_provider_ptr_->SetColorInversionEnabled(systemSettings.color_inversion(),
InternalSettingsCallback);
}
if (systemSettings.has_color_correction()) {
switch (systemSettings.color_correction()) {
case fuchsia::settings::ColorBlindnessType::NONE:
settings_provider_ptr_->SetColorCorrection(
fuchsia::accessibility::ColorCorrection::DISABLED, InternalSettingsCallback);
break;
case fuchsia::settings::ColorBlindnessType::PROTANOMALY:
settings_provider_ptr_->SetColorCorrection(
fuchsia::accessibility::ColorCorrection::CORRECT_PROTANOMALY, InternalSettingsCallback);
break;
case fuchsia::settings::ColorBlindnessType::DEUTERANOMALY:
settings_provider_ptr_->SetColorCorrection(
fuchsia::accessibility::ColorCorrection::CORRECT_DEUTERANOMALY,
InternalSettingsCallback);
break;
case fuchsia::settings::ColorBlindnessType::TRITANOMALY:
settings_provider_ptr_->SetColorCorrection(
fuchsia::accessibility::ColorCorrection::CORRECT_TRITANOMALY, InternalSettingsCallback);
break;
}
}
}
void App::SetuiWatchCallback(fuchsia::settings::Accessibility_Watch_Result result) {
if (result.is_err()) {
FX_LOGS(ERROR) << "Error reading setui accessibility settings.";
} else if (result.is_response()) {
UpdateInternalSettings(result.response().settings);
SetState(state_.withSettings(result.response().settings));
}
WatchSetui();
}
void App::WatchSetui() { setui_settings_->Watch(fit::bind_member(this, &App::SetuiWatchCallback)); }
fuchsia::accessibility::SettingsPtr App::GetSettings() const {
return settings_manager_.GetSettings();
}
fuchsia::accessibility::ColorCorrectionMode App::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;
}
}
} // namespace a11y_manager