blob: ff9f47ec890b22e2e40aac839f3a451acb9b24f8 [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
5#include "ash/shelf/voice_interaction_overlay.h"
6
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"
24#include "base/memory/ptr_util.h"
25#include "base/metrics/user_metrics.h"
26#include "base/metrics/user_metrics_action.h"
Xiaohui Chen207b12d2017-08-03 18:02:4327#include "base/time/time.h"
28#include "base/timer/timer.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3829#include "chromeos/chromeos_switches.h"
30#include "ui/accessibility/ax_node_data.h"
31#include "ui/app_list/presenter/app_list.h"
32#include "ui/base/l10n/l10n_util.h"
xiaohuic4d5e7b4b2017-07-17 17:02:2333#include "ui/compositor/callback_layer_animation_observer.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3834#include "ui/compositor/layer_animation_element.h"
35#include "ui/compositor/layer_animation_observer.h"
36#include "ui/compositor/layer_animation_sequence.h"
37#include "ui/compositor/layer_animator.h"
38#include "ui/compositor/paint_context.h"
39#include "ui/compositor/paint_recorder.h"
40#include "ui/compositor/scoped_layer_animation_settings.h"
Xiaohui Chen207b12d2017-08-03 18:02:4341#include "ui/gfx/animation/linear_animation.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3842#include "ui/gfx/canvas.h"
43#include "ui/gfx/image/canvas_image_source.h"
44#include "ui/gfx/image/image_skia.h"
45#include "ui/gfx/image/image_skia_operations.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3846#include "ui/gfx/scoped_canvas.h"
xiaohuic08a57482017-06-21 22:20:0547#include "ui/gfx/skia_paint_util.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3848#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
49#include "ui/views/animation/ink_drop_impl.h"
50#include "ui/views/animation/ink_drop_mask.h"
xiaohuic4d5e7b4b2017-07-17 17:02:2351#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
Xiaohui Chen294e7bd2017-06-13 16:57:3852#include "ui/views/painter.h"
53#include "ui/views/widget/widget.h"
54
55namespace ash {
56namespace {
xiaohuicfb5c54fc2017-06-28 17:32:0257constexpr int kFullExpandDurationMs = 450;
Xiaohui Chen294e7bd2017-06-13 16:57:3858constexpr int kFullRetractDurationMs = 300;
59constexpr int kFullBurstDurationMs = 200;
60
61constexpr float kRippleCircleInitRadiusDip = 40.f;
62constexpr float kRippleCircleStartRadiusDip = 1.f;
63constexpr float kRippleCircleRadiusDip = 77.f;
64constexpr float kRippleCircleBurstRadiusDip = 96.f;
65constexpr SkColor kRippleColor = SK_ColorWHITE;
66constexpr int kRippleExpandDurationMs = 400;
67constexpr int kRippleOpacityDurationMs = 100;
68constexpr int kRippleOpacityRetractDurationMs = 200;
69constexpr float kRippleOpacity = 0.2f;
70
xiaohuic4d5e7b4b2017-07-17 17:02:2371constexpr float kIconInitSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3872constexpr float kIconStartSizeDip = 4.f;
73constexpr float kIconSizeDip = 24.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2374constexpr float kIconEndSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3875constexpr float kIconOffsetDip = 56.f;
76constexpr float kIconOpacity = 1.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3877
78constexpr float kBackgroundInitSizeDip = 48.f;
79constexpr float kBackgroundStartSizeDip = 10.f;
80constexpr float kBackgroundSizeDip = 48.f;
Xiaohui Chen294e7bd2017-06-13 16:57:3881constexpr int kBackgroundOpacityDurationMs = 200;
xiaohuic08a57482017-06-21 22:20:0582constexpr float kBackgroundShadowElevationDip = 24.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2383// TODO(xiaohuic): this is 2x device size, 1x actually have a different size.
84// Need to figure out a way to dynamically change sizes.
85constexpr float kBackgroundLargeWidthDip = 352.5f;
86constexpr float kBackgroundLargeHeightDip = 540.0f;
Xiaohui Chen67fc36b2017-08-10 18:27:2787constexpr float kBackgroundCornerRadiusDip = 12.f;
xiaohuic4d5e7b4b2017-07-17 17:02:2388constexpr float kBackgroundPaddingDip = 6.f;
Xiaohui Chen442b2ea2017-08-10 18:16:5889constexpr int kBackgroundMorphDurationMs = 150;
xiaohuic4d5e7b4b2017-07-17 17:02:2390
91constexpr int kHideDurationMs = 200;
92
93// The minimum scale factor to use when scaling rectangle layers. Smaller values
94// were causing visual anomalies.
95constexpr float kMinimumRectScale = 0.0001f;
96
97// The minimum scale factor to use when scaling circle layers. Smaller values
98// were causing visual anomalies.
99constexpr float kMinimumCircleScale = 0.001f;
100
Xiaohui Chen207b12d2017-08-03 18:02:43101// These are voice interaction logo specs.
102constexpr float kMoleculeOffsetXDip[] = {-10.f, 10.f, 10.f, 19.f};
103constexpr float kMoleculeOffsetYDip[] = {-8.f, -2.f, 13.f, -9.f};
104constexpr float kMoleculeRadiusDip[] = {12.f, 6.f, 7.f, 3.f};
105constexpr float kMoleculeAmplitude = 2.f;
106constexpr SkColor kMoleculeColors[] = {
107 static_cast<SkColor>(0xFF4184F3), // Blue
108 static_cast<SkColor>(0xFFEA4335), // Red
109 static_cast<SkColor>(0xFFFBBC05), // Yellow
110 static_cast<SkColor>(0xFF34A853) // Green
111};
112constexpr int kMoleculeAnimationDurationMs = 1200;
113constexpr int kMoleculeAnimationOffset = 50;
114constexpr int kMoleculeOrder[] = {0, 2, 3, 1};
115
116} // namespace
117
118class VoiceInteractionIcon : public ui::Layer {
xiaohuic4d5e7b4b2017-07-17 17:02:23119 public:
Xiaohui Chen207b12d2017-08-03 18:02:43120 VoiceInteractionIcon() : Layer(ui::LAYER_NOT_DRAWN) {
xiaohuic4d5e7b4b2017-07-17 17:02:23121 set_name("VoiceInteractionOverlay:ICON_LAYER");
122 SetBounds(gfx::Rect(0, 0, kIconInitSizeDip, kIconInitSizeDip));
123 SetFillsBoundsOpaquely(false);
124 SetMasksToBounds(false);
Xiaohui Chen207b12d2017-08-03 18:02:43125
126 InitMoleculeShape();
xiaohuic4d5e7b4b2017-07-17 17:02:23127 }
128
Xiaohui Chen207b12d2017-08-03 18:02:43129 void StartAnimation() {
130 animation_timer_.Start(FROM_HERE,
131 base::TimeDelta::FromMilliseconds(
132 base::TimeTicks::kMillisecondsPerSecond /
133 gfx::LinearAnimation::kDefaultFrameRate),
134 this, &VoiceInteractionIcon::AnimationProgressed);
135 }
136
137 void StopAnimation() { animation_timer_.Stop(); }
138
xiaohuic4d5e7b4b2017-07-17 17:02:23139 private:
Xiaohui Chen207b12d2017-08-03 18:02:43140 enum Dot {
141 BLUE_DOT = 0,
142 RED_DOT,
143 YELLOW_DOT,
144 GREEN_DOT,
145 // The total number of shapes, not an actual shape.
146 DOT_COUNT
147 };
148
149 std::string ToLayerName(Dot dot) {
150 switch (dot) {
151 case BLUE_DOT:
152 return "BLUE_DOT";
153 case RED_DOT:
154 return "RED_DOT";
155 case YELLOW_DOT:
156 return "YELLOW_DOT";
157 case GREEN_DOT:
158 return "GREEN_DOT";
159 case DOT_COUNT:
160 NOTREACHED() << "The DOT_COUNT value should never be used.";
161 return "DOT_COUNT";
162 }
163 return "UNKNOWN";
xiaohuic4d5e7b4b2017-07-17 17:02:23164 }
165
Xiaohui Chen207b12d2017-08-03 18:02:43166 void AnimationProgressed() {
167 gfx::Transform transform;
xiaohuic4d5e7b4b2017-07-17 17:02:23168
Xiaohui Chen207b12d2017-08-03 18:02:43169 uint64_t now =
170 (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds();
171 for (int i = 0; i < DOT_COUNT; ++i) {
172 float normalizedTime =
173 ((now - kMoleculeAnimationOffset * kMoleculeOrder[i]) %
174 kMoleculeAnimationDurationMs) /
175 static_cast<float>(kMoleculeAnimationDurationMs);
176
177 transform.MakeIdentity();
178 transform.Translate(0,
179 kMoleculeAmplitude * sin(normalizedTime * 2 * M_PI));
180
181 dot_layers_[i]->SetTransform(transform);
182 }
183 }
184
185 /**
186 * Convenience method to place dots to Molecule shape used by Molecule
187 * animations.
188 */
189 void InitMoleculeShape() {
190 for (int i = 0; i < DOT_COUNT; ++i) {
191 dot_layer_delegates_[i] = base::MakeUnique<views::CircleLayerDelegate>(
192 kMoleculeColors[i], kMoleculeRadiusDip[i]);
193 dot_layers_[i] = base::MakeUnique<ui::Layer>();
194
195 dot_layers_[i]->SetBounds(gfx::Rect(
196 kIconInitSizeDip / 2 + kMoleculeOffsetXDip[i] - kMoleculeRadiusDip[i],
197 kIconInitSizeDip / 2 + kMoleculeOffsetYDip[i] - kMoleculeRadiusDip[i],
198 kMoleculeRadiusDip[i] * 2, kMoleculeRadiusDip[i] * 2));
199 dot_layers_[i]->SetFillsBoundsOpaquely(false);
200 dot_layers_[i]->set_delegate(dot_layer_delegates_[i].get());
201 dot_layers_[i]->SetVisible(true);
202 dot_layers_[i]->SetOpacity(1.0);
203 dot_layers_[i]->SetMasksToBounds(false);
204 dot_layers_[i]->set_name("DOT:" + ToLayerName(static_cast<Dot>(i)));
205
206 Add(dot_layers_[i].get());
207 }
208 }
209
210 std::unique_ptr<ui::Layer> dot_layers_[DOT_COUNT];
211 std::unique_ptr<views::CircleLayerDelegate> dot_layer_delegates_[DOT_COUNT];
212
213 base::RepeatingTimer animation_timer_;
xiaohuic4d5e7b4b2017-07-17 17:02:23214
215 DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIcon);
216};
Xiaohui Chen294e7bd2017-06-13 16:57:38217
218class VoiceInteractionIconBackground : public ui::Layer,
219 public ui::LayerDelegate {
220 public:
xiaohuic4d5e7b4b2017-07-17 17:02:23221 VoiceInteractionIconBackground()
222 : Layer(ui::LAYER_NOT_DRAWN),
223 large_size_(
224 gfx::Size(kBackgroundLargeWidthDip, kBackgroundLargeHeightDip)),
225 small_size_(gfx::Size(kBackgroundSizeDip, kBackgroundSizeDip)),
226 center_point_(
227 gfx::PointF(kBackgroundSizeDip / 2, kBackgroundSizeDip / 2)),
228 circle_layer_delegate_(base::MakeUnique<views::CircleLayerDelegate>(
Xiaohui Chenb77a9422017-08-15 19:27:44229 SK_ColorWHITE,
xiaohuic4d5e7b4b2017-07-17 17:02:23230 kBackgroundSizeDip / 2)),
231 rect_layer_delegate_(base::MakeUnique<views::RectangleLayerDelegate>(
Xiaohui Chenb77a9422017-08-15 19:27:44232 SK_ColorWHITE,
xiaohuic4d5e7b4b2017-07-17 17:02:23233 gfx::SizeF(small_size_))) {
Xiaohui Chen294e7bd2017-06-13 16:57:38234 set_name("VoiceInteractionOverlay:BACKGROUND_LAYER");
235 SetBounds(gfx::Rect(0, 0, kBackgroundInitSizeDip, kBackgroundInitSizeDip));
236 SetFillsBoundsOpaquely(false);
237 SetMasksToBounds(false);
238
Xiaohui Chenb77a9422017-08-15 19:27:44239 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
240 AddPaintLayer(static_cast<PaintedShape>(i));
xiaohuic4d5e7b4b2017-07-17 17:02:23241
xiaohuic08a57482017-06-21 22:20:05242 shadow_values_ =
243 gfx::ShadowValue::MakeMdShadowValues(kBackgroundShadowElevationDip);
244 const gfx::Insets shadow_margin =
245 gfx::ShadowValue::GetMargin(shadow_values_);
246
Xiaohui Chen294e7bd2017-06-13 16:57:38247 shadow_layer_.reset(new ui::Layer());
248 shadow_layer_->set_delegate(this);
249 shadow_layer_->SetFillsBoundsOpaquely(false);
250 shadow_layer_->SetBounds(
xiaohuic08a57482017-06-21 22:20:05251 gfx::Rect(shadow_margin.left(), shadow_margin.top(),
252 kBackgroundInitSizeDip - shadow_margin.width(),
253 kBackgroundInitSizeDip - shadow_margin.height()));
Xiaohui Chen294e7bd2017-06-13 16:57:38254 Add(shadow_layer_.get());
255 }
256 ~VoiceInteractionIconBackground() override{};
257
xiaohuic4d5e7b4b2017-07-17 17:02:23258 void AnimateToLarge(const gfx::PointF& new_center,
259 ui::LayerAnimationObserver* animation_observer) {
260 PaintedShapeTransforms transforms;
261 // Setup the painted layers to be the small round size and show it
262 CalculateCircleTransforms(small_size_, &transforms);
263 SetTransforms(transforms);
264 SetPaintedLayersVisible(true);
265
266 // Hide the shadow layer
267 shadow_layer_->SetVisible(false);
268
269 center_point_ = new_center;
270 // Animate the painted layers to the large rectangle size
271 CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
272 &transforms);
273
274 AnimateToTransforms(
275 transforms,
276 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs),
277 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
278 gfx::Tween::LINEAR_OUT_SLOW_IN, animation_observer);
279 }
280
Xiaohui Chenba65544e2017-08-12 02:30:14281 void SetToLarge(const gfx::PointF& new_center) {
282 PaintedShapeTransforms transforms;
283 SetPaintedLayersVisible(true);
284 // Hide the foreground layers and only show the background layers.
285 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
286 painted_layers_[i]->SetVisible(false);
287 // Hide the shadow layer
288 shadow_layer_->SetVisible(false);
289
290 center_point_ = new_center;
291 // Set the painted layers to the large rectangle size
292 CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
293 &transforms);
294 SetTransforms(transforms);
295 }
296
xiaohuic4d5e7b4b2017-07-17 17:02:23297 void ResetShape() {
298 // This reverts to the original small round shape.
299 shadow_layer_->SetVisible(true);
300 SetPaintedLayersVisible(false);
301 center_point_.SetPoint(small_size_.width() / 2.f,
302 small_size_.height() / 2.f);
303 }
304
Xiaohui Chen294e7bd2017-06-13 16:57:38305 private:
xiaohuic4d5e7b4b2017-07-17 17:02:23306 // Enumeration of the different shapes that compose the background.
307 enum PaintedShape {
308 TOP_LEFT_CIRCLE = 0,
309 TOP_RIGHT_CIRCLE,
310 BOTTOM_RIGHT_CIRCLE,
311 BOTTOM_LEFT_CIRCLE,
312 HORIZONTAL_RECT,
313 VERTICAL_RECT,
314 // The total number of shapes, not an actual shape.
315 PAINTED_SHAPE_COUNT
316 };
317
318 typedef gfx::Transform PaintedShapeTransforms[PAINTED_SHAPE_COUNT];
319
Xiaohui Chenb77a9422017-08-15 19:27:44320 void AddPaintLayer(PaintedShape painted_shape) {
xiaohuic4d5e7b4b2017-07-17 17:02:23321 ui::LayerDelegate* delegate = nullptr;
322 switch (painted_shape) {
323 case TOP_LEFT_CIRCLE:
324 case TOP_RIGHT_CIRCLE:
325 case BOTTOM_RIGHT_CIRCLE:
326 case BOTTOM_LEFT_CIRCLE:
Xiaohui Chenb77a9422017-08-15 19:27:44327 delegate = circle_layer_delegate_.get();
xiaohuic4d5e7b4b2017-07-17 17:02:23328 break;
329 case HORIZONTAL_RECT:
330 case VERTICAL_RECT:
Xiaohui Chenb77a9422017-08-15 19:27:44331 delegate = rect_layer_delegate_.get();
xiaohuic4d5e7b4b2017-07-17 17:02:23332 break;
333 case PAINTED_SHAPE_COUNT:
334 NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type.";
335 break;
336 }
337
338 ui::Layer* layer = new ui::Layer();
339 Add(layer);
340
341 layer->SetBounds(gfx::Rect(small_size_));
342 layer->SetFillsBoundsOpaquely(false);
343 layer->set_delegate(delegate);
344 layer->SetVisible(true);
345 layer->SetOpacity(1.0);
346 layer->SetMasksToBounds(false);
347 layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape));
348
Xiaohui Chenb77a9422017-08-15 19:27:44349 painted_layers_[static_cast<int>(painted_shape)].reset(layer);
xiaohuic4d5e7b4b2017-07-17 17:02:23350 }
351
352 void SetTransforms(const PaintedShapeTransforms transforms) {
Xiaohui Chenb77a9422017-08-15 19:27:44353 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
xiaohuic4d5e7b4b2017-07-17 17:02:23354 painted_layers_[i]->SetTransform(transforms[i]);
xiaohuic4d5e7b4b2017-07-17 17:02:23355 }
356
357 void SetPaintedLayersVisible(bool visible) {
Xiaohui Chenb77a9422017-08-15 19:27:44358 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
xiaohuic4d5e7b4b2017-07-17 17:02:23359 painted_layers_[i]->SetVisible(visible);
xiaohuic4d5e7b4b2017-07-17 17:02:23360 }
361
362 void CalculateCircleTransforms(const gfx::Size& size,
363 PaintedShapeTransforms* transforms_out) const {
364 CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f,
365 transforms_out);
366 }
367
368 void CalculateRectTransforms(const gfx::Size& desired_size,
369 float corner_radius,
370 PaintedShapeTransforms* transforms_out) const {
371 DCHECK_GE(desired_size.width() / 2.0f, corner_radius)
372 << "The circle's diameter should not be greater than the total width.";
373 DCHECK_GE(desired_size.height() / 2.0f, corner_radius)
374 << "The circle's diameter should not be greater than the total height.";
375
376 gfx::SizeF size(desired_size);
377 // This function can be called before the layer's been added to a view,
378 // either at construction time or in tests.
379 if (GetCompositor()) {
380 // Modify |desired_size| so that the ripple aligns to pixel bounds.
381 const float dsf = GetCompositor()->device_scale_factor();
382 gfx::RectF ripple_bounds((gfx::PointF(center_point_)), gfx::SizeF());
383 ripple_bounds.Inset(-gfx::InsetsF(desired_size.height() / 2.0f,
384 desired_size.width() / 2.0f));
385 ripple_bounds.Scale(dsf);
386 ripple_bounds = gfx::RectF(gfx::ToEnclosingRect(ripple_bounds));
387 ripple_bounds.Scale(1.0f / dsf);
388 size = ripple_bounds.size();
389 }
390
391 // The shapes are drawn such that their center points are not at the origin.
392 // Thus we use the CalculateCircleTransform() and CalculateRectTransform()
393 // methods to calculate the complex Transforms.
394
395 const float circle_scale = std::max(
396 kMinimumCircleScale,
397 corner_radius / static_cast<float>(circle_layer_delegate_->radius()));
398
399 const float circle_target_x_offset = size.width() / 2.0f - corner_radius;
400 const float circle_target_y_offset = size.height() / 2.0f - corner_radius;
401
402 (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform(
403 circle_scale, -circle_target_x_offset, -circle_target_y_offset);
404 (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform(
405 circle_scale, circle_target_x_offset, -circle_target_y_offset);
406 (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform(
407 circle_scale, circle_target_x_offset, circle_target_y_offset);
408 (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform(
409 circle_scale, -circle_target_x_offset, circle_target_y_offset);
410
411 const float rect_delegate_width = rect_layer_delegate_->size().width();
412 const float rect_delegate_height = rect_layer_delegate_->size().height();
413
414 (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform(
415 std::max(kMinimumRectScale, size.width() / rect_delegate_width),
416 std::max(kMinimumRectScale, (size.height() - 2.0f * corner_radius) /
417 rect_delegate_height));
418
419 (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform(
420 std::max(kMinimumRectScale,
421 (size.width() - 2.0f * corner_radius) / rect_delegate_width),
422 std::max(kMinimumRectScale, size.height() / rect_delegate_height));
423 }
424
425 gfx::Transform CalculateCircleTransform(float scale,
426 float target_center_x,
427 float target_center_y) const {
428 gfx::Transform transform;
429 // Offset for the center point of the ripple.
430 transform.Translate(center_point_.x(), center_point_.y());
431 // Move circle to target.
432 transform.Translate(target_center_x, target_center_y);
433 transform.Scale(scale, scale);
434 // Align center point of the painted circle.
435 const gfx::Vector2dF circle_center_offset =
436 circle_layer_delegate_->GetCenteringOffset();
437 transform.Translate(-circle_center_offset.x(), -circle_center_offset.y());
438 return transform;
439 }
440
441 gfx::Transform CalculateRectTransform(float x_scale, float y_scale) const {
442 gfx::Transform transform;
443 transform.Translate(center_point_.x(), center_point_.y());
444 transform.Scale(x_scale, y_scale);
445 const gfx::Vector2dF rect_center_offset =
446 rect_layer_delegate_->GetCenteringOffset();
447 transform.Translate(-rect_center_offset.x(), -rect_center_offset.y());
448 return transform;
449 }
450
451 void AnimateToTransforms(
452 const PaintedShapeTransforms transforms,
453 base::TimeDelta duration,
454 ui::LayerAnimator::PreemptionStrategy preemption_strategy,
455 gfx::Tween::Type tween,
456 ui::LayerAnimationObserver* animation_observer) {
457 for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) {
458 ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator();
459 ui::ScopedLayerAnimationSettings animation(animator);
460 animation.SetPreemptionStrategy(preemption_strategy);
461 animation.SetTweenType(tween);
xiaohuic4d5e7b4b2017-07-17 17:02:23462 std::unique_ptr<ui::LayerAnimationElement> element =
463 ui::LayerAnimationElement::CreateTransformElement(transforms[i],
464 duration);
465 ui::LayerAnimationSequence* sequence =
466 new ui::LayerAnimationSequence(std::move(element));
467
468 if (animation_observer)
469 sequence->AddObserver(animation_observer);
470
471 animator->StartAnimation(sequence);
472 }
473 }
474
475 std::string ToLayerName(PaintedShape painted_shape) {
476 switch (painted_shape) {
477 case TOP_LEFT_CIRCLE:
478 return "TOP_LEFT_CIRCLE";
479 case TOP_RIGHT_CIRCLE:
480 return "TOP_RIGHT_CIRCLE";
481 case BOTTOM_RIGHT_CIRCLE:
482 return "BOTTOM_RIGHT_CIRCLE";
483 case BOTTOM_LEFT_CIRCLE:
484 return "BOTTOM_LEFT_CIRCLE";
485 case HORIZONTAL_RECT:
486 return "HORIZONTAL_RECT";
487 case VERTICAL_RECT:
488 return "VERTICAL_RECT";
489 case PAINTED_SHAPE_COUNT:
490 NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used.";
491 return "PAINTED_SHAPE_COUNT";
492 }
493 return "UNKNOWN";
494 }
495
Xiaohui Chen294e7bd2017-06-13 16:57:38496 void OnPaintLayer(const ui::PaintContext& context) override {
497 // Radius is based on the parent layer size, the shadow layer is expanded
498 // to make room for the shadow.
499 float radius = size().width() / 2.f;
Xiaohui Chen294e7bd2017-06-13 16:57:38500
xiaohuic08a57482017-06-21 22:20:05501 ui::PaintRecorder recorder(context, shadow_layer_->size());
Xiaohui Chen294e7bd2017-06-13 16:57:38502 gfx::Canvas* canvas = recorder.canvas();
xiaohuic08a57482017-06-21 22:20:05503
504 cc::PaintFlags flags;
Xiaohui Chenb77a9422017-08-15 19:27:44505 flags.setColor(SK_ColorWHITE);
xiaohuic08a57482017-06-21 22:20:05506 flags.setAntiAlias(true);
507 flags.setStyle(cc::PaintFlags::kFill_Style);
508 flags.setLooper(gfx::CreateShadowDrawLooper(shadow_values_));
509 gfx::Rect shadow_bounds = shadow_layer_->bounds();
xiaohuicfb5c54fc2017-06-28 17:32:02510 canvas->DrawCircle(
511 gfx::PointF(radius - shadow_bounds.x(), radius - shadow_bounds.y()),
512 radius, flags);
Xiaohui Chen294e7bd2017-06-13 16:57:38513 }
514
515 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
516
517 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
518
xiaohuic4d5e7b4b2017-07-17 17:02:23519 // ui::Layers for all of the painted shape layers that compose the morphing
Xiaohui Chenb77a9422017-08-15 19:27:44520 // shape.
xiaohuic4d5e7b4b2017-07-17 17:02:23521 std::unique_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT];
xiaohuic4d5e7b4b2017-07-17 17:02:23522
523 const gfx::Size large_size_;
524
525 const gfx::Size small_size_;
526
527 // The center point of the painted shape.
528 gfx::PointF center_point_;
529
530 // ui::LayerDelegate to paint circles for all the circle layers.
531 std::unique_ptr<views::CircleLayerDelegate> circle_layer_delegate_;
xiaohuic4d5e7b4b2017-07-17 17:02:23532
533 // ui::LayerDelegate to paint rectangles for all the rectangle layers.
534 std::unique_ptr<views::RectangleLayerDelegate> rect_layer_delegate_;
xiaohuic4d5e7b4b2017-07-17 17:02:23535
Xiaohui Chen294e7bd2017-06-13 16:57:38536 gfx::ShadowValues shadow_values_;
537
538 std::unique_ptr<ui::Layer> shadow_layer_;
539
540 DISALLOW_COPY_AND_ASSIGN(VoiceInteractionIconBackground);
541};
542
Xiaohui Chen294e7bd2017-06-13 16:57:38543VoiceInteractionOverlay::VoiceInteractionOverlay(AppListButton* host_view)
xiaohuic4d5e7b4b2017-07-17 17:02:23544 : ripple_layer_(base::MakeUnique<ui::Layer>()),
545 icon_layer_(base::MakeUnique<VoiceInteractionIcon>()),
546 background_layer_(base::MakeUnique<VoiceInteractionIconBackground>()),
Xiaohui Chen294e7bd2017-06-13 16:57:38547 host_view_(host_view),
Xiaohui Chen294e7bd2017-06-13 16:57:38548 circle_layer_delegate_(kRippleColor, kRippleCircleInitRadiusDip) {
549 SetPaintToLayer(ui::LAYER_NOT_DRAWN);
550 layer()->set_name("VoiceInteractionOverlay:ROOT_LAYER");
551 layer()->SetMasksToBounds(false);
552
553 ripple_layer_->SetBounds(gfx::Rect(0, 0, kRippleCircleInitRadiusDip * 2,
554 kRippleCircleInitRadiusDip * 2));
555 ripple_layer_->set_delegate(&circle_layer_delegate_);
556 ripple_layer_->SetFillsBoundsOpaquely(false);
557 ripple_layer_->SetMasksToBounds(true);
558 ripple_layer_->set_name("VoiceInteractionOverlay:PAINTED_LAYER");
559 layer()->Add(ripple_layer_.get());
560
561 layer()->Add(background_layer_.get());
562
563 layer()->Add(icon_layer_.get());
564}
565
566VoiceInteractionOverlay::~VoiceInteractionOverlay() {}
567
xiaohuic4d5e7b4b2017-07-17 17:02:23568void VoiceInteractionOverlay::StartAnimation(bool show_icon) {
Xiaohui Chenba65544e2017-08-12 02:30:14569 animation_state_ = AnimationState::STARTING;
xiaohuic4d5e7b4b2017-07-17 17:02:23570 show_icon_ = show_icon;
Xiaohui Chen294e7bd2017-06-13 16:57:38571 SetVisible(true);
572
573 // Setup ripple initial state.
574 ripple_layer_->SetOpacity(0);
575
576 SkMScalar scale_factor =
577 kRippleCircleStartRadiusDip / kRippleCircleInitRadiusDip;
578 gfx::Transform transform;
579
Sammie Quonfb3feae2017-07-25 20:13:42580 const gfx::Point center = host_view_->GetAppListButtonCenterPoint();
Xiaohui Chen294e7bd2017-06-13 16:57:38581 transform.Translate(center.x() - kRippleCircleStartRadiusDip,
582 center.y() - kRippleCircleStartRadiusDip);
583 transform.Scale(scale_factor, scale_factor);
584 ripple_layer_->SetTransform(transform);
585
586 // Setup ripple animations.
587 {
588 scale_factor = kRippleCircleRadiusDip / kRippleCircleInitRadiusDip;
589 transform.MakeIdentity();
590 transform.Translate(center.x() - kRippleCircleRadiusDip,
591 center.y() - kRippleCircleRadiusDip);
592 transform.Scale(scale_factor, scale_factor);
593
594 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
595 settings.SetTransitionDuration(
596 base::TimeDelta::FromMilliseconds(kRippleExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02597 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38598
599 ripple_layer_->SetTransform(transform);
600
601 settings.SetTransitionDuration(
602 base::TimeDelta::FromMilliseconds(kRippleOpacityDurationMs));
603 ripple_layer_->SetOpacity(kRippleOpacity);
604 }
605
xiaohuic4d5e7b4b2017-07-17 17:02:23606 icon_layer_->SetOpacity(0);
607 background_layer_->SetOpacity(0);
608 if (!show_icon_)
609 return;
Xiaohui Chen294e7bd2017-06-13 16:57:38610
611 // Setup icon initial state.
Xiaohui Chen294e7bd2017-06-13 16:57:38612 transform.MakeIdentity();
Xiaohui Chen294e7bd2017-06-13 16:57:38613 transform.Translate(center.x() - kIconStartSizeDip / 2.f,
614 center.y() - kIconStartSizeDip / 2.f);
615
616 scale_factor = kIconStartSizeDip / kIconInitSizeDip;
617 transform.Scale(scale_factor, scale_factor);
618 icon_layer_->SetTransform(transform);
619
Xiaohui Chenc219ab02017-08-09 23:59:57620 const bool is_tablet_mode = Shell::Get()
621 ->tablet_mode_controller()
622 ->IsTabletModeWindowManagerEnabled();
623 const int icon_x_offset = is_tablet_mode ? 0 : kIconOffsetDip;
Xiaohui Chen294e7bd2017-06-13 16:57:38624 // Setup icon animation.
625 scale_factor = kIconSizeDip / kIconInitSizeDip;
626 transform.MakeIdentity();
Xiaohui Chenc219ab02017-08-09 23:59:57627 transform.Translate(center.x() - kIconSizeDip / 2 + icon_x_offset,
xiaohuic4d5e7b4b2017-07-17 17:02:23628 center.y() - kIconSizeDip / 2 - kIconOffsetDip);
Xiaohui Chen294e7bd2017-06-13 16:57:38629 transform.Scale(scale_factor, scale_factor);
630
631 {
632 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
633 settings.SetTransitionDuration(
634 base::TimeDelta::FromMilliseconds(kFullExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02635 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38636
637 icon_layer_->SetTransform(transform);
638 icon_layer_->SetOpacity(kIconOpacity);
639 }
640
641 // Setup background initial state.
xiaohuic4d5e7b4b2017-07-17 17:02:23642 background_layer_->ResetShape();
Xiaohui Chen294e7bd2017-06-13 16:57:38643
644 transform.MakeIdentity();
645 transform.Translate(center.x() - kBackgroundStartSizeDip / 2.f,
646 center.y() - kBackgroundStartSizeDip / 2.f);
647
648 scale_factor = kBackgroundStartSizeDip / kBackgroundInitSizeDip;
649 transform.Scale(scale_factor, scale_factor);
650 background_layer_->SetTransform(transform);
651
652 // Setup background animation.
653 scale_factor = kBackgroundSizeDip / kBackgroundInitSizeDip;
654 transform.MakeIdentity();
Xiaohui Chenc219ab02017-08-09 23:59:57655 transform.Translate(center.x() - kBackgroundSizeDip / 2 + icon_x_offset,
xiaohuic4d5e7b4b2017-07-17 17:02:23656 center.y() - kBackgroundSizeDip / 2 - kIconOffsetDip);
Xiaohui Chen294e7bd2017-06-13 16:57:38657 transform.Scale(scale_factor, scale_factor);
658
659 {
660 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
661 settings.SetTransitionDuration(
662 base::TimeDelta::FromMilliseconds(kFullExpandDurationMs));
xiaohuic219666d2017-06-28 20:23:02663 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
Xiaohui Chen294e7bd2017-06-13 16:57:38664
665 background_layer_->SetTransform(transform);
666 }
667
668 {
669 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
670 settings.SetTransitionDuration(
Xiaohui Chen294e7bd2017-06-13 16:57:38671 base::TimeDelta::FromMilliseconds(kBackgroundOpacityDurationMs));
xiaohuic219666d2017-06-28 20:23:02672 settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
xiaohuicfb5c54fc2017-06-28 17:32:02673
Xiaohui Chen294e7bd2017-06-13 16:57:38674 background_layer_->SetOpacity(1);
675 }
676}
677
678void VoiceInteractionOverlay::BurstAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14679 animation_state_ = AnimationState::BURSTING;
Xiaohui Chen294e7bd2017-06-13 16:57:38680
Sammie Quonfb3feae2017-07-25 20:13:42681 gfx::Point center = host_view_->GetAppListButtonCenterPoint();
xiaohuic4d5e7b4b2017-07-17 17:02:23682 gfx::Transform transform;
Xiaohui Chen294e7bd2017-06-13 16:57:38683
684 // Setup ripple animations.
685 {
686 SkMScalar scale_factor =
687 kRippleCircleBurstRadiusDip / kRippleCircleInitRadiusDip;
xiaohuic4d5e7b4b2017-07-17 17:02:23688 transform.Translate(center.x() - kRippleCircleBurstRadiusDip,
689 center.y() - kRippleCircleBurstRadiusDip);
690 transform.Scale(scale_factor, scale_factor);
Xiaohui Chen294e7bd2017-06-13 16:57:38691
692 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
693 settings.SetTransitionDuration(
694 base::TimeDelta::FromMilliseconds(kFullBurstDurationMs));
695 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
xiaohuic4d5e7b4b2017-07-17 17:02:23696 settings.SetPreemptionStrategy(
697 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
Xiaohui Chen294e7bd2017-06-13 16:57:38698
xiaohuic4d5e7b4b2017-07-17 17:02:23699 ripple_layer_->SetTransform(transform);
Xiaohui Chen294e7bd2017-06-13 16:57:38700 ripple_layer_->SetOpacity(0);
701 }
702
xiaohuic4d5e7b4b2017-07-17 17:02:23703 if (!show_icon_)
704 return;
705
Xiaohui Chen294e7bd2017-06-13 16:57:38706 // Setup icon animation.
xiaohuic4d5e7b4b2017-07-17 17:02:23707 // TODO(xiaohuic): Currently the animation does not support RTL.
Xiaohui Chen294e7bd2017-06-13 16:57:38708 {
709 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
710 settings.SetTransitionDuration(
xiaohuic4d5e7b4b2017-07-17 17:02:23711 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
712 settings.SetPreemptionStrategy(
713 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
Xiaohui Chen294e7bd2017-06-13 16:57:38714 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
715
xiaohuic4d5e7b4b2017-07-17 17:02:23716 transform.MakeIdentity();
717 transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
718 kIconEndSizeDip / 2,
719 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
720 kIconEndSizeDip / 2);
721 SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
722 transform.Scale(scale_factor, scale_factor);
723
724 icon_layer_->SetTransform(transform);
Xiaohui Chen207b12d2017-08-03 18:02:43725 icon_layer_->StartAnimation();
Xiaohui Chen294e7bd2017-06-13 16:57:38726 }
727
728 // Setup background animation.
xiaohuic4d5e7b4b2017-07-17 17:02:23729 // Transform to new shape.
730 // We want to animate from the background's current position into a larger
731 // size. The animation moves the background's center point while morphing from
732 // circle to a rectangle.
Xiaohui Chenc219ab02017-08-09 23:59:57733 const bool is_tablet_mode = Shell::Get()
734 ->tablet_mode_controller()
735 ->IsTabletModeWindowManagerEnabled();
736 const int icon_x_offset = is_tablet_mode ? 0 : kIconOffsetDip;
737 float x_offset = center.x() - kBackgroundSizeDip / 2 + icon_x_offset;
xiaohuic4d5e7b4b2017-07-17 17:02:23738 float y_offset = center.y() - kBackgroundSizeDip / 2 - kIconOffsetDip;
Xiaohui Chen294e7bd2017-06-13 16:57:38739
xiaohuic4d5e7b4b2017-07-17 17:02:23740 background_layer_->AnimateToLarge(
741 gfx::PointF(
742 kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
743 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset),
Xiaohui Chenba65544e2017-08-12 02:30:14744 nullptr);
745}
746
747void VoiceInteractionOverlay::WaitingAnimation() {
748 // If we are already playing burst animation, it will end up at waiting state
749 // anyway. No need to do anything.
750 if (IsBursting())
751 return;
752
753 animation_state_ = AnimationState::WAITING;
754
755 gfx::Point center = host_view_->GetAppListButtonCenterPoint();
756 gfx::Transform transform;
757
758 ripple_layer_->SetOpacity(0);
759 icon_layer_->SetOpacity(0);
760 background_layer_->SetOpacity(0);
761 SetVisible(true);
762
763 // Setup icon layer.
764 {
765 transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
766 kIconEndSizeDip / 2,
767 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
768 kIconEndSizeDip / 2);
769 SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
770 transform.Scale(scale_factor, scale_factor);
771 icon_layer_->SetTransform(transform);
772
773 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
774 settings.SetTransitionDuration(
775 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
776 settings.SetPreemptionStrategy(
777 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
778 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
779
780 icon_layer_->SetOpacity(1);
781 icon_layer_->StartAnimation();
782 }
783
784 // Setup background layer.
785 {
786 float x_offset = center.x() - kBackgroundSizeDip / 2;
787 float y_offset = center.y() - kBackgroundSizeDip / 2;
788
789 transform.MakeIdentity();
790 background_layer_->SetTransform(transform);
791 background_layer_->SetToLarge(gfx::PointF(
792 kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
793 -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset));
794
795 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
796 settings.SetTransitionDuration(
797 base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
798 settings.SetPreemptionStrategy(
799 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
800 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
801
802 background_layer_->SetOpacity(1);
803 }
Xiaohui Chen294e7bd2017-06-13 16:57:38804}
805
806void VoiceInteractionOverlay::EndAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14807 if (IsBursting()) {
Xiaohui Chen294e7bd2017-06-13 16:57:38808 // Too late, user action already fired, we have to finish what's started.
809 return;
810 }
811
812 // Play reverse animation
813 // Setup ripple animations.
814 SkMScalar scale_factor =
815 kRippleCircleStartRadiusDip / kRippleCircleInitRadiusDip;
816 gfx::Transform transform;
817
Sammie Quonfb3feae2017-07-25 20:13:42818 const gfx::Point center = host_view_->GetAppListButtonCenterPoint();
Xiaohui Chen294e7bd2017-06-13 16:57:38819 transform.Translate(center.x() - kRippleCircleStartRadiusDip,
820 center.y() - kRippleCircleStartRadiusDip);
821 transform.Scale(scale_factor, scale_factor);
822
823 {
824 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
825 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
826 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
827 settings.SetTransitionDuration(
828 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14829 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38830
831 ripple_layer_->SetTransform(transform);
832
833 settings.SetTransitionDuration(
834 base::TimeDelta::FromMilliseconds(kRippleOpacityRetractDurationMs));
835 ripple_layer_->SetOpacity(0);
836 }
837
xiaohuic4d5e7b4b2017-07-17 17:02:23838 if (!show_icon_)
839 return;
840
Xiaohui Chen294e7bd2017-06-13 16:57:38841 // Setup icon animation.
842 transform.MakeIdentity();
843
844 transform.Translate(center.x() - kIconStartSizeDip / 2.f,
845 center.y() - kIconStartSizeDip / 2.f);
846
847 scale_factor = kIconStartSizeDip / kIconInitSizeDip;
848 transform.Scale(scale_factor, scale_factor);
849
850 {
851 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
852 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
853 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
854 settings.SetTransitionDuration(
855 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14856 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38857
858 icon_layer_->SetTransform(transform);
859 icon_layer_->SetOpacity(0);
860 }
861
862 // Setup background animation.
863 transform.MakeIdentity();
864
865 transform.Translate(center.x() - kBackgroundStartSizeDip / 2.f,
866 center.y() - kBackgroundStartSizeDip / 2.f);
867
868 scale_factor = kBackgroundStartSizeDip / kBackgroundInitSizeDip;
869 transform.Scale(scale_factor, scale_factor);
870
871 {
872 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
873 settings.SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
874 IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
875 settings.SetTransitionDuration(
876 base::TimeDelta::FromMilliseconds(kFullRetractDurationMs));
xiaohuic591d4f672017-06-22 18:19:14877 settings.SetTweenType(gfx::Tween::SLOW_OUT_LINEAR_IN);
Xiaohui Chen294e7bd2017-06-13 16:57:38878
879 background_layer_->SetTransform(transform);
880 background_layer_->SetOpacity(0);
881 }
882}
883
xiaohuic4d5e7b4b2017-07-17 17:02:23884void VoiceInteractionOverlay::HideAnimation() {
Xiaohui Chenba65544e2017-08-12 02:30:14885 animation_state_ = AnimationState::HIDDEN;
xiaohuic4d5e7b4b2017-07-17 17:02:23886
887 // Setup ripple animations.
888 {
889 ui::ScopedLayerAnimationSettings settings(ripple_layer_->GetAnimator());
890 settings.SetTransitionDuration(
891 base::TimeDelta::FromMilliseconds(kHideDurationMs));
892 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
893 settings.SetPreemptionStrategy(
894 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
895
896 ripple_layer_->SetOpacity(0);
897 }
898
899 // Setup icon animation.
900 {
901 ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
902 settings.SetTransitionDuration(
903 base::TimeDelta::FromMilliseconds(kHideDurationMs));
904 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
905 settings.SetPreemptionStrategy(
906 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
907
908 icon_layer_->SetOpacity(0);
Xiaohui Chen207b12d2017-08-03 18:02:43909 icon_layer_->StopAnimation();
xiaohuic4d5e7b4b2017-07-17 17:02:23910 }
911
912 // Setup background animation.
913 {
914 ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
915 settings.SetTransitionDuration(
916 base::TimeDelta::FromMilliseconds(kHideDurationMs));
917 settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
918 settings.SetPreemptionStrategy(
919 ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
920
921 background_layer_->SetOpacity(0);
922 }
923}
924
Xiaohui Chen294e7bd2017-06-13 16:57:38925} // namespace ash