Implement AssistantProgressIndicator motion spec.

Still missing tweens. Need to discuss with UX about potentially revising
specs to use pre-defined tweens.

See bug for demo.

Bug: b:111573788
Change-Id: I7d94416afa834d83aafa7e1e5f8eb9d6efe44333
Reviewed-on: https://chromium-review.googlesource.com/1144197
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: Xiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577765}
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc
index 667af5fa..ef82ec08b 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -36,6 +36,8 @@
 constexpr int kPreferredHeightDip = 48;
 
 // Animation.
+constexpr base::TimeDelta kAnimationFadeInDelay =
+    base::TimeDelta::FromMilliseconds(83);
 constexpr base::TimeDelta kAnimationFadeInDuration =
     base::TimeDelta::FromMilliseconds(100);
 constexpr base::TimeDelta kAnimationFadeOutDuration =
@@ -258,11 +260,13 @@
                    gfx::Transform(), kAnimationTransformInDuration,
                    gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
            // Animate opacity to 100% with delay.
-           assistant::util::CreateLayerAnimationSequenceWithDelay(
+           assistant::util::CreateLayerAnimationSequence(
+               ui::LayerAnimationElement::CreatePauseElement(
+                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                   kAnimationFadeInDelay),
                assistant::util::CreateOpacityElement(
                    1.f, kAnimationFadeInDuration,
-                   gfx::Tween::Type::FAST_OUT_LINEAR_IN),
-               /*delay=*/kAnimationFadeOutDuration)});
+                   gfx::Tween::Type::FAST_OUT_LINEAR_IN))});
 
       // When switching to keyboard input modality, we focus the textfield.
       textfield_->RequestFocus();
@@ -290,11 +294,13 @@
                    gfx::Transform(), kAnimationTransformInDuration,
                    gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
            // Animate opacity to 100% with delay.
-           assistant::util::CreateLayerAnimationSequenceWithDelay(
+           assistant::util::CreateLayerAnimationSequence(
+               ui::LayerAnimationElement::CreatePauseElement(
+                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+                   kAnimationFadeInDelay),
                assistant::util::CreateOpacityElement(
                    1.f, kAnimationFadeInDuration,
-                   gfx::Tween::Type::FAST_OUT_LINEAR_IN),
-               /*delay=*/kAnimationFadeOutDuration)});
+                   gfx::Tween::Type::FAST_OUT_LINEAR_IN))});
       break;
     }
     case InputModality::kStylus:
diff --git a/ash/assistant/ui/main_stage/assistant_progress_indicator.cc b/ash/assistant/ui/main_stage/assistant_progress_indicator.cc
index d7d1ceb..6eff37731 100644
--- a/ash/assistant/ui/main_stage/assistant_progress_indicator.cc
+++ b/ash/assistant/ui/main_stage/assistant_progress_indicator.cc
@@ -4,47 +4,61 @@
 
 #include "ash/assistant/ui/main_stage/assistant_progress_indicator.h"
 
-#include <memory>
-
+#include "ash/assistant/util/animation_util.h"
+#include "base/bind.h"
+#include "base/time/time.h"
+#include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animator.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/views/background.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace ash {
 
 namespace {
 
+// Appearance.
 constexpr int kDotCount = 3;
-constexpr int kDotSizeDip = 6;
+constexpr float kDotLargeSizeDip = 9.f;
+constexpr float kDotSmallSizeDip = 6.f;
 constexpr int kSpacingDip = 4;
 
-// DotView ---------------------------------------------------------------------
+// Animation.
+constexpr base::TimeDelta kAnimationOffsetDuration =
+    base::TimeDelta::FromMilliseconds(216);
+constexpr base::TimeDelta kAnimationPauseDuration =
+    base::TimeDelta::FromMilliseconds(500);
+constexpr base::TimeDelta kAnimationScaleUpDuration =
+    base::TimeDelta::FromMilliseconds(266);
+constexpr base::TimeDelta kAnimationScaleDownDuration =
+    base::TimeDelta::FromMilliseconds(450);
 
-class DotView : public views::View {
+// Transformation.
+constexpr float kScaleFactor = kDotLargeSizeDip / kDotSmallSizeDip;
+constexpr float kTranslationDip = -(kDotLargeSizeDip - kDotSmallSizeDip) / 2.f;
+
+// DotBackground ---------------------------------------------------------------
+
+class DotBackground : public views::Background {
  public:
-  DotView() = default;
-  ~DotView() override = default;
+  DotBackground() = default;
+  ~DotBackground() override = default;
 
-  // views::View:
-  gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(kDotSizeDip, GetHeightForWidth(kDotSizeDip));
-  }
-
-  int GetHeightForWidth(int width) const override { return kDotSizeDip; }
-
-  void OnPaint(gfx::Canvas* canvas) override {
+  void Paint(gfx::Canvas* canvas, views::View* view) const override {
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
     flags.setColor(gfx::kGoogleGrey300);
 
-    const gfx::Rect& b = GetContentsBounds();
+    const gfx::Rect& b = view->GetContentsBounds();
     const gfx::Point center = gfx::Point(b.width() / 2, b.height() / 2);
+    const int radius = std::min(b.width() / 2, b.height() / 2);
 
-    canvas->DrawCircle(center, kDotSizeDip / 2, flags);
+    canvas->DrawCircle(center, radius, flags);
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(DotView);
+  DISALLOW_COPY_AND_ASSIGN(DotBackground);
 };
 
 }  // namespace
@@ -61,9 +75,72 @@
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), kSpacingDip));
 
