// Copyright 2022 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/camera/autozoom_toast_controller.h"
#include <algorithm>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/camera/autozoom_controller_impl.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_utils.h"
#include "ash/system/unified/unified_system_tray.h"
namespace ash {
AutozoomToastController::Delegate::Delegate() = default;
void AutozoomToastController::Delegate::AddAutozoomObserver(
AutozoomObserver* observer) {
Shell::Get()->autozoom_controller()->AddObserver(observer);
}
void AutozoomToastController::Delegate::RemoveAutozoomObserver(
AutozoomObserver* observer) {
Shell::Get()->autozoom_controller()->RemoveObserver(observer);
}
bool AutozoomToastController::Delegate::IsAutozoomEnabled() {
return Shell::Get()->autozoom_controller()->GetState() !=
cros::mojom::CameraAutoFramingState::OFF;
}
bool AutozoomToastController::Delegate::IsAutozoomControlEnabled() {
return Shell::Get()->autozoom_controller()->IsAutozoomControlEnabled();
}
AutozoomToastController::AutozoomToastController(
UnifiedSystemTray* tray,
std::unique_ptr<Delegate> delegate)
: tray_(tray), delegate_(std::move(delegate)) {
delegate_->AddAutozoomObserver(this);
}
AutozoomToastController::~AutozoomToastController() {
if (bubble_widget_ && !bubble_widget_->IsClosed())
bubble_widget_->CloseNow();
delegate_->RemoveAutozoomObserver(this);
}
void AutozoomToastController::ShowToast() {
// If the bubble already exists, update the content of the bubble and extend
// the autoclose timer.
if (bubble_widget_) {
UpdateToastView();
if (!mouse_hovered_)
StartAutoCloseTimer();
return;
}
tray_->CloseSecondaryBubbles();
TrayBubbleView::InitParams init_params =
CreateInitParamsForTrayBubble(tray_, /*anchor_to_shelf_corner=*/true);
init_params.type = TrayBubbleView::TrayBubbleType::kSecondaryBubble;
init_params.preferred_width = kAutozoomToastMinWidth;
// Use this controller as the delegate rather than the tray.
init_params.delegate = GetWeakPtr();
auto bubble_view = std::make_unique<TrayBubbleView>(init_params);
// bubble_view_ is owned by the view hierarchy and not by this class.
bubble_view_ = bubble_view.get();
toast_view_ =
bubble_view->AddChildView(std::make_unique<AutozoomToastView>(this));
bubble_widget_ =
views::BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
bubble_view_->InitializeAndShowBubble();
StartAutoCloseTimer();
UpdateToastView();
tray_->NotifySecondaryBubbleHeight(toast_view_->height());
}
void AutozoomToastController::HideToast() {
close_timer_.Stop();
if (!bubble_widget_ || bubble_widget_->IsClosed())
return;
bubble_widget_->Close();
tray_->NotifySecondaryBubbleHeight(0);
}
void AutozoomToastController::BubbleViewDestroyed() {
close_timer_.Stop();
bubble_view_ = nullptr;
bubble_widget_ = nullptr;
}
void AutozoomToastController::OnMouseEnteredView() {
close_timer_.Stop();
mouse_hovered_ = true;
}
void AutozoomToastController::OnMouseExitedView() {
StartAutoCloseTimer();
mouse_hovered_ = false;
}
std::u16string AutozoomToastController::GetAccessibleNameForBubble() {
if (!toast_view_)
return std::u16string();
return toast_view_->accessible_name();
}
void AutozoomToastController::HideBubble(const TrayBubbleView* bubble_view) {}
void AutozoomToastController::StartAutoCloseTimer() {
close_timer_.Stop();
// Don't start the timer if the toast is focused.
if (toast_view_ && toast_view_->IsButtonFocused())
return;
close_timer_.Start(
FROM_HERE,
Shell::Get()->accessibility_controller()->spoken_feedback().enabled()
? kSecondaryBubbleWithSpokenFeedbackDuration
: kSecondaryBubbleDuration,
this, &AutozoomToastController::HideToast);
}
void AutozoomToastController::OnAutozoomStateChanged(
cros::mojom::CameraAutoFramingState state) {
if (state == cros::mojom::CameraAutoFramingState::OFF) {
// TODO(pihsun): Should we hide toast immediately when the autozoom is
// toggled off while the toast is showing? Or should there be a "Autozoom
// is off" toast?
HideToast();
}
}
void AutozoomToastController::UpdateToastView() {
if (toast_view_) {
toast_view_->SetAutozoomEnabled(/*enabled=*/delegate_->IsAutozoomEnabled());
int width = std::clamp(toast_view_->GetPreferredSize().width(),
kAutozoomToastMinWidth, kAutozoomToastMaxWidth);
bubble_view_->SetPreferredWidth(width);
}
}
void AutozoomToastController::StopAutocloseTimer() {
close_timer_.Stop();
}
void AutozoomToastController::OnAutozoomControlEnabledChanged(bool enabled) {
if (enabled) {
if (delegate_->IsAutozoomEnabled()) {
ShowToast();
} else {
HideToast();
}
}
}
} // namespace ash