blob: 68ba71aaeb4ddd07f510b98a9b6cae7373f2da9e [file] [log] [blame]
Xiaohui Chen294e7bd2017-06-13 16:57:381// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Xiaohui Chena258a1b2017-11-20 22:22:485#include "ash/shelf/assistant_overlay.h"
Xiaohui Chen294e7bd2017-06-13 16:57:386
7#include <algorithm>
8#include <memory>
xiaohuic4d5e7b4b2017-07-17 17:02:239#include <string>
Xiaohui Chen294e7bd2017-06-13 16:57:3810#include <utility>
11
12#include "ash/public/cpp/shelf_types.h"
13#include "ash/resources/vector_icons/vector_icons.h"
14#include "ash/shelf/app_list_button.h"
15#include "ash/shelf/ink_drop_button_listener.h"
16#include "ash/shelf/shelf.h"
17#include "ash/shelf/shelf_constants.h"
18#include "ash/shelf/shelf_view.h"
19#include "ash/shell.h"
20#include "ash/strings/grit/ash_strings.h"
21#include "ash/system/tray/tray_popup_utils.h"
Xiaohui Chenc219ab02017-08-09 23:59:5722#include "ash/wm/tablet_mode/tablet_mode_controller.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3823#include "base/command_line.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3824#include "base/metrics/user_metrics.h"
25#include "base/metrics/user_metrics_action.h"
Xiaohui Chen207b12d2017-08-03 18:02:4326#include "base/time/time.h"
27#include "base/timer/timer.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3828#include "chromeos/chromeos_switches.h"
29#include "ui/accessibility/ax_node_data.h"
30#include "ui/app_list/presenter/app_list.h"
31#include "ui/base/l10n/l10n_util.h"
xiaohuic4d5e7b4b2017-07-17 17:02:2332#include "ui/compositor/callback_layer_animation_observer.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3833#include "ui/compositor/layer_animation_element.h"
34#include "ui/compositor/layer_animation_observer.h"
35#include "ui/compositor/layer_animation_sequence.h"
36#include "ui/compositor/layer_animator.h"
37#include "ui/compositor/paint_context.h"
38#include "ui/compositor/paint_recorder.h"
39#include "ui/compositor/scoped_layer_animation_settings.h"
Xiaohui Chen207b12d2017-08-03 18:02:4340#include "ui/gfx/animation/linear_animation.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3841#include "ui/gfx/canvas.h"
42#include "ui/gfx/image/canvas_image_source.h"
43#include "ui/gfx/image/image_skia.h"
44#include "ui/gfx/image/image_skia_operations.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3845#include "ui/gfx/scoped_canvas.h"
xiaohuic08a57482017-06-21 22:20:0546#include "ui/gfx/skia_paint_util.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3847#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
48#include "ui/views/animation/ink_drop_impl.h"
49#include "ui/views/animation/ink_drop_mask.h"
xiaohuic4d5e7b4b2017-07-17 17:02:2350#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3851#include "ui/views/painter.h"
52#include "ui/views/widget/widget.h"
53
54namespace ash {
55namespace {
xiaohuicfb5c54fc2017-06-28 17:32:0256constexpr int kFullExpandDurationMs = 450;
Xiaohui Chen294e7bd2017-06-13 16:57:3857constexpr int kFullRetractDurationMs = 300;
58constexpr int kFullBurstDurationMs = 200;
59
60constexpr float kRippleCircleInitRadiusDip = 40.f;
61constexpr float kRippleCircleStartRadiusDip = 1.f;
62constexpr float kRippleCircleRadiusDip = 77.f;
63constexpr float kRippleCircleBurstRadiusDip = 96.f;
64constexpr SkColor kRippleColor = SK_ColorWHITE;
65constexpr int kRippleExpandDurationMs = 400;
66constexpr int kRippleOpacityDurationMs = 100;
67constexpr int kRippleOpacityRetractDurationMs = 200;
68constexpr float kRippleOpacity = 0.2f;
69
xiaohuic4d5e7b4b2017-07-17 17:02:2370constexpr float kIconInitSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3871constexpr float kIconStartSizeDip = 4.f;
72constexpr float kIconSizeDip = 24.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2373constexpr float kIconEndSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3874constexpr float kIconOffsetDip = 56.f;
75constexpr float kIconOpacity = 1.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3876
77constexpr float kBackgroundInitSizeDip = 48.f;
78constexpr float kBackgroundStartSizeDip = 10.f;
79constexpr float kBackgroundSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3880constexpr int kBackgroundOpacityDurationMs = 200;
xiaohuic08a57482017-06-21 22:20:0581constexpr float kBackgroundShadowElevationDip = 24.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2382// TODO(xiaohuic): this is 2x device size, 1x actually have a different size.
83// Need to figure out a way to dynamically change sizes.
84constexpr float kBackgroundLargeWidthDip = 352.5f;
85constexpr float kBackgroundLargeHeightDip = 540.0f;
Xiaohui Chen67fc36b2017-08-10 18:27:2786constexpr float kBackgroundCornerRadiusDip = 12.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2387constexpr float kBackgroundPaddingDip = 6.f;
Xiaohui Chen442b2ea2017-08-10 18:16:5888constexpr int kBackgroundMorphDurationMs = 150;
xiaohuic4d5e7b4b2017-07-17 17:02:2389
90constexpr int kHideDurationMs = 200;
91
92// The minimum scale factor to use when scaling rectangle layers. Smaller values
93// were causing visual anomalies.
94constexpr float kMinimumRectScale = 0.0001f;
95
96// The minimum scale factor to use when scaling circle layers. Smaller values
97// were causing visual anomalies.
98constexpr float kMinimumCircleScale = 0.001f;
99
Xiaohui Chen207b12d2017-08-03 18:02:43100// These are voice interaction logo specs.
101constexpr float kMoleculeOffsetXDip[] = {-10.f, 10.f, 10.f, 19.f};
102constexpr float kMoleculeOffsetYDip[] = {-8.f, -2.f, 13.f, -9.f};
103constexpr float kMoleculeRadiusDip[] = {12.f, 6.f, 7.f, 3.f};
104constexpr float kMoleculeAmplitude = 2.f;
105constexpr SkColor kMoleculeColors[] = {
106 static_cast<SkColor>(0xFF4184F3), // Blue
107 static_cast<SkColor>(0xFFEA4335), // Red
108 static_cast<SkColor>(0xFFFBBC05), // Yellow
109 static_cast<SkColor>(0xFF34A853) // Green
110};
111constexpr int kMoleculeAnimationDurationMs = 1200;
112constexpr int kMoleculeAnimationOffset = 50;
113constexpr int kMoleculeOrder[] = {0, 2, 3, 1};
114
115} // namespace
116
Xiaohui Chena258a1b2017-11-20 22:22:48117class AssistantIcon : public ui::Layer, public ui::CompositorAnimationObserver {
xiaohuic4d5e7b4b2017-07-17 17:02:23118 public:
Xiaohui Chena258a1b2017-11-20 22:22:48119 AssistantIcon() : Layer(ui::LAYER_NOT_DRAWN) {
120 set_name("AssistantOverlay:ICON_LAYER");
xiaohuic4d5e7b4b2017-07-17 17:02:23121 SetBounds(gfx::Rect(0, 0, kIconInitSizeDip, kIconInitSizeDip));
122 SetFillsBoundsOpaquely(false);
123 SetMasksToBounds(false);
Xiaohui Chen207b12d2017-08-03 18:02:43124
125 InitMoleculeShape();
xiaohuic4d5e7b4b2017-07-17 17:02:23126 }
127
Xiaohui Chena258a1b2017-11-20 22:22:48128 ~AssistantIcon() override { StopAnimation(); }
Xiaohui Chenf2ff69522017-11-02 17:28:45129
Xiaohui Chen207b12d2017-08-03 18:02:43130 void StartAnimation() {
Xiaohui Chenf2ff69522017-11-02 17:28:45131 ui::Compositor* compositor = GetCompositor();
132 if (compositor && !compositor->HasAnimationObserver(this)) {
133 animating_compositor_ = compositor;
134 animating_compositor_->AddAnimationObserver(this);
135 }
Xiaohui Chen207b12d2017-08-03 18:02:43136 }
137
Xiaohui Chenf2ff69522017-11-02 17:28:45138 void StopAnimation() {
139 if (animating_compositor_ &&
140 animating_compositor_->HasAnimationObserver(this)) {
141 animating_compositor_->RemoveAnimationObserver(this);
142 }
143 animating_compositor_ = nullptr;
144 }
145
146 // ui::CompositorAnimationObserver
147 void OnCompositingShuttingDown(ui::Compositor* compositor) override {
148 DCHECK(compositor);
149 if (animating_compositor_ == compositor) {
150 StopAnimation();
151 }
152 }
153
154 void OnAnimationStep(base::TimeTicks timestamp) override {
155 uint64_t elapsed = (timestamp - base::TimeTicks()).InMilliseconds();
156 for (int i = 0; i < DOT_COUNT; ++i) {
157 float normalizedTime =
158 ((elapsed - kMoleculeAnimationOffset * kMoleculeOrder[i]) %
159 kMoleculeAnimationDurationMs) /
160 static_cast<float>(kMoleculeAnimationDurationMs);
161
162 gfx::Transform transform;
163 transform.Translate(0,
164 kMoleculeAmplitude * sin(normalizedTime * 2 * M_PI));
165
166 dot_layers_[i]->SetTransform(transform);
167 }
168 }
Xiaohui Chen207b12d2017-08-03 18:02:43169
xiaohuic4d5e7b4b2017-07-17 17:02:23170 private:
Xiaohui Chen207b12d2017-08-03 18:02:43171 enum Dot {
172 BLUE_DOT = 0,
173 RED_DOT,
174 YELLOW_DOT,
175 GREEN_DOT,
176 // The total number of shapes, not an actual shape.
177 DOT_COUNT
178 };
179
180 std::string ToLayerName(Dot dot) {
181 switch (dot) {
182 case BLUE_DOT:
183 return "BLUE_DOT";
184 case RED_DOT:
185 return "RED_DOT";
186 case YELLOW_DOT:
187 return "YELLOW_DOT";
188 case GREEN_DOT:
189 return "GREEN_DOT";
190 case DOT_COUNT:
191 NOTREACHED() << "The DOT_COUNT value should never be used.";
192 return "DOT_COUNT";
193 }
194 return "UNKNOWN";
xiaohuic4d5e7b4b2017-07-17 17:02:23195 }
196
Xiaohui Chen207b12d2017-08-03 18:02:43197 /**
198 * Convenience method to place dots to Molecule shape used by Molecule
199 * animations.
200 */
201 void InitMoleculeShape() {
202 for (int i = 0; i < DOT_COUNT; ++i) {
Xiaohui Chen45ba54022017-10-03 17:34:41203 dot_layer_delegates_[i] = std::make_unique<views::CircleLayerDelegate>(
Xiaohui Chen207b12d2017-08-03 18:02:43204 kMoleculeColors[i], kMoleculeRadiusDip[i]);
Xiaohui Chen45ba54022017-10-03 17:34:41205 dot_layers_[i] = std::make_unique<ui::Layer>();
Xiaohui Chen207b12d2017-08-03 18:02:43206
207 dot_layers_[i]->SetBounds(gfx::Rect(
208 kIconInitSizeDip / 2 + kMoleculeOffsetXDip[i] - kMoleculeRadiusDip[i],
209 kIconInitSizeDip / 2 + kMoleculeOffsetYDip[i] - kMoleculeRadiusDip[i],
210 kMoleculeRadiusDip[i] * 2, kMoleculeRadiusDip[i] * 2));
211 dot_layers_[i]->SetFillsBoundsOpaquely(false);
212 dot_layers_[i]->set_delegate(dot_layer_delegates_[i].get());
213 dot_layers_[i]->SetVisible(true);
214 dot_layers_[i]->SetOpacity(1.0);
215 dot_layers_[i]->SetMasksToBounds(false);
216 dot_layers_[i]->set_name("DOT:" + ToLayerName(static_cast<Dot>(i)));
217
218 Add(dot_layers_[i].get());
219 }
220 }
221
222 std::unique_ptr<ui::Layer> dot_layers_[DOT_COUNT];
223 std::unique_ptr<views::CircleLayerDelegate> dot_layer_delegates_[DOT_COUNT];
224
Xiaohui Chenf2ff69522017-11-02 17:28:45225 ui::Compositor* animating_compositor_ = nullptr;
Justin Donnelly4698aea32017-11-01 21:15:55226
Xiaohui Chena258a1b2017-11-20 22:22:48227 DISALLOW_COPY_AND_ASSIGN(AssistantIcon);
xiaohuic4d5e7b4b2017-07-17 17:02:23228};
Xiaohui Chen294e7bd2017-06-13 16:57:38229
Xiaohui Chena258a1b2017-11-20 22:22:48230class AssistantIconBackground : public ui::Layer, public ui::LayerDelegate {
Xiaohui Chen294e7bd2017-06-13 16:57:38231 public:
Xiaohui Chena258a1b2017-11-20 22:22:48232 AssistantIconBackground()
xiaohuic4d5e7b4b2017-07-17 17:02:23233 : Layer(ui::LAYER_NOT_DRAWN),
234 large_size_(
235 gfx::Size(kBackgroundLargeWidthDip, kBackgroundLargeHeightDip)),
236 small_size_(gfx::Size(kBackgroundSizeDip, kBackgroundSizeDip)),
237 center_point_(
238 gfx::PointF(kBackgroundSizeDip / 2, kBackgroundSizeDip / 2)),
Xiaohui Chen45ba54022017-10-03 17:34:41239 circle_layer_delegate_(std::make_unique<views::CircleLayerDelegate>(
Xiaohui Chenb77a9422017-08-15 19:27:44240 SK_ColorWHITE,
xiaohuic4d5e7b4b2017-07-17 17:02:23241 kBackgroundSizeDip / 2)),
Xiaohui Chen45ba54022017-10-03 17:34:41242 rect_layer_delegate_(std::make_unique<views::RectangleLayerDelegate>(
Xiaohui Chenb77a9422017-08-15 19:27:44243 SK_ColorWHITE,
xiaohuic4d5e7b4b2017-07-17 17:02:23244 gfx::SizeF(small_size_))) {
Xiaohui Chena258a1b2017-11-20 22:22:48245 set_name("AssistantOverlay:BACKGROUND_LAYER");
Xiaohui Chen294e7bd2017-06-13 16:57:38246 SetBounds(gfx::Rect(0, 0, kBackgroundInitSizeDip, kBackgroundInitSizeDip));
247 SetFillsBoundsOpaquely(false);
248 SetMasksToBounds(false);
249
xiaohuic08a57482017-06-21 22:20:05250 shadow_values_ =
251 gfx::ShadowValue::MakeMdShadowValues(kBackgroundShadowElevationDip);
252 const gfx::Insets shadow_margin =
253 gfx::ShadowValue::GetMargin(shadow_values_);
254
Xiaohui Chen45ba54022017-10-03 17:34:41255 border_shadow_delegate_ =
256 std::make_unique<views::BorderShadowLayerDelegate>(
257 shadow_values_, gfx::Rect(large_size_), SK_ColorWHITE,
258 kBackgroundCornerRadiusDip);
259
260 large_shadow_layer_ = std::make_unique<ui::Layer>();
261 large_shadow_layer_->set_delegate(border_shadow_delegate_.get());
262 large_shadow_layer_->SetFillsBoundsOpaquely(false);
263 large_shadow_layer_->SetBounds(
264 gfx::Rect(shadow_margin.left(), shadow_margin.top(),
265 kBackgroundLargeWidthDip - shadow_margin.width(),
266 kBackgroundLargeHeightDip - shadow_margin.height()));
267 Add(large_shadow_layer_.get());
268
269 shadow_layer_ = std::make_unique<ui::Layer>();
Xiaohui Chen294e7bd2017-06-13 16:57:38270 shadow_layer_->set_delegate(this);
271 shadow_layer_->SetFillsBoundsOpaquely(false);
272 shadow_layer_->SetBounds(
xiaohuic08a57482017-06-21 22:20:05273 gfx::Rect(shadow_margin.left(), shadow_margin.top(),
274 kBackgroundInitSizeDip - shadow_margin.width(),
275 kBackgroundInitSizeDip - shadow_margin.height()));
Xiaohui Chen294e7bd2017-06-13 16:57:38276 Add(shadow_layer_.get());
Xiaohui Chen45ba54022017-10-03 17:34:41277
278 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
279 AddPaintLayer(static_cast<PaintedShape>(i));
Xiaohui Chen294e7bd2017-06-13 16:57:38280 }
Chris Watkinsc24daf62017-11-28 03:43:09281 ~AssistantIconBackground() override = default;
282 ;
Xiaohui Chen294e7bd2017-06-13 16:57:38283
Xiaohui Chen45ba54022017-10-03 17:34:41284 void MoveLargeShadow(const gfx::PointF& new_center) {
285 gfx::Transform transform;
286 transform.Translate(new_center.x() - kBackgroundLargeWidthDip / 2,
287 new_center.y() - kBackgroundLargeHeightDip / 2);
288 large_shadow_layer_->SetTransform(transform);
289 }
290
xiaohuic4d5e7b4b2017-07-17 17:02:23291 void AnimateToLarge(const gfx::PointF& new_center,
292 ui::LayerAnimationObserver* animation_observer) {
293 PaintedShapeTransforms transforms;
294 // Setup the painted layers to be the small round size and show it
295 CalculateCircleTransforms(small_size_, &transforms);
296 SetTransforms(transforms);
297 SetPaintedLayersVisible(true);
298
299 // Hide the shadow layer
300 shadow_layer_->SetVisible(false);
Xiaohui Chen45ba54022017-10-03 17:34:41301 // Also hide the large shadow layer, it will be shown when animation ends.
302 large_shadow_layer_->SetVisible(false);
303 // Move the shadow to the right place.
304 MoveLargeShadow(new_center);
xiaohuic4d5e7b4b2017-07-17 17:02:23305
306 center_point_ = new_center;
307 // Animate the painted layers to the large rectangle size
308 CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
309 &transforms);
310
311 AnimateToTransforms(
312 transforms,
313 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs),
314 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
315 gfx::Tween::LINEAR_OUT_SLOW_IN, animation_observer);
316 }
317
Xiaohui Chenba65544e2017-08-12 02:30:14318 void SetToLarge(const gfx::PointF& new_center) {
319 PaintedShapeTransforms transforms;
320 SetPaintedLayersVisible(true);
Xiaohui Chenba65544e2017-08-12 02:30:14321 // Hide the shadow layer
322 shadow_layer_->SetVisible(false);
Xiaohui Chen45ba54022017-10-03 17:34:41323 // Show the large shadow behind
324 large_shadow_layer_->SetVisible(true);
325 // Move the shadow to the right place.
326 MoveLargeShadow(new_center);
Xiaohui Chenba65544e2017-08-12 02:30:14327
328 center_point_ = new_center;
329 // Set the painted layers to the large rectangle size
330 CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
331 &transforms);
332 SetTransforms(transforms);
333 }
334
xiaohuic4d5e7b4b2017-07-17 17:02:23335 void ResetShape() {
336 // This reverts to the original small round shape.
337 shadow_layer_->SetVisible(true);
Xiaohui Chen45ba54022017-10-03 17:34:41338 large_shadow_layer_->SetVisible(false);
xiaohuic4d5e7b4b2017-07-17 17:02:23339 SetPaintedLayersVisible(false);
340 center_point_.SetPoint(small_size_.width() / 2.f,
341 small_size_.height() / 2.f);
342 }
343
Xiaohui Chen294e7bd2017-06-13 16:57:38344 private:
xiaohuic4d5e7b4b2017-07-17 17:02:23345 // Enumeration of the different shapes that compose the background.
346 enum PaintedShape {
347 TOP_LEFT_CIRCLE = 0,
348 TOP_RIGHT_CIRCLE,
349 BOTTOM_RIGHT_CIRCLE,
350 BOTTOM_LEFT_CIRCLE,
351 HORIZONTAL_RECT,
352 VERTICAL_RECT,
353 // The total number of shapes, not an actual shape.
354 PAINTED_SHAPE_COUNT
355 };
356
357 typedef gfx::Transform PaintedShapeTransforms[PAINTED_SHAPE_COUNT];
358
Xiaohui Chenb77a9422017-08-15 19:27:44359 void AddPaintLayer(PaintedShape painted_shape) {
xiaohuic4d5e7b4b2017-07-17 17:02:23360 ui::LayerDelegate* delegate = nullptr;
361 switch (painted_shape) {
362 case TOP_LEFT_CIRCLE:
363 case TOP_RIGHT_CIRCLE:
364 case BOTTOM_RIGHT_CIRCLE:
365 case BOTTOM_LEFT_CIRCLE:
Xiaohui Chenb77a9422017-08-15 19:27:44366 delegate = circle_layer_delegate_.get();
xiaohuic4d5e7b4b2017-07-17 17:02:23367 break;
368 case HORIZONTAL_RECT:
369 case VERTICAL_RECT:
Xiaohui Chenb77a9422017-08-15 19:27:44370 delegate = rect_layer_delegate_.get();
xiaohuic4d5e7b4b2017-07-17 17:02:23371 break;
372 case PAINTED_SHAPE_COUNT:
373 NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type.";
374 break;
375 }
376
377 ui::Layer* layer = new ui::Layer();
378 Add(layer);
379
380 layer->SetBounds(gfx::Rect(small_size_));
381 layer->SetFillsBoundsOpaquely(false);
382 layer->set_delegate(delegate);
383 layer->SetVisible(true);
384 layer->SetOpacity(1.0);
385 layer->SetMasksToBounds(false);
386 layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape));
387
Xiaohui Chenb77a9422017-08-15 19:27:44388 painted_layers_[static_cast<int>(painted_shape)].reset(layer);
xiaohuic4d5e7b4b2017-07-17 17:02:23389 }
390
391 void SetTransforms(const PaintedShapeTransforms transforms) {
Xiaohui Chenb77a9422017-08-15 19:27:44392 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
xiaohuic4d5e7b4b2017-07-17 17:02:23393 painted_layers_[i]->SetTransform(transforms[i]);
394 }
395
396 void SetPaintedLayersVisible(bool visible) {
Xiaohui Chenb77a9422017-08-15 19:27:44397 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
xiaohuic4d5e7b4b2017-07-17 17:02:23398 painted_layers_[i]->SetVisible(visible);
399 }
400
401 void CalculateCircleTransforms(const gfx::Size& size,
402 PaintedShapeTransforms* transforms_out) const {
403 CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f,
404 transforms_out);
405 }
406
407 void CalculateRectTransforms(const gfx::Size& desired_size,
408 float corner_radius,
409 PaintedShapeTransforms* transforms_out) const {
410 DCHECK_GE(desired_size.width() / 2.0f, corner_radius)
411 << "The circle's diameter should not be greater than the total width.";
412 DCHECK_GE(desired_size.height() / 2.0f, corner_radius)
413 << "The circle's diameter should not be greater than the total height.";
414
415 gfx::SizeF size(desired_size);
416 // This function can be called before the layer's been added to a view,
417 // either at construction time or in tests.
418 if (GetCompositor()) {
419 // Modify |desired_size| so that the ripple aligns to pixel bounds.
420 const float dsf = GetCompositor()->device_scale_factor();
421 gfx::RectF ripple_bounds((gfx::PointF(center_point_)), gfx::SizeF());
422 ripple_bounds.Inset(-gfx::InsetsF(desired_size.height() / 2.0f,
423 desired_size.width() / 2.0f));
424 ripple_bounds.Scale(dsf);
425 ripple_bounds = gfx::RectF(gfx::ToEnclosingRect(ripple_bounds));
426 ripple_bounds.Scale(1.0f / dsf);
427 size = ripple_bounds.size();
428 }
429
430 // The shapes are drawn such that their center points are not at the origin.
431 // Thus we use the CalculateCircleTransform() and CalculateRectTransform()
432 // methods to calculate the complex Transforms.
433
434 const float circle_scale = std::max(
435 kMinimumCircleScale,
436 corner_radius / static_cast<float>(circle_layer_delegate_->radius()));
437
438 const float circle_target_x_offset = size.width() / 2.0f - corner_radius;
439 const float circle_target_y_offset = size.height() / 2.0f - corner_radius;
440
441 (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform(
442 circle_scale, -circle_target_x_offset, -circle_target_y_offset);
443 (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform(
444 circle_scale, circle_target_x_offset, -circle_target_y_offset);
445 (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform(
446 circle_scale, circle_target_x_offset, circle_target_y_offset);
447 (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform(
448 circle_scale, -circle_target_x_offset, circle_target_y_offset);
449
450 const float rect_delegate_width = rect_layer_delegate_->size().width();
451 const float rect_delegate_height = rect_layer_delegate_->size().height();
452
453 (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform(
454 std::max(kMinimumRectScale, size.width() / rect_delegate_width),
455 std::max(kMinimumRectScale, (size.height() - 2.0f * corner_radius) /
456 rect_delegate_height));
457
458 (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform(
459 std::max(kMinimumRectScale,
460 (size.width() - 2.0f * corner_radius) / rect_delegate_width),
461 std::max(kMinimumRectScale, size.height() / rect_delegate_height));
462 }
463
464 gfx::Transform CalculateCircleTransform(float scale,
465 float target_center_x,
466 float target_center_y) const {
467 gfx::Transform transform;
468 // Offset for the center point of the ripple.
469 transform.Translate(center_point_.x(), center_point_.y());
470 // Move circle to target.
471 transform.Translate(target_center_x, target_center_y);
472 transform.Scale(scale, scale);
473 // Align center point of the painted circle.
474 const gfx::Vector2dF circle_center_offset =
475 circle_layer_delegate_->GetCenteringOffset();
476 transform.Translate(-circle_center_offset.x(), -circle_center_offset.y());
477 return transform;
478 }
479
480 gfx::Transform CalculateRectTransform(float x_scale, float y_scale) const {
481 gfx::Transform transform;
482 transform.Translate(center_point_.x(), center_point_.y());
483 transform.Scale(x_scale, y_scale);
484 const gfx::Vector2dF rect_center_offset =
485 rect_layer_delegate_->GetCenteringOffset();
486 transform.Translate(-rect_center_offset.x(), -rect_center_offset.y());
487 return transform;
488 }
489
490 void AnimateToTransforms(
491 const PaintedShapeTransforms transforms,
492 base::TimeDelta duration,
493 ui::LayerAnimator::PreemptionStrategy preemption_strategy,
494 gfx::Tween::Type tween,
495 ui::LayerAnimationObserver* animation_observer) {
496 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) {
497 ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator();
498 ui::ScopedLayerAnimationSettings animation(animator);
499 animation.SetPreemptionStrategy(preemption_strategy);
500 animation.SetTweenType(tween);
501 std::unique_ptr<ui::LayerAnimationElement> element =
502 ui::LayerAnimationElement::CreateTransformElement(transforms[i],
503 duration);
504 ui::LayerAnimationSequence* sequence =
505 new ui::LayerAnimationSequence(std::move(element));
506
507 if (animation_observer)
508 sequence->AddObserver(animation_observer);
509
510 animator->StartAnimation(sequence);
511 }
Xiaohui Chen45ba54022017-10-03 17:34:41512
513 {
514 ui::ScopedLayerAnimationSettings animation(
515 large_shadow_layer_->GetAnimator());
516 animation.SetTweenType(tween);
517 animation.SetTransitionDuration(duration);
518
519 large_shadow_layer_->SetVisible(true);
520 }
xiaohuic4d5e7b4b2017-07-17 17:02:23521 }
522
523 std::string ToLayerName(PaintedShape painted_shape) {
524 switch (painted_shape) {
525 case TOP_LEFT_CIRCLE:
526 return "TOP_LEFT_CIRCLE";
527 case TOP_RIGHT_CIRCLE:
528 return "TOP_RIGHT_CIRCLE";
529 case BOTTOM_RIGHT_CIRCLE:
530 return "BOTTOM_RIGHT_CIRCLE";
531 case BOTTOM_LEFT_CIRCLE:
532 return "BOTTOM_LEFT_CIRCLE";
533 case HORIZONTAL_RECT:
534 return "HORIZONTAL_RECT";
535 case VERTICAL_RECT:
536 return "VERTICAL_RECT";
537 case PAINTED_SHAPE_COUNT:
538 NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used.";
539 return "PAINTED_SHAPE_COUNT";
540 }
541 return "UNKNOWN";
542 }
543
Xiaohui Chen294e7bd2017-06-13 16:57:38544 void OnPaintLayer(const ui::PaintContext& context) override {
545 // Radius is based on the parent layer size, the shadow layer is expanded
546 // to make room for the shadow.
547 float radius = size().width() / 2.f;
Xiaohui Chen294e7bd2017-06-13 16:57:38548
xiaohuic08a57482017-06-21 22:20:05549 ui::PaintRecorder recorder(context, shadow_layer_->size());
Xiaohui Chen294e7bd2017-06-13 16:57:38550 gfx::Canvas* canvas = recorder.canvas();
xiaohuic08a57482017-06-21 22:20:05551
552 cc::PaintFlags flags;
Xiaohui Chenb77a9422017-08-15 19:27:44553 flags.setColor(SK_ColorWHITE);
xiaohuic08a57482017-06-21 22:20:05554 flags.setAntiAlias(true);
555 flags.setStyle(cc::PaintFlags::kFill_Style);
556 flags.setLooper(gfx::CreateShadowDrawLooper(shadow_values_));
557 gfx::Rect shadow_bounds = shadow_layer_->bounds();
xiaohuicfb5c54fc2017-06-28 17:32:02558 canvas->DrawCircle(
559 gfx::PointF(radius - shadow_bounds.x(), radius - shadow_bounds.y()),
560 radius, flags);
Xiaohui Chen294e7bd2017-06-13 16:57:38561 }
562
Scott Violet06aff2b42017-09-08 00:26:32563 void OnDeviceScaleFactorChanged(float old_device_scale_factor,
564 float new_device_scale_factor) override {}
Xiaohui Chen294e7bd2017-06-13 16:57:38565
xiaohuic4d5e7b4b2017-07-17 17:02:23566 // ui::Layers for all of the painted shape layers that compose the morphing
Xiaohui Chenb77a9422017-08-15 19:27:44567 // shape.
xiaohuic4d5e7b4b2017-07-17 17:02:23568 std::unique_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT];
569
570 const gfx::Size large_size_;
571
572 const gfx::Size small_size_;
573
574 // The center point of the painted shape.
575 gfx::PointF center_point_;
576
577 // ui::LayerDelegate to paint circles for all the circle layers.
578 std::unique_ptr<views::CircleLayerDelegate> circle_layer_delegate_;
579
580 // ui::LayerDelegate to paint rectangles for all the rectangle layers.
581 std::unique_ptr<views::RectangleLayerDelegate> rect_layer_delegate_;
582
Xiaohui Chen45ba54022017-10-03 17:34:41583 // ui::LayerDelegate to paint rounded rectangle with shadow.
584 std::unique_ptr<views::BorderShadowLayerDelegate> border_shadow_delegate_;
585
Xiaohui Chen294e7bd2017-06-13 16:57:38586 gfx::ShadowValues shadow_values_;
587
Xiaohui Chen45ba54022017-10-03 17:34:41588 // This layer shows the small circle with shadow.
Xiaohui Chen294e7bd2017-06-13 16:57:38589 std::unique_ptr<ui::Layer> shadow_layer_;
590
Xiaohui Chen45ba54022017-10-03 17:34:41591 // This layer shows the large rounded rectangle with shadow.
592 std::unique_ptr<ui::Layer> large_shadow_layer_;
593
Xiaohui Chena258a1b2017-11-20 22:22:48594 DISALLOW_COPY_AND_ASSIGN(AssistantIconBackground);
Xiaohui Chen294e7bd2017-06-13 16:57:38595};
596
Xiaohui Chena258a1b2017-11-20 22:22:48597AssistantOverlay::AssistantOverlay(AppListButton* host_view)
Xiaohui Chen45ba54022017-10-03 17:34:41598 : ripple_layer_(std::make_unique<ui::Layer>()),
Xiaohui Chena258a1b2017-11-20 22:22:48599 icon_layer_(std::make_unique<AssistantIcon>()),
600 background_layer_(std::make_unique<AssistantIconBackground>()),
Xiaohui Chen294e7bd2017-06-13 16:57:38601 host_view_(host_view),
Xiaohui Chen294e7bd2017-06-13 16:57:38602 circle_layer_delegate_(kRippleColor, kRippleCircleInitRadiusDip) {
603 SetPaintToLayer(ui::LAYER_NOT_DRAWN);
Xiaohui Chena258a1b2017-11-20 22:22:48604 layer()->set_name("AssistantOverlay:ROOT_LAYER");
Xiaohui Chen294e7bd2017-06-13 16:57:38605 layer()->SetMasksToBounds(false);
606
607 ripple_layer_->SetBounds(gfx::Rect(0, 0, kRippleCircleInitRadiusDip * 2,
608 kRippleCircleInitRadiusDip * 2));
609 ripple_layer_->set_delegate(&circle_layer_delegate_);
610 ripple_layer_->SetFillsBoundsOpaquely(false);
611 ripple_layer_->SetMasksToBounds(true);
Xiaohui Chena258a1b2017-11-20 22:22:48612 ripple_layer_->set_name("AssistantOverlay:PAINTED_LAYER");
Xiaohui Chen294e7bd2017-06-13 16:57:38613 layer()->Add(ripple_layer_.get());
614
615 layer()->Add(background_layer_.get());
616
617 layer()->Add(icon_layer_.get());
618}
619
Chris Watkinsc24daf62017-11-28 03:43:09620AssistantOverlay::~AssistantOverlay() = default;
Xiaohui Chen294e7bd2017-06-13 16:57:38621
Xiaohui Chena258a1b2017-11-20 22:22:48622void AssistantOverlay::StartAnimation(bool show_icon) {
Xiaohui Chenba65544e2017-08-12 02:30:14623 animation_state_ = AnimationState::STARTING;
xiaohuic4d5e7b4b2017-07-17 17:02:23624 show_icon_ = show_icon;
Xiaohui Chen294e7bd2017-06-13 16:57:38625 SetVisible(true);
626
627 // Setup ripple initial state.
628 ripple_layer_->SetOpacity(0);
629
630 SkMScalar scale_factor =
631 kRippleCircleStartRadiusDip / kRippleCircleInitRadiusDip;
632 gfx::Transform transform;
633
Sammie Quonfb3feae2017-07-25 20:13:42634 const gfx::Point center = host_view_->GetAppListButtonCenterPoint();
Xiaohui Chen294e7bd2017-06-13 16:57:38635 transform.Translate(center.x() - kRippleCircleStartRadiusDip,
636 center.y() - kRippleCircleStartRadiusDip);
637 transform.Scale(scale_factor, scale_factor);
638 ripple_layer_->SetTransform(transform);
639
640 // Setup ripple animations.
641 {
642 scale_factor = kRippleCircleRadiusDip / kRippleCircleInitRadiusDip;
643 transform.MakeIdentity();
644 transform.Translate(center.x() - kRippleCircleRadiusDip,
645 center.y() - kRippleCircleRadiusDip);
646 transform.Scale(scale_factor, scale_factor);
647
648 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
649 settings.SetTransitionDuration(
650 base::TimeDelta::FromMilliseconds(kRippleExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02651 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38652
653 ripple_layer_->SetTransform(transform);
654
655 settings.SetTransitionDuration(
656 base::TimeDelta::FromMilliseconds(kRippleOpacityDurationMs));
657 ripple_layer_->SetOpacity(kRippleOpacity);
658 }
659
xiaohuic4d5e7b4b2017-07-17 17:02:23660 icon_layer_->SetOpacity(0);
661 background_layer_->SetOpacity(0);
662 if (!show_icon_)
663 return;
Xiaohui Chen294e7bd2017-06-13 16:57:38664
665 // Setup icon initial state.
Xiaohui Chen294e7bd2017-06-13 16:57:38666 transform.MakeIdentity();
Xiaohui Chen294e7bd2017-06-13 16:57:38667 transform.Translate(center.x() - kIconStartSizeDip / 2.f,
668 center.y() - kIconStartSizeDip / 2.f);
669
670 scale_factor = kIconStartSizeDip / kIconInitSizeDip;
671 transform.Scale(scale_factor, scale_factor);
672 icon_layer_->SetTransform(transform);
673
Xiaohui Chenc219ab02017-08-09 23:59:57674 const bool is_tablet_mode = Shell::Get()
675 ->tablet_mode_controller()
676 ->IsTabletModeWindowManagerEnabled();
677 const int icon_x_offset = is_tablet_mode ? 0 : kIconOffsetDip;
Xiaohui Chena35b634e2017-10-06 23:22:47678 const int icon_y_offset =
679 is_tablet_mode ? -kRippleCircleRadiusDip : -kIconOffsetDip;
Xiaohui Chen294e7bd2017-06-13 16:57:38680 // Setup icon animation.
681 scale_factor = kIconSizeDip / kIconInitSizeDip;
682 transform.MakeIdentity();
Xiaohui Chenc219ab02017-08-09 23:59:57683 transform.Translate(center.x() - kIconSizeDip / 2 + icon_x_offset,
Xiaohui Chena35b634e2017-10-06 23:22:47684 center.y() - kIconSizeDip / 2 + icon_y_offset);
Xiaohui Chen294e7bd2017-06-13 16:57:38685 transform.Scale(scale_factor, scale_factor);
686
687 {
688 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
689 settings.SetTransitionDuration(
690 base::TimeDelta::FromMilliseconds(kFullExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02691 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38692
693 icon_layer_->SetTransform(transform);
694 icon_layer_->SetOpacity(kIconOpacity);
695 }
696
697 // Setup background initial state.
xiaohuic4d5e7b4b2017-07-17 17:02:23698 background_layer_->ResetShape();
Xiaohui Chen294e7bd2017-06-13 16:57:38699
700 transform.MakeIdentity();
701 transform.Translate(center.x() - kBackgroundStartSizeDip / 2.f,
702 center.y() - kBackgroundStartSizeDip / 2.f);
703
704 scale_factor = kBackgroundStartSizeDip / kBackgroundInitSizeDip;
705 transform.Scale(scale_factor, scale_factor);
706 background_layer_->SetTransform(transform);
707
708 // Setup background animation.
709 scale_factor = kBackgroundSizeDip / kBackgroundInitSizeDip;
710 transform.MakeIdentity();
Xiaohui Chenc219ab02017-08-09 23:59:57711 transform.Translate(center.x() - kBackgroundSizeDip / 2 + icon_x_offset,
Xiaohui Chena35b634e2017-10-06 23:22:47712 center.y() - kBackgroundSizeDip / 2 + icon_y_offset);
Xiaohui Chen294e7bd2017-06-13 16:57:38713 transform.Scale(scale_factor, scale_factor);
714
715 {
716 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
717 settings.SetTransitionDuration(
718 base::TimeDelta::FromMilliseconds(kFullExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02719 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38720
721 background_layer_->SetTransform(transform);
722 }
723
724 {
725 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
726 settings.SetTransitionDuration(
Xiaohui Chen294e7bd2017-06-13 16:57:38727 base::TimeDelta::FromMilliseconds(kBackgroundOpacityDurationMs));
xiaohuic219666d2017-06-28 20:23:02728 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
xiaohuicfb5c54fc2017-06-28 17:32:02729
Xiaohui Chen294e7bd2017-06-13 16:57:38730 background_layer_->SetOpacity(1);
731 }
732}
733
Xiaohui Chena258a1b2017-11-20 22:22:48734void AssistantOverlay::BurstAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14735 animation_state_ = AnimationState::BURSTING;
Xiaohui Chen294e7bd2017-06-13 16:57:38736
Sammie Quonfb3feae2017-07-25 20:13:42737 gfx::Point center = host_view_->GetAppListButtonCenterPoint();
xiaohuic4d5e7b4b2017-07-17 17:02:23738 gfx::Transform transform;
Xiaohui Chen294e7bd2017-06-13 16:57:38739
740 // Setup ripple animations.
741 {
742 SkMScalar scale_factor =
743 kRippleCircleBurstRadiusDip / kRippleCircleInitRadiusDip;
xiaohuic4d5e7b4b2017-07-17 17:02:23744 transform.Translate(center.x() - kRippleCircleBurstRadiusDip,
745 center.y() - kRippleCircleBurstRadiusDip);
746 transform.Scale(scale_factor, scale_factor);
Xiaohui Chen294e7bd2017-06-13 16:57:38747
748 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
749 settings.SetTransitionDuration(
750 base::TimeDelta::FromMilliseconds(kFullBurstDurationMs));
751 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
xiaohuic4d5e7b4b2017-07-17 17:02:23752 settings.SetPreemptionStrategy(
753 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
Xiaohui Chen294e7bd2017-06-13 16:57:38754
xiaohuic4d5e7b4b2017-07-17 17:02:23755 ripple_layer_->SetTransform(transform);
Xiaohui Chen294e7bd2017-06-13 16:57:38756 ripple_layer_->SetOpacity(0);
757 }
758
xiaohuic4d5e7b4b2017-07-17 17:02:23759 if (!show_icon_)
760 return;
761
Xiaohui Chen294e7bd2017-06-13 16:57:38762 // Setup icon animation.
xiaohuic4d5e7b4b2017-07-17 17:02:23763 // TODO(xiaohuic): Currently the animation does not support RTL.
Xiaohui Chen294e7bd2017-06-13 16:57:38764 {
765 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
766 settings.SetTransitionDuration(
xiaohuic4d5e7b4b2017-07-17 17:02:23767 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
768 settings.SetPreemptionStrategy(
769 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
Xiaohui Chen294e7bd2017-06-13 16:57:38770 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
771
xiaohuic4d5e7b4b2017-07-17 17:02:23772 transform.MakeIdentity();
773 transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
774 kIconEndSizeDip / 2,
775 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
776 kIconEndSizeDip / 2);
777 SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
778 transform.Scale(scale_factor, scale_factor);
779
780 icon_layer_->SetTransform(transform);
Xiaohui Chen207b12d2017-08-03 18:02:43781 icon_layer_->StartAnimation();
Xiaohui Chen294e7bd2017-06-13 16:57:38782 }
783
784 // Setup background animation.
xiaohuic4d5e7b4b2017-07-17 17:02:23785 // Transform to new shape.
786 // We want to animate from the background's current position into a larger
787 // size. The animation moves the background's center point while morphing from
788 // circle to a rectangle.
Xiaohui Chenc219ab02017-08-09 23:59:57789 const bool is_tablet_mode = Shell::Get()
790 ->tablet_mode_controller()
791 ->IsTabletModeWindowManagerEnabled();
792 const int icon_x_offset = is_tablet_mode ? 0 : kIconOffsetDip;
Xiaohui Chena35b634e2017-10-06 23:22:47793 const int icon_y_offset =
794 is_tablet_mode ? -kRippleCircleRadiusDip : -kIconOffsetDip;
Xiaohui Chenc219ab02017-08-09 23:59:57795 float x_offset = center.x() - kBackgroundSizeDip / 2 + icon_x_offset;
Xiaohui Chena35b634e2017-10-06 23:22:47796 float y_offset = center.y() - kBackgroundSizeDip / 2 + icon_y_offset;
Xiaohui Chen294e7bd2017-06-13 16:57:38797
xiaohuic4d5e7b4b2017-07-17 17:02:23798 background_layer_->AnimateToLarge(
799 gfx::PointF(
800 kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
801 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset),
Xiaohui Chenba65544e2017-08-12 02:30:14802 nullptr);
803}
804
Xiaohui Chena258a1b2017-11-20 22:22:48805void AssistantOverlay::WaitingAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14806 // If we are already playing burst animation, it will end up at waiting state
807 // anyway. No need to do anything.
808 if (IsBursting())
809 return;
810
811 animation_state_ = AnimationState::WAITING;
812
813 gfx::Point center = host_view_->GetAppListButtonCenterPoint();
814 gfx::Transform transform;
815
816 ripple_layer_->SetOpacity(0);
817 icon_layer_->SetOpacity(0);
818 background_layer_->SetOpacity(0);
819 SetVisible(true);
820
821 // Setup icon layer.
822 {
823 transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
824 kIconEndSizeDip / 2,
825 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
826 kIconEndSizeDip / 2);
827 SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
828 transform.Scale(scale_factor, scale_factor);
829 icon_layer_->SetTransform(transform);
830
831 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
832 settings.SetTransitionDuration(
833 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
834 settings.SetPreemptionStrategy(
835 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
836 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
837
838 icon_layer_->SetOpacity(1);
839 icon_layer_->StartAnimation();
840 }
841
842 // Setup background layer.
843 {
844 float x_offset = center.x() - kBackgroundSizeDip / 2;
845 float y_offset = center.y() - kBackgroundSizeDip / 2;
846
847 transform.MakeIdentity();
848 background_layer_->SetTransform(transform);
849 background_layer_->SetToLarge(gfx::PointF(
850 kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
851 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset));
852
853 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
854 settings.SetTransitionDuration(
855 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
856 settings.SetPreemptionStrategy(
857 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
858 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
859
860 background_layer_->SetOpacity(1);
861 }
Xiaohui Chen294e7bd2017-06-13 16:57:38862}
863
Xiaohui Chena258a1b2017-11-20 22:22:48864void AssistantOverlay::EndAnimation() {
Xiaohui Chen56b5ab62017-11-02 18:34:26865 if (IsBursting() || IsHidden()) {
Xiaohui Chen294e7bd2017-06-13 16:57:38866 // Too late, user action already fired, we have to finish what's started.
Xiaohui Chen56b5ab62017-11-02 18:34:26867 // Or the widget has already been hidden, no need to play the end animation.
Xiaohui Chen294e7bd2017-06-13 16:57:38868 return;
869 }
870
871 // Play reverse animation
872 // Setup ripple animations.
873 SkMScalar scale_factor =
874 kRippleCircleStartRadiusDip / kRippleCircleInitRadiusDip;
875 gfx::Transform transform;
876
Sammie Quonfb3feae2017-07-25 20:13:42877 const gfx::Point center = host_view_->GetAppListButtonCenterPoint();
Xiaohui Chen294e7bd2017-06-13 16:57:38878 transform.Translate(center.x() - kRippleCircleStartRadiusDip,
879 center.y() - kRippleCircleStartRadiusDip);
880 transform.Scale(scale_factor, scale_factor);
881
882 {
883 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
884 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
885 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
886 settings.SetTransitionDuration(
887 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14888 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38889
890 ripple_layer_->SetTransform(transform);
891
892 settings.SetTransitionDuration(
893 base::TimeDelta::FromMilliseconds(kRippleOpacityRetractDurationMs));
894 ripple_layer_->SetOpacity(0);
895 }
896
xiaohuic4d5e7b4b2017-07-17 17:02:23897 if (!show_icon_)
898 return;
899
Xiaohui Chen294e7bd2017-06-13 16:57:38900 // Setup icon animation.
901 transform.MakeIdentity();
902
903 transform.Translate(center.x() - kIconStartSizeDip / 2.f,
904 center.y() - kIconStartSizeDip / 2.f);
905
906 scale_factor = kIconStartSizeDip / kIconInitSizeDip;
907 transform.Scale(scale_factor, scale_factor);
908
909 {
910 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
911 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
912 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
913 settings.SetTransitionDuration(
914 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14915 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38916
917 icon_layer_->SetTransform(transform);
918 icon_layer_->SetOpacity(0);
919 }
920
921 // Setup background animation.
922 transform.MakeIdentity();
923
924 transform.Translate(center.x() - kBackgroundStartSizeDip / 2.f,
925 center.y() - kBackgroundStartSizeDip / 2.f);
926
927 scale_factor = kBackgroundStartSizeDip / kBackgroundInitSizeDip;
928 transform.Scale(scale_factor, scale_factor);
929
930 {
931 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
932 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
933 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
934 settings.SetTransitionDuration(
935 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14936 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38937
938 background_layer_->SetTransform(transform);
939 background_layer_->SetOpacity(0);
940 }
941}
942
Xiaohui Chena258a1b2017-11-20 22:22:48943void AssistantOverlay::HideAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14944 animation_state_ = AnimationState::HIDDEN;
xiaohuic4d5e7b4b2017-07-17 17:02:23945
946 // Setup ripple animations.
947 {
948 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
949 settings.SetTransitionDuration(
950 base::TimeDelta::FromMilliseconds(kHideDurationMs));
951 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
952 settings.SetPreemptionStrategy(
953 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
954
955 ripple_layer_->SetOpacity(0);
956 }
957
958 // Setup icon animation.
959 {
960 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
961 settings.SetTransitionDuration(
962 base::TimeDelta::FromMilliseconds(kHideDurationMs));
963 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
964 settings.SetPreemptionStrategy(
965 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
966
967 icon_layer_->SetOpacity(0);
Xiaohui Chen207b12d2017-08-03 18:02:43968 icon_layer_->StopAnimation();
xiaohuic4d5e7b4b2017-07-17 17:02:23969 }
970
971 // Setup background animation.
972 {
973 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
974 settings.SetTransitionDuration(
975 base::TimeDelta::FromMilliseconds(kHideDurationMs));
976 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
977 settings.SetPreemptionStrategy(
978 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
979
980 background_layer_->SetOpacity(0);
981 }
982}
983
Xiaohui Chen294e7bd2017-06-13 16:57:38984} // namespace ash