#include "ash/system/accessibility/dictation_button_tray.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/tray_background_view_catalog.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/public/cpp/shelf_config.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/progress_indicator/progress_indicator.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "components/prefs/pref_service.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/ime/text_input_client.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/compositor/layer.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
namespace ash {
namespace {
ui::ImageModel GetIconImage(bool active, bool enabled) {
ui::ColorId color_id =
enabled ? (active ? cros_tokens::kCrosSysSystemOnPrimaryContainer
: cros_tokens::kCrosSysOnSurface)
: cros_tokens::kCrosSysSecondary;
return active
? ui::ImageModel::FromVectorIcon(kDictationOnNewuiIcon, color_id)
: ui::ImageModel::FromVectorIcon(kDictationOffNewuiIcon, color_id);
}
bool IsDictationActive() {
return Shell::Get()->accessibility_controller()->dictation_active();
}
}
DictationButtonTray::DictationButtonTray(
Shelf* shelf,
TrayBackgroundViewCatalogName catalog_name)
: TrayBackgroundView(shelf, catalog_name), download_progress_(0) {
SetCallback(base::BindRepeating(
&DictationButtonTray::OnDictationButtonPressed, base::Unretained(this)));
Shell* shell = Shell::Get();
ui::TextInputClient* client =
shell->window_tree_host_manager()->input_method()->GetTextInputClient();
in_text_input_ =
(client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE);
SetEnabled(in_text_input_);
SetIsActive(false);
const ui::ImageModel icon_image =
GetIconImage(false, GetEnabled());
const int vertical_padding = (kTrayItemSize - icon_image.Size().height()) / 2;
const int horizontal_padding =
(kTrayItemSize - icon_image.Size().height()) / 2;
auto icon = std::make_unique<views::ImageView>();
icon->SetImage(icon_image);
icon->SetBorder(views::CreateEmptyBorder(
gfx::Insets::VH(vertical_padding, horizontal_padding)));
icon->SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION));
icon_ = tray_container()->AddChildView(std::move(icon));
shell->AddShellObserver(this);
shell->accessibility_controller()->AddObserver(this);
shell->session_controller()->AddObserver(this);
GetViewAccessibility().SetName(
l10n_util::GetStringUTF16(IDS_ASH_DICTATION_BUTTON_ACCESSIBLE_NAME));
}
DictationButtonTray::~DictationButtonTray() {
Shell* shell = Shell::Get();
if (!shell) {
return;
}
shell->RemoveShellObserver(this);
auto* accessibility_controller = shell->accessibility_controller();
if (accessibility_controller) {
accessibility_controller->RemoveObserver(this);
}
auto* session_controller = shell->session_controller();
if (session_controller) {
session_controller->RemoveObserver(this);
}
input_method_observation_.Reset();
}
void DictationButtonTray::OnDictationStarted() {
UpdateStateAndIcon(true, GetEnabled());
}
void DictationButtonTray::OnDictationEnded() {
UpdateStateAndIcon(false, GetEnabled());
}
void DictationButtonTray::OnAccessibilityStatusChanged() {
UpdateVisibility();
CheckDictationStatusAndUpdateIcon();
}
void DictationButtonTray::OnSessionStateChanged(
session_manager::SessionState state) {
CheckDictationStatusAndUpdateIcon();
}
void DictationButtonTray::Initialize() {
TrayBackgroundView::Initialize();
UpdateVisibility();
}
void DictationButtonTray::ClickedOutsideBubble(const ui::LocatedEvent& event) {}
void DictationButtonTray::UpdateTrayItemColor(bool is_active) {
if (progress_indicator_) {
progress_indicator_->SetColorId(
is_active ? cros_tokens::kCrosSysSystemOnPrimaryContainer
: cros_tokens::kCrosSysPrimary);
}
}
void DictationButtonTray::HandleLocaleChange() {
icon_->SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION));
}
void DictationButtonTray::HideBubbleWithView(
const TrayBubbleView* bubble_view) {
}
void DictationButtonTray::OnThemeChanged() {
TrayBackgroundView::OnThemeChanged();
if (progress_indicator_)
progress_indicator_->InvalidateLayer();
}
void DictationButtonTray::Layout(PassKey) {
LayoutSuperclass<TrayBackgroundView>(this);
UpdateProgressIndicatorBounds();
}
void DictationButtonTray::HideBubble(const TrayBubbleView* bubble_view) {
}
void DictationButtonTray::OnCaretBoundsChanged(
const ui::TextInputClient* client) {
TextInputChanged(client);
}
void DictationButtonTray::OnTextInputStateChanged(
const ui::TextInputClient* client) {
TextInputChanged(client);
}
void DictationButtonTray::UpdateOnSpeechRecognitionDownloadChanged(
int download_progress) {
if (!visible_preferred())
return;
const bool download_in_progress =
download_progress > 0 && download_progress < 100;
const bool is_dictation_enabled = !download_in_progress && in_text_input_;
UpdateStateAndIcon(IsDictationActive(), is_dictation_enabled);
icon_->SetTooltipText(l10n_util::GetStringUTF16(
download_in_progress
? IDS_ASH_ACCESSIBILITY_DICTATION_BUTTON_TOOLTIP_SODA_DOWNLOADING
: IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION));
download_progress_ = download_progress;
if (!progress_indicator_) {
progress_indicator_ =
ProgressIndicator::CreateDefaultInstance(base::BindRepeating(
[](DictationButtonTray* tray) -> std::optional<float> {
const int progress = tray->download_progress();
return (progress > 0 && progress < 100)
? progress / 100.f
: ProgressIndicator::kProgressComplete;
},
base::Unretained(this)));
progress_indicator_->SetInnerIconVisible(false);
layer()->Add(progress_indicator_->CreateLayer(base::BindRepeating(
[](const DictationButtonTray* self, ui::ColorId color_id) {
return self->GetColorProvider()->GetColor(color_id);
},
base::Unretained(this))));
UpdateProgressIndicatorBounds();
UpdateTrayItemColor(is_active());
}
progress_indicator_->InvalidateLayer();
}
void DictationButtonTray::OnDictationButtonPressed(const ui::Event& event) {
Shell::Get()->accessibility_controller()->ToggleDictationFromSource(
DictationToggleSource::kButton);
CheckDictationStatusAndUpdateIcon();
}
void DictationButtonTray::UpdateProgressIndicatorBounds() {
if (progress_indicator_)
progress_indicator_->layer()->SetBounds(GetBackgroundBounds());
}
void DictationButtonTray::UpdateVisibility() {
const bool is_visible =
Shell::Get()->accessibility_controller()->dictation().enabled();
if (is_visible && !input_method_observation_.IsObserving()) {
input_method_observation_.Observe(
Shell::Get()->window_tree_host_manager()->input_method());
} else if (!is_visible) {
input_method_observation_.Reset();
}
SetVisiblePreferred(is_visible);
}
void DictationButtonTray::UpdateStateAndIcon(bool is_dictation_active,
bool is_dictation_enabled) {
const bool should_update_icon = is_active() != is_dictation_active ||
GetEnabled() != is_dictation_enabled;
SetIsActive(is_dictation_active);
SetEnabled(is_dictation_enabled);
if (should_update_icon) {
icon_->SetImage(GetIconImage(is_dictation_active, is_dictation_enabled));
}
}
void DictationButtonTray::CheckDictationStatusAndUpdateIcon() {
UpdateStateAndIcon(IsDictationActive(), GetEnabled());
}
void DictationButtonTray::TextInputChanged(const ui::TextInputClient* client) {
in_text_input_ =
client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
const bool is_dictation_enabled =
(download_progress_ <= 0 || download_progress_ >= 100) && in_text_input_;
UpdateStateAndIcon(IsDictationActive(), is_dictation_enabled);
}
BEGIN_METADATA(DictationButtonTray)
END_METADATA
}