-  // Dots.
-  for (int i = 0; i < kDotCount; ++i)
-    AddChildView(new DotView());
+  // Initialize dots.
+  for (int i = 0; i < kDotCount; ++i) {
+    views::View* dot_view = new views::View();
+    dot_view->SetBackground(std::make_unique<DotBackground>());
+    dot_view->SetPreferredSize(gfx::Size(kDotSmallSizeDip, kDotSmallSizeDip));
+
+    // Dots will animate on their own layers.
+    dot_view->SetPaintToLayer();
+    dot_view->layer()->SetFillsBoundsOpaquely(false);
+
+    AddChildView(dot_view);
+  }
+}
+
+void AssistantProgressIndicator::AddedToWidget() {
+  VisibilityChanged(/*starting_from=*/this, /*is_visible=*/visible());
+}
+
+void AssistantProgressIndicator::RemovedFromWidget() {
+  VisibilityChanged(/*starting_from=*/this, /*is_visible=*/false);
+}
+
+void AssistantProgressIndicator::VisibilityChanged(views::View* starting_from,
+                                                   bool is_visible) {
+  if (!is_visible) {
+    // Stop all animations.
+    for (int i = 0; i < child_count(); ++i) {
+      child_at(i)->layer()->GetAnimator()->StopAnimating();
+    }
+    return;
+  }
+
+  using namespace assistant::util;
+
+  // The animation performs scaling on the child views. In order to give the
+  // illusion that scaling is being performed about the center of the view as
+  // the transformation origin, we also need to perform a translation.
+  gfx::Transform transform;
+  transform.Translate(kTranslationDip, kTranslationDip);
+  transform.Scale(kScaleFactor, kScaleFactor);
+
+  for (int i = 0; i < child_count(); ++i) {
+    views::View* view = child_at(i);
+
+    if (i > 0) {
+      // Schedule the animations to start after an offset.
+      view->layer()->GetAnimator()->SchedulePauseForProperties(
+          i * kAnimationOffsetDuration,
+          ui::LayerAnimationElement::AnimatableProperty::TRANSFORM);
+    }
+
+    // Schedule transformation animation.
+    view->layer()->GetAnimator()->ScheduleAnimation(
+        CreateLayerAnimationSequence(
+            // Animate scale up.
+            CreateTransformElement(transform, kAnimationScaleUpDuration),
+            // Animate scale down.
+            CreateTransformElement(gfx::Transform(),
+                                   kAnimationScaleDownDuration),
+            // Pause before next iteration.
+            ui::LayerAnimationElement::CreatePauseElement(
+                ui::LayerAnimationElement::AnimatableProperty::TRANSFORM,
+                kAnimationPauseDuration),
+            // Animation parameters.
+            {.is_cyclic = true}));
+  }
 }
 
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/assistant/ui/main_stage/assistant_progress_indicator.h b/ash/assistant/ui/main_stage/assistant_progress_indicator.h
index 43496d1..0dd8f74 100644
--- a/ash/assistant/ui/main_stage/assistant_progress_indicator.h
+++ b/ash/assistant/ui/main_stage/assistant_progress_indicator.h
@@ -5,6 +5,8 @@
 #ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_PROGRESS_INDICATOR_H_
 #define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_PROGRESS_INDICATOR_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "ui/views/view.h"
 
@@ -15,6 +17,11 @@
   AssistantProgressIndicator();
   ~AssistantProgressIndicator() override;
 
+  // views::View:
+  void AddedToWidget() override;
+  void RemovedFromWidget() override;
+  void VisibilityChanged(views::View* starting_from, bool is_visible) override;
+
  private:
   void InitLayout();
 
