1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85

ash / system / phonehub / phone_hub_app_loading_icon.cc [blame]

// 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/phonehub/phone_hub_app_loading_icon.h"

#include "ash/style/ash_color_provider.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/views/animation/animation_builder.h"

namespace ash {

namespace {

// Constants for loading animation
constexpr float kAnimationOpacityHigh = 1.0f;
constexpr float kAnimationOpacityLow = 0.5f;
constexpr int kAnimationFadeDownDurationInMs = 500;
constexpr int kAnimationFadeUpDurationInMs = 500;

class LoadingCircle : public gfx::CanvasImageSource {
 public:
  explicit LoadingCircle(int size)
      : CanvasImageSource(AppIcon::GetRecommendedImageSize(size)) {}

  LoadingCircle(const LoadingCircle&) = delete;
  LoadingCircle& operator=(const LoadingCircle&) = delete;

  void Draw(gfx::Canvas* canvas) override {
    float radius = size().width() / 2.0f;
    cc::PaintFlags flags;
    flags.setStyle(cc::PaintFlags::kFill_Style);
    flags.setAntiAlias(true);
    flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
        AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive));
    canvas->DrawCircle(gfx::PointF(radius, radius), radius, flags);
  }
};

}  // namespace

AppLoadingIcon::AppLoadingIcon(int size)
    : AppIcon(gfx::Image(
                  gfx::CanvasImageSource::MakeImageSkia<LoadingCircle>(size)),
              size) {
  SetPaintToLayer();
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetFillsBoundsCompletely(false);
}

AppLoadingIcon::~AppLoadingIcon() = default;

void AppLoadingIcon::StartLoadingAnimation(
    std::optional<base::TimeDelta> initial_delay) {
  if (initial_delay) {
    animation_initial_delay_timer_.Start(
        FROM_HERE, *initial_delay,
        base::BindOnce(&AppLoadingIcon::StartLoadingAnimation,
                       base::Unretained(this),
                       /*initial_delay=*/std::nullopt));
    return;
  }

  views::AnimationBuilder builder;
  animation_abort_handle_ = builder.GetAbortHandle();
  builder.Repeatedly()
      .SetDuration(base::Milliseconds(kAnimationFadeDownDurationInMs))
      .SetOpacity(this, kAnimationOpacityLow, gfx::Tween::ACCEL_30_DECEL_20_85)
      .Then()
      .SetDuration(base::Milliseconds(kAnimationFadeUpDurationInMs))
      .SetOpacity(this, kAnimationOpacityHigh, gfx::Tween::LINEAR);
}

void AppLoadingIcon::StopLoadingAnimation() {
  animation_abort_handle_.reset();
  animation_initial_delay_timer_.Stop();
}

BEGIN_METADATA(AppLoadingIcon)
END_METADATA

}  // namespace ash