// 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 "ash/system/status_area_animation_controller.h"
#include "ash/system/notification_center/notification_center_tray.h"
#include "ash/system/tray/tray_container.h"
#include "base/memory/weak_ptr.h"
#include "ui/compositor/layer.h"
#include "ui/views/animation/animation_builder.h"
namespace ash {
StatusAreaAnimationController::StatusAreaAnimationController(
NotificationCenterTray* notification_center_tray)
: notification_center_tray_(notification_center_tray) {
if (!notification_center_tray_) {
return;
}
notification_center_tray_->AddTrayBackgroundViewObserver(this);
notification_center_tray_->AddNotificationCenterTrayObserver(this);
notification_center_tray_default_animation_enabler_ =
std::make_unique<base::ScopedClosureRunner>(
notification_center_tray->SetUseCustomVisibilityAnimations());
notification_center_tray_item_animation_enablers_ =
std::list<base::ScopedClosureRunner>();
}
StatusAreaAnimationController::~StatusAreaAnimationController() {
if (notification_center_tray_) {
notification_center_tray_->RemoveNotificationCenterTrayObserver(this);
notification_center_tray_->RemoveTrayBackgroundViewObserver(this);
}
}
void StatusAreaAnimationController::OnVisiblePreferredChanged(
bool visible_preferred) {
PerformAnimation(visible_preferred);
}
void StatusAreaAnimationController::OnAllTrayItemsAdded() {
// `NotificationCenterTray`'s `TrayItemView`s need to have their animations
// disabled ahead of the first time the `NotificationCenterTray` becomes
// visible. This is the right time to disable those animations because it is
// after all the `TrayItemView`s have been added to the tray but before the
// tray has had a chance to update its visibility from the default non-visible
// state.
DisableNotificationCenterTrayItemAnimations();
}
void StatusAreaAnimationController::
DisableNotificationCenterTrayItemAnimations() {
for (views::View* tray_item :
notification_center_tray_->tray_container()->children()) {
notification_center_tray_item_animation_enablers_.push_back(
static_cast<TrayItemView*>(tray_item)->DisableAnimation());
}
}
void StatusAreaAnimationController::
EnableNotificationCenterTrayItemAnimations() {
notification_center_tray_item_animation_enablers_.clear();
}
void StatusAreaAnimationController::PerformAnimation(bool visible) {
if (visible) {
notification_center_tray_->layer()->SetVisible(true);
views::AnimationBuilder()
.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
.OnAborted(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr || !ptr->notification_center_tray_) {
return;
}
ptr->notification_center_tray_->OnAnimationAborted();
// Don't enable notification center tray item animations if this
// show animation was interrupted by a hide animation.
if (!ptr->notification_center_tray_->visible_preferred()) {
return;
}
ptr->EnableNotificationCenterTrayItemAnimations();
},
weak_factory_.GetWeakPtr()))
.OnEnded(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr || !ptr->notification_center_tray_) {
return;
}
ptr->notification_center_tray_->OnAnimationEnded();
ptr->EnableNotificationCenterTrayItemAnimations();
},
weak_factory_.GetWeakPtr()))
.Once()
.Offset(base::Milliseconds(50))
.SetDuration(base::Milliseconds(150))
.SetOpacity(notification_center_tray_, 1, gfx::Tween::LINEAR);
} else {
DisableNotificationCenterTrayItemAnimations();
views::AnimationBuilder()
.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
.OnScheduled(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr) {
return;
}
ptr->is_hide_animation_scheduled_ = true;
},
weak_factory_.GetWeakPtr()))
.OnStarted(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr || !ptr->notification_center_tray_) {
return;
}
ptr->notification_center_tray_->OnHideAnimationStarted();
},
weak_factory_.GetWeakPtr()))
.OnAborted(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr || !ptr->notification_center_tray_) {
return;
}
ptr->is_hide_animation_scheduled_ = false;
ptr->notification_center_tray_->OnAnimationAborted();
ptr->ImmediatelyUpdateTrayItemVisibilities();
},
weak_factory_.GetWeakPtr()))
.OnEnded(base::BindOnce(
[](base::WeakPtr<StatusAreaAnimationController> ptr) {
if (!ptr || !ptr->notification_center_tray_) {
return;
}
ptr->is_hide_animation_scheduled_ = false;
ptr->notification_center_tray_->OnAnimationEnded();
ptr->ImmediatelyUpdateTrayItemVisibilities();
},
weak_factory_.GetWeakPtr()))
.Once()
.SetDuration(base::Milliseconds(150))
.SetOpacity(notification_center_tray_, 0, gfx::Tween::LINEAR)
.SetVisibility(notification_center_tray_, false);
}
}
void StatusAreaAnimationController::ImmediatelyUpdateTrayItemVisibilities() {
for (views::View* tray_item :
notification_center_tray_->tray_container()->children()) {
static_cast<TrayItemView*>(tray_item)->ImmediatelyUpdateVisibility();
}
}
} // namespace ash