chromium/ash/shelf/shelf_background_animator.cc

// Copyright 2016 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/shelf/shelf_background_animator.h"

#include <algorithm>
#include <memory>

#include "ash/animation/animation_change_type.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_background_animator_observer.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/style/ash_color_id.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "base/time/time.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/animation/tween.h"

namespace ash {

ShelfBackgroundAnimator::AnimationValues::AnimationValues() = default;

ShelfBackgroundAnimator::AnimationValues::~AnimationValues() = default;

void ShelfBackgroundAnimator::AnimationValues::UpdateCurrentValues(double t) {
  current_color_ =
      gfx::Tween::ColorValueBetween(t, initial_color_, target_color_);
}

void ShelfBackgroundAnimator::AnimationValues::SetTargetValues(
    SkColor target_color) {
  initial_color_ = current_color_;
  target_color_ = target_color;
}

bool ShelfBackgroundAnimator::AnimationValues::InitialValuesEqualTargetValuesOf(
    const AnimationValues& other) const {
  return initial_color_ == other.target_color_;
}

ShelfBackgroundAnimator::ShelfBackgroundAnimator(
    Shelf* shelf,
    WallpaperControllerImpl* wallpaper_controller)
    : shelf_(shelf), wallpaper_controller_(wallpaper_controller) {}

ShelfBackgroundAnimator::~ShelfBackgroundAnimator() {
  if (wallpaper_controller_)
    wallpaper_controller_->RemoveObserver(this);
  if (shelf_)
    shelf_->RemoveObserver(this);
}

void ShelfBackgroundAnimator::Init(ShelfBackgroundType background_type) {
  if (wallpaper_controller_)
    wallpaper_controller_->AddObserver(this);
  if (shelf_)
    shelf_->AddObserver(this);

  // Initialize animators so that adding observers get notified with consistent
  // values.
  AnimateBackground(background_type, AnimationChangeType::IMMEDIATE);
}

void ShelfBackgroundAnimator::AddObserver(
    ShelfBackgroundAnimatorObserver* observer) {
  observers_.AddObserver(observer);
  NotifyObserver(observer);
}

void ShelfBackgroundAnimator::RemoveObserver(
    ShelfBackgroundAnimatorObserver* observer) {
  observers_.RemoveObserver(observer);
}

void ShelfBackgroundAnimator::NotifyObserver(
    ShelfBackgroundAnimatorObserver* observer) {
  observer->UpdateShelfBackground(shelf_background_values_.current_color());
}

void ShelfBackgroundAnimator::PaintBackground(
    ShelfBackgroundType background_type,
    AnimationChangeType change_type) {
  if (target_background_type_ == background_type &&
      change_type == AnimationChangeType::ANIMATE) {
    return;
  }

  AnimateBackground(background_type, change_type);
}

void ShelfBackgroundAnimator::AnimationProgressed(
    const gfx::Animation* animation) {
  DCHECK_EQ(animation, animator_.get());
  SetAnimationValues(animation->GetCurrentValue());
}

void ShelfBackgroundAnimator::AnimationEnded(const gfx::Animation* animation) {
  DCHECK_EQ(animation, animator_.get());
  SetAnimationValues(animation->GetCurrentValue());
  animator_.reset();

  for (auto& observer : observers_)
    observer.OnShelfBackgroundAnimationEnded();
}

void ShelfBackgroundAnimator::OnWallpaperColorsChanged() {
  AnimateBackground(target_background_type_, AnimationChangeType::ANIMATE);
}

void ShelfBackgroundAnimator::OnBackgroundTypeChanged(
    ShelfBackgroundType background_type,
    AnimationChangeType change_type) {
  PaintBackground(background_type, change_type);
}

void ShelfBackgroundAnimator::NotifyObservers() {
  for (auto& observer : observers_)
    NotifyObserver(&observer);
}

void ShelfBackgroundAnimator::AnimateBackground(
    ShelfBackgroundType background_type,
    AnimationChangeType change_type) {
  StopAnimator();

  if (change_type == AnimationChangeType::IMMEDIATE) {
    animator_.reset();
    SetTargetValues(background_type);
    SetAnimationValues(1.0);
  } else if (CanReuseAnimator(background_type)) {
    // |animator_| should not be null here as CanReuseAnimator() returns false
    // when it is null.
    if (animator_->IsShowing())
      animator_->Hide();
    else
      animator_->Show();
  } else {
    CreateAnimator(background_type);
    SetTargetValues(background_type);
    animator_->Show();
  }

  if (target_background_type_ != background_type) {
    previous_background_type_ = target_background_type_;
    target_background_type_ = background_type;
  }
}

bool ShelfBackgroundAnimator::CanReuseAnimator(
    ShelfBackgroundType background_type) const {
  if (!animator_)
    return false;

  AnimationValues target_shelf_background_values;
  GetTargetValues(background_type, &target_shelf_background_values);

  return previous_background_type_ == background_type &&
         shelf_background_values_.InitialValuesEqualTargetValuesOf(
             target_shelf_background_values);
}

void ShelfBackgroundAnimator::CreateAnimator(
    ShelfBackgroundType background_type) {
  base::TimeDelta duration;

  switch (background_type) {
    case ShelfBackgroundType::kDefaultBg:
    case ShelfBackgroundType::kHomeLauncher:
      duration = base::Milliseconds(500);
      break;
    case ShelfBackgroundType::kMaximized:
    case ShelfBackgroundType::kOobe:
    case ShelfBackgroundType::kLogin:
    case ShelfBackgroundType::kLoginNonBlurredWallpaper:
    case ShelfBackgroundType::kOverview:
    case ShelfBackgroundType::kInApp:
      duration = base::Milliseconds(250);
      break;
  }

  animator_ = std::make_unique<gfx::SlideAnimation>(this);
  animator_->SetSlideDuration(
      ui::ScopedAnimationDurationScaleMode::duration_multiplier() * duration);
}

void ShelfBackgroundAnimator::StopAnimator() {
  if (animator_)
    animator_->Stop();
}

void ShelfBackgroundAnimator::SetTargetValues(
    ShelfBackgroundType background_type) {
  GetTargetValues(background_type, &shelf_background_values_);
}

void ShelfBackgroundAnimator::GetTargetValues(
    ShelfBackgroundType background_type,
    AnimationValues* shelf_background_values) const {
  shelf_background_values->SetTargetValues(GetBackgroundColor(background_type));
}

SkColor ShelfBackgroundAnimator::GetBackgroundColor(
    ShelfBackgroundType background_type) const {
  if (!shelf_)
    return shelf_background_values_.current_color();

  const auto* shelf_widget = shelf_->shelf_widget();
  DCHECK(shelf_widget);

  SkColor shelf_target_color =
      ShelfConfig::Get()->GetDefaultShelfColor(shelf_widget);
  switch (background_type) {
    case ShelfBackgroundType::kDefaultBg:
    case ShelfBackgroundType::kHomeLauncher:
      shelf_target_color =
          ShelfConfig::Get()->GetDefaultShelfColor(shelf_widget);
      break;
    case ShelfBackgroundType::kMaximized:
    case ShelfBackgroundType::kInApp:
      shelf_target_color =
          ShelfConfig::Get()->GetMaximizedShelfColor(shelf_widget);
      break;
    case ShelfBackgroundType::kOverview:
      shelf_target_color =
          display::Screen::GetScreen()->InTabletMode()
              ? ShelfConfig::Get()->GetMaximizedShelfColor(shelf_widget)
              : ShelfConfig::Get()->GetDefaultShelfColor(shelf_widget);
      break;
    case ShelfBackgroundType::kOobe:
      shelf_target_color = SK_ColorTRANSPARENT;
      break;
    case ShelfBackgroundType::kLogin:
      shelf_target_color = SK_ColorTRANSPARENT;
      break;
    case ShelfBackgroundType::kLoginNonBlurredWallpaper:
      shelf_target_color = shelf_->shelf_widget()->GetColorProvider()->GetColor(
          kColorAshShieldAndBase80);
      break;
  }
  return shelf_target_color;
}

void ShelfBackgroundAnimator::CompleteAnimationForTesting() {
  if (animator_)
    animator_->End();
}

void ShelfBackgroundAnimator::SetAnimationValues(double t) {
  DCHECK_GE(t, 0.0);
  DCHECK_LE(t, 1.0);
  shelf_background_values_.UpdateCurrentValues(t);
  NotifyObservers();
}

}  // namespace ash