chromium/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc

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

#include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"

#include "ash/accessibility/accessibility_controller.h"
#include "ash/constants/tray_background_view_catalog.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/color/color_id.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"

namespace views {
class ImageView;
}

namespace ash {

namespace {

ui::ImageModel GetImageOnCurrentSelectToSpeakStatus(
    const SelectToSpeakState& select_to_speak_state) {
  const bool is_jelly_enabled = chromeos::features::IsJellyEnabled();
  // For Jelly: `kSelectToSpeakStateInactive` means the tray is inactive and
  // will have a different icon color. `kSelectToSpeakStateSelecting` and
  // `kSelectToSpeakStateSpeaking` means the tray is active.
  switch (select_to_speak_state) {
    case SelectToSpeakState::kSelectToSpeakStateInactive:
      return ui::ImageModel::FromVectorIcon(
          kSystemTraySelectToSpeakNewuiIcon,
          is_jelly_enabled
              ? static_cast<ui::ColorId>(cros_tokens::kCrosSysOnSurface)
              : kColorAshIconColorPrimary);
    case SelectToSpeakState::kSelectToSpeakStateSelecting:
      return ui::ImageModel::FromVectorIcon(
          kSystemTraySelectToSpeakActiveNewuiIcon,
          is_jelly_enabled ? static_cast<ui::ColorId>(
                                 cros_tokens::kCrosSysSystemOnPrimaryContainer)
                           : kColorAshIconColorPrimary);
    case SelectToSpeakState::kSelectToSpeakStateSpeaking:
      return ui::ImageModel::FromVectorIcon(
          kSystemTrayStopNewuiIcon,
          is_jelly_enabled ? static_cast<ui::ColorId>(
                                 cros_tokens::kCrosSysSystemOnPrimaryContainer)
                           : kColorAshIconColorPrimary);
  }
}

std::u16string GetTooltipTextOnCurrentSelectToSpeakStatus(
    const SelectToSpeakState& select_to_speak_state) {
  switch (select_to_speak_state) {
    case SelectToSpeakState::kSelectToSpeakStateInactive:
      return l10n_util::GetStringUTF16(
          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK);
    case SelectToSpeakState::kSelectToSpeakStateSelecting:
      return l10n_util::GetStringUTF16(
          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK_INSTRUCTIONS);
    case SelectToSpeakState::kSelectToSpeakStateSpeaking:
      return l10n_util::GetStringUTF16(
          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK_STOP_INSTRUCTIONS);
  }
}

}  // namespace

SelectToSpeakTray::SelectToSpeakTray(Shelf* shelf,
                                     TrayBackgroundViewCatalogName catalog_name)
    : TrayBackgroundView(shelf, catalog_name) {
  SetCallback(base::BindRepeating([](const ui::Event& event) {
    Shell::Get()->accessibility_controller()->RequestSelectToSpeakStateChange();
  }));

  const ui::ImageModel inactive_image = ui::ImageModel::FromVectorIcon(
      kSystemTraySelectToSpeakNewuiIcon, kColorAshIconColorPrimary);
  auto icon = std::make_unique<views::ImageView>();
  icon->SetImage(inactive_image);
  const int vertical_padding =
      (kTrayItemSize - inactive_image.Size().height()) / 2;
  const int horizontal_padding =
      (kTrayItemSize - inactive_image.Size().width()) / 2;
  icon->SetBorder(views::CreateEmptyBorder(
      gfx::Insets::VH(vertical_padding, horizontal_padding)));
  icon->SetTooltipText(l10n_util::GetStringUTF16(
      IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK));
  icon_ = tray_container()->AddChildView(std::move(icon));

  // Observe the accessibility controller state changes to know when Select to
  // Speak state is updated or when it is disabled/enabled.
  Shell::Get()->accessibility_controller()->AddObserver(this);
}

SelectToSpeakTray::~SelectToSpeakTray() {
  // This may be called during shutdown in which case some of the
  // ash objects may already be destroyed.
  auto* shell = Shell::Get();
  if (!shell) {
    return;
  }
  auto* accessibility_controller = shell->accessibility_controller();
  if (accessibility_controller) {
    accessibility_controller->RemoveObserver(this);
  }
}

void SelectToSpeakTray::Initialize() {
  TrayBackgroundView::Initialize();
  UpdateUXOnCurrentStatus();
}

std::u16string SelectToSpeakTray::GetAccessibleNameForTray() {
  return l10n_util::GetStringUTF16(
      IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME);
}

void SelectToSpeakTray::HandleLocaleChange() {
  const auto select_to_speak_state =
      Shell::Get()->accessibility_controller()->GetSelectToSpeakState();
  icon_->SetTooltipText(
      GetTooltipTextOnCurrentSelectToSpeakStatus(select_to_speak_state));
}

void SelectToSpeakTray::OnThemeChanged() {
  TrayBackgroundView::OnThemeChanged();
  UpdateIconOnColorChanges();
}

void SelectToSpeakTray::OnAccessibilityStatusChanged() {
  UpdateUXOnCurrentStatus();
}

void SelectToSpeakTray::OnSessionStateChanged(
    session_manager::SessionState state) {
  UpdateIconOnColorChanges();
}

void SelectToSpeakTray::UpdateUXOnCurrentStatus() {
  auto* accessibility_controller = Shell::Get()->accessibility_controller();
  if (!accessibility_controller->select_to_speak().enabled()) {
    SetVisiblePreferred(false);
    return;
  }
  const auto select_to_speak_state =
      accessibility_controller->GetSelectToSpeakState();
  icon_->SetImage(GetImageOnCurrentSelectToSpeakStatus(select_to_speak_state));
  icon_->SetTooltipText(
      GetTooltipTextOnCurrentSelectToSpeakStatus(select_to_speak_state));
  SetIsActive(accessibility_controller->GetSelectToSpeakState() !=
              SelectToSpeakState::kSelectToSpeakStateInactive);
  SetVisiblePreferred(true);
}

void SelectToSpeakTray::UpdateIconOnColorChanges() {
  auto* accessibility_controller = Shell::Get()->accessibility_controller();
  if (!visible_preferred() ||
      !accessibility_controller->select_to_speak().enabled()) {
    return;
  }
  const auto select_to_speak_state =
      accessibility_controller->GetSelectToSpeakState();
  icon_->SetImage(GetImageOnCurrentSelectToSpeakStatus(select_to_speak_state));
}

BEGIN_METADATA(SelectToSpeakTray);
END_METADATA

}  // namespace ash