chromium/chrome/browser/ash/accessibility/service/user_interface_impl.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/accessibility/service/user_interface_impl.h"

#include "ash/accessibility/accessibility_controller.h"
#include "ash/public/cpp/accessibility_focus_ring_info.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "content/public/common/color_parser.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/accessibility/public/mojom/user_interface.mojom-shared.h"
#include "services/accessibility/public/mojom/user_interface.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"

namespace ash {

UserInterfaceImpl::UserInterfaceImpl() = default;

UserInterfaceImpl::~UserInterfaceImpl() = default;

void UserInterfaceImpl::Bind(
    mojo::PendingReceiver<ax::mojom::UserInterface> ui_receiver) {
  ui_receivers_.Add(this, std::move(ui_receiver));
}

void UserInterfaceImpl::DarkenScreen(bool darken) {
  AccessibilityManager::Get()->SetDarkenScreen(darken);
}

void UserInterfaceImpl::OpenSettingsSubpage(const std::string& subpage) {
  AccessibilityManager::Get()->OpenSettingsSubpage(subpage);
}

void UserInterfaceImpl::ShowConfirmationDialog(
    const std::string& title,
    const std::string& description,
    const std::optional<std::string>& cancel_name,
    ShowConfirmationDialogCallback callback) {
  // If a dialog is already being shown we do not show a new one.
  // Instead, return false through the callback on the new dialog
  // to indicate it was closed without the user taking any action.
  // This is consistent with AcceleratorController, and matches
  // the implementation in accessibility_controller_impl.cc.
  if (show_confirmation_dialog_callback_) {
    std::move(callback).Run(false);
    return;
  }
  show_confirmation_dialog_callback_ = std::move(callback);
  ash::AccessibilityController::Get()->ShowConfirmationDialog(
      base::UTF8ToUTF16(title), base::UTF8ToUTF16(description),
      l10n_util::GetStringUTF16(IDS_APP_CONTINUE),
      cancel_name ? base::UTF8ToUTF16(cancel_name.value())
                  : l10n_util::GetStringUTF16(IDS_APP_CANCEL),
      base::BindOnce(&UserInterfaceImpl::OnDialogResult,
                     weak_factory_.GetWeakPtr(), /* confirmed */ true),
      base::BindOnce(&UserInterfaceImpl::OnDialogResult,
                     weak_factory_.GetWeakPtr(), /* not confirmed */ false),
      base::BindOnce(&UserInterfaceImpl::OnDialogResult,
                     weak_factory_.GetWeakPtr(), /* not confirmed */ false));
}

void UserInterfaceImpl::OnDialogResult(bool confirmed) {
  std::move(show_confirmation_dialog_callback_).Run(confirmed);
  show_confirmation_dialog_callback_.Reset();
}

void UserInterfaceImpl::SetFocusRings(
    std::vector<ax::mojom::FocusRingInfoPtr> focus_rings,
    ax::mojom::AssistiveTechnologyType at_type) {
  auto* accessibility_manager = AccessibilityManager::Get();
  for (auto& focus_ring_info : focus_rings) {
    auto focus_ring = std::make_unique<ash::AccessibilityFocusRingInfo>();
    focus_ring->behavior = ash::FocusRingBehavior::PERSIST;

    for (const gfx::Rect& rect : focus_ring_info->rects) {
      focus_ring->rects_in_screen.emplace_back(rect);
    }
    const std::string id = accessibility_manager->GetFocusRingId(
        at_type,
        focus_ring_info->id.has_value() ? focus_ring_info->id.value() : "");
    if (focus_ring_info->color.has_value()) {
      focus_ring->color = focus_ring_info->color.value();
    }
    if (focus_ring_info->secondary_color.has_value()) {
      focus_ring->secondary_color = focus_ring_info->secondary_color.value();
    }
    if (focus_ring_info->background_color.has_value()) {
      focus_ring->background_color = focus_ring_info->background_color.value();
    }
    switch (focus_ring_info->type) {
      case ax::mojom::FocusType::kSolid:
        focus_ring->type = ash::FocusRingType::SOLID;
        break;
      case ax::mojom::FocusType::kDashed:
        focus_ring->type = ash::FocusRingType::DASHED;
        break;
      case ax::mojom::FocusType::kGlow:
        focus_ring->type = ash::FocusRingType::GLOW;
        break;
    }
    if (focus_ring_info->stacking_order.has_value()) {
      switch (focus_ring_info->stacking_order.value()) {
        case ax::mojom::FocusRingStackingOrder::kAboveAccessibilityBubbles:
          focus_ring->stacking_order =
              ash::FocusRingStackingOrder::ABOVE_ACCESSIBILITY_BUBBLES;
          break;
        case ax::mojom::FocusRingStackingOrder::kBelowAccessibilityBubbles:
          focus_ring->stacking_order =
              ash::FocusRingStackingOrder::BELOW_ACCESSIBILITY_BUBBLES;
          break;
      }
    }
    // Update the touch exploration controller so that synthesized touch events
    // are anchored within the focused object.
    // NOTE: The final anchor point will be determined by the first rect of the
    // final focus ring.
    if (!focus_ring->rects_in_screen.empty()) {
      accessibility_manager->SetTouchAccessibilityAnchorPoint(
          focus_ring->rects_in_screen[0].CenterPoint());
    }

    accessibility_manager->SetFocusRing(id, std::move(focus_ring));
  }
}

void UserInterfaceImpl::SetHighlights(const std::vector<gfx::Rect>& rects,
                                      SkColor color) {
  AccessibilityManager::Get()->SetHighlights(rects, color);
}

void UserInterfaceImpl::SetVirtualKeyboardVisible(bool is_visible) {
  AccessibilityController::Get()->SetVirtualKeyboardVisible(is_visible);
}

}  // namespace ash