1 // Copyright 2019 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/ambient/ui/photo_view.h"
6 
7 #include <algorithm>
8 #include <iterator>
9 #include <memory>
10 
11 #include "ash/ambient/ambient_constants.h"
12 #include "ash/ambient/model/ambient_backend_model.h"
13 #include "ash/ambient/ui/ambient_background_image_view.h"
14 #include "ash/ambient/ui/ambient_view_delegate.h"
15 #include "ash/ambient/ui/ambient_view_ids.h"
16 #include "ash/public/cpp/metrics_util.h"
17 #include "base/metrics/histogram_functions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "ui/aura/window.h"
20 #include "ui/compositor/animation_throughput_reporter.h"
21 #include "ui/compositor/layer.h"
22 #include "ui/compositor/scoped_layer_animation_settings.h"
23 #include "ui/views/controls/image_view.h"
24 #include "ui/views/layout/fill_layout.h"
25 
26 namespace ash {
27 
28 namespace {
29 
30 constexpr char kPhotoTransitionSmoothness[] =
31     "Ash.AmbientMode.AnimationSmoothness.PhotoTransition";
32 
ReportSmoothness(int value)33 void ReportSmoothness(int value) {
34   base::UmaHistogramPercentageObsoleteDoNotUse(kPhotoTransitionSmoothness,
35                                                value);
36 }
37 
38 }  // namespace
39 
40 // PhotoView ------------------------------------------------------------------
PhotoView(AmbientViewDelegate * delegate)41 PhotoView::PhotoView(AmbientViewDelegate* delegate) : delegate_(delegate) {
42   DCHECK(delegate_);
43   SetID(AmbientViewID::kAmbientPhotoView);
44   Init();
45 }
46 
~PhotoView()47 PhotoView::~PhotoView() {
48   delegate_->GetAmbientBackendModel()->RemoveObserver(this);
49 }
50 
GetClassName() const51 const char* PhotoView::GetClassName() const {
52   return "PhotoView";
53 }
54 
OnImagesChanged()55 void PhotoView::OnImagesChanged() {
56   // If NeedToAnimate() is true, will start transition animation and
57   // UpdateImages() when animation completes. Otherwise, update images
58   // immediately.
59   if (NeedToAnimateTransition()) {
60     StartTransitionAnimation();
61     return;
62   }
63 
64   UpdateImage(delegate_->GetAmbientBackendModel()->GetNextImage());
65 }
66 
Init()67 void PhotoView::Init() {
68   SetPaintToLayer();
69   layer()->SetFillsBoundsOpaquely(false);
70   SetLayoutManager(std::make_unique<views::FillLayout>());
71 
72   for (auto*& image_view : image_views_) {
73     // Creates image views.
74     image_view =
75         AddChildView(std::make_unique<AmbientBackgroundImageView>(delegate_));
76     // Each image view will be animated on its own layer.
77     image_view->SetPaintToLayer();
78     image_view->layer()->SetFillsBoundsOpaquely(false);
79   }
80 
81   // Hides one image view initially for fade in animation.
82   image_views_[1]->layer()->SetOpacity(0.0f);
83 
84   auto* model = delegate_->GetAmbientBackendModel();
85   model->AddObserver(this);
86 
87   UpdateImage(model->GetCurrentImage());
88 }
89 
UpdateImage(const PhotoWithDetails & next_image)90 void PhotoView::UpdateImage(const PhotoWithDetails& next_image) {
91   if (next_image.photo.isNull())
92     return;
93 
94   image_views_[image_index_]->UpdateImage(next_image.photo,
95                                           next_image.related_photo);
96   image_views_[image_index_]->UpdateImageDetails(
97       base::UTF8ToUTF16(next_image.details));
98   image_index_ = 1 - image_index_;
99 }
100 
StartTransitionAnimation()101 void PhotoView::StartTransitionAnimation() {
102   ui::Layer* visible_layer = image_views_[image_index_]->layer();
103   {
104     ui::ScopedLayerAnimationSettings animation(visible_layer->GetAnimator());
105     animation.SetTransitionDuration(kAnimationDuration);
106     animation.SetTweenType(gfx::Tween::LINEAR);
107     animation.SetPreemptionStrategy(
108         ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
109     animation.CacheRenderSurface();
110 
111     ui::AnimationThroughputReporter reporter(
112         animation.GetAnimator(),
113         metrics_util::ForSmoothness(base::BindRepeating(ReportSmoothness)));
114 
115     visible_layer->SetOpacity(0.0f);
116   }
117 
118   ui::Layer* invisible_layer = image_views_[1 - image_index_]->layer();
119   {
120     ui::ScopedLayerAnimationSettings animation(invisible_layer->GetAnimator());
121     animation.SetTransitionDuration(kAnimationDuration);
122     animation.SetTweenType(gfx::Tween::LINEAR);
123     animation.SetPreemptionStrategy(
124         ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
125     animation.CacheRenderSurface();
126     // For simplicity, only observe one animation.
127     animation.AddObserver(this);
128 
129     ui::AnimationThroughputReporter reporter(
130         animation.GetAnimator(),
131         metrics_util::ForSmoothness(base::BindRepeating(ReportSmoothness)));
132 
133     invisible_layer->SetOpacity(1.0f);
134   }
135 }
136 
OnImplicitAnimationsCompleted()137 void PhotoView::OnImplicitAnimationsCompleted() {
138   UpdateImage(delegate_->GetAmbientBackendModel()->GetNextImage());
139   delegate_->OnPhotoTransitionAnimationCompleted();
140 }
141 
NeedToAnimateTransition() const142 bool PhotoView::NeedToAnimateTransition() const {
143   // Can do transition animation if both two images in |images_unscaled_| are
144   // not nullptr. Check the image index 1 is enough.
145   return !image_views_[1]->GetCurrentImage().isNull();
146 }
147 
GetCurrentImagesForTesting()148 const gfx::ImageSkia& PhotoView::GetCurrentImagesForTesting() {
149   return image_views_[image_index_]->GetCurrentImage();
150 }
151 
152 }  // namespace ash
153