diff --git a/ash/assistant/util/animation_util.cc b/ash/assistant/util/animation_util.cc
index acc7519..dbc90f7 100644
--- a/ash/assistant/util/animation_util.cc
+++ b/ash/assistant/util/animation_util.cc
@@ -14,7 +14,34 @@
 
 ui::LayerAnimationSequence* CreateLayerAnimationSequence(
     std::unique_ptr<ui::LayerAnimationElement> a,
-    std::unique_ptr<ui::LayerAnimationElement> b) {
+    const LayerAnimationSequenceParams& params) {
+  return CreateLayerAnimationSequence(std::move(a), nullptr, nullptr, nullptr,
+                                      params);
+}
+
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    const LayerAnimationSequenceParams& params) {
+  return CreateLayerAnimationSequence(std::move(a), std::move(b), nullptr,
+                                      nullptr, params);
+}
+
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    std::unique_ptr<ui::LayerAnimationElement> c,
+    const LayerAnimationSequenceParams& params) {
+  return CreateLayerAnimationSequence(std::move(a), std::move(b), std::move(c),
+                                      nullptr, params);
+}
+
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    std::unique_ptr<ui::LayerAnimationElement> c,
+    std::unique_ptr<ui::LayerAnimationElement> d,
+    const LayerAnimationSequenceParams& params) {
   ui::LayerAnimationSequence* layer_animation_sequence =
       new ui::LayerAnimationSequence();
 
@@ -23,24 +50,13 @@
   if (b)
     layer_animation_sequence->AddElement(std::move(b));
 
-  return layer_animation_sequence;
-}
+  if (c)
+    layer_animation_sequence->AddElement(std::move(c));
 
-ui::LayerAnimationSequence* CreateLayerAnimationSequenceWithDelay(
-    std::unique_ptr<ui::LayerAnimationElement> layer_animation_element,
-    const base::TimeDelta& delay) {
-  DCHECK(delay.InMilliseconds() >= 0);
+  if (d)
+    layer_animation_sequence->AddElement(std::move(d));
 
-  ui::LayerAnimationSequence* layer_animation_sequence =
-      new ui::LayerAnimationSequence();
-
-  if (!delay.is_zero()) {
-    layer_animation_sequence->AddElement(
-        ui::LayerAnimationElement::CreatePauseElement(
-            layer_animation_element->properties(), delay));
-  }
-
-  layer_animation_sequence->AddElement(std::move(layer_animation_element));
+  layer_animation_sequence->set_is_cyclic(params.is_cyclic);
 
   return layer_animation_sequence;
 }
diff --git a/ash/assistant/util/animation_util.h b/ash/assistant/util/animation_util.h
index 5d55e23..8b0c65c 100644
--- a/ash/assistant/util/animation_util.h
+++ b/ash/assistant/util/animation_util.h
@@ -22,19 +22,45 @@
 namespace assistant {
 namespace util {
 
-// Creates a LayerAnimationSequence containing the specified
-// LayerAnimationElements. The method caller assumes ownership of the
-// returned pointer.
-ui::LayerAnimationSequence* CreateLayerAnimationSequence(
-    std::unique_ptr<ui::LayerAnimationElement> a,
-    std::unique_ptr<ui::LayerAnimationElement> b = nullptr);
+// Parameters for a LayerAnimationSequence.
+struct LayerAnimationSequenceParams {
+  // True if the animation sequence should loop endlessly, false otherwise.
+  bool is_cyclic = false;
+};
 
 // Creates a LayerAnimationSequence containing the specified
-// |layer_animation_element| to be started after the given |delay|. The method
-// caller assumes ownership of the returned pointer.
-ui::LayerAnimationSequence* CreateLayerAnimationSequenceWithDelay(
-    std::unique_ptr<ui::LayerAnimationElement> layer_animation_element,
-    const base::TimeDelta& delay);
+// LayerAnimationElements with the given |params|. The method caller assumes
+// ownership of the returned pointer.
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    const LayerAnimationSequenceParams& params = {});
+
+// Creates a LayerAnimationSequence containing the specified
+// LayerAnimationElements with the given |params|. The method caller assumes
+// ownership of the returned pointer.
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    const LayerAnimationSequenceParams& params = {});
+
+// Creates a LayerAnimationSequence containing the specified
+// LayerAnimationElements with the given |params|. The method caller assumes
+// ownership of the returned pointer.
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    std::unique_ptr<ui::LayerAnimationElement> c,
+    const LayerAnimationSequenceParams& params = {});
+
+// Creates a LayerAnimationSequence containing the specified
+// LayerAnimationElements with the given |params|. The method caller assumes
+// ownership of the returned pointer.
+ui::LayerAnimationSequence* CreateLayerAnimationSequence(
+    std::unique_ptr<ui::LayerAnimationElement> a,
+    std::unique_ptr<ui::LayerAnimationElement> b,
+    std::unique_ptr<ui::LayerAnimationElement> c,
+    std::unique_ptr<ui::LayerAnimationElement> d,
+    const LayerAnimationSequenceParams& params = {});
 
 // Creates a LayerAnimationElement to animate opacity with the given parameters.
 std::unique_ptr<ui::LayerAnimationElement> CreateOpacityElement(