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)33void ReportSmoothness(int value) { 34 base::UmaHistogramPercentageObsoleteDoNotUse(kPhotoTransitionSmoothness, 35 value); 36 } 37 38 } // namespace 39 40 // PhotoView ------------------------------------------------------------------ PhotoView(AmbientViewDelegate * delegate)41PhotoView::PhotoView(AmbientViewDelegate* delegate) : delegate_(delegate) { 42 DCHECK(delegate_); 43 SetID(AmbientViewID::kAmbientPhotoView); 44 Init(); 45 } 46 ~PhotoView()47PhotoView::~PhotoView() { 48 delegate_->GetAmbientBackendModel()->RemoveObserver(this); 49 } 50 GetClassName() const51const char* PhotoView::GetClassName() const { 52 return "PhotoView"; 53 } 54 OnImagesChanged()55void 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()67void 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)90void 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()101void 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()137void PhotoView::OnImplicitAnimationsCompleted() { 138 UpdateImage(delegate_->GetAmbientBackendModel()->GetNextImage()); 139 delegate_->OnPhotoTransitionAnimationCompleted(); 140 } 141 NeedToAnimateTransition() const142bool 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()148const gfx::ImageSkia& PhotoView::GetCurrentImagesForTesting() { 149 return image_views_[image_index_]->GetCurrentImage(); 150 } 151 152 } // namespace ash 153