progress_indicator.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. // Copyright 2021 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. #include "ash/system/progress_indicator/progress_indicator.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/resources/vector_icons/vector_icons.h"
  7. #include "ash/style/ash_color_provider.h"
  8. #include "ash/system/progress_indicator/progress_icon_animation.h"
  9. #include "ash/system/progress_indicator/progress_indicator_animation_registry.h"
  10. #include "ash/system/progress_indicator/progress_ring_animation.h"
  11. #include "base/scoped_observation.h"
  12. #include "base/threading/sequenced_task_runner_handle.h"
  13. #include "third_party/skia/include/core/SkPath.h"
  14. #include "third_party/skia/include/core/SkPathBuilder.h"
  15. #include "third_party/skia/include/core/SkPathMeasure.h"
  16. #include "ui/compositor/layer.h"
  17. #include "ui/compositor/paint_recorder.h"
  18. #include "ui/gfx/canvas.h"
  19. #include "ui/gfx/geometry/insets.h"
  20. #include "ui/gfx/geometry/skia_conversions.h"
  21. #include "ui/gfx/paint_vector_icon.h"
  22. #include "ui/gfx/scoped_canvas.h"
  23. namespace ash {
  24. namespace {
  25. // Appearance.
  26. constexpr float kInnerIconSizeScaleFactor = 14.f / 28.f;
  27. constexpr float kOuterRingOpacity = 0.6f;
  28. constexpr float kInnerRingStrokeWidthScaleFactor = 1.5f / 28.f;
  29. constexpr float kOuterRingStrokeWidth = 2.f;
  30. constexpr float kOuterRingStrokeWidthScaleFactor = 4.f / 28.f;
  31. // Helpers ---------------------------------------------------------------------
  32. // Returns the segment of the specified `path` between `start` and `end`.
  33. // NOTE: It is required that: `0.f` <= `start` <= `end` <= `1.f`.
  34. SkPath CreatePathSegment(const SkPath& path, float start, float end) {
  35. DCHECK_LE(0.f, start);
  36. DCHECK_LE(start, end);
  37. DCHECK_LE(end, 1.f);
  38. SkPathMeasure measure(path, /*force_closed=*/false);
  39. start *= measure.getLength();
  40. end *= measure.getLength();
  41. SkPath path_segment;
  42. measure.getSegment(start, end, &path_segment, /*start_with_move_to=*/true);
  43. return path_segment;
  44. }
  45. // Returns a rounded rect path from the specified `rect` and `corner_radius`.
  46. // NOTE: Unlike a typical rounded rect which starts from the *top-left* corner
  47. // and proceeds clockwise, the rounded rect returned by this method starts at
  48. // the *top-center*. This is a subtle but important detail as calling
  49. // `CreatePathSegment()` with a path created from this method will treat
  50. // *top-center* as the start point, as is needed when painting progress.
  51. SkPath CreateRoundedRectPath(const gfx::RectF& rect, float corner_radius) {
  52. // Top center.
  53. SkPoint top_center(SkPoint::Make(rect.width() / 2.f, 0.f));
  54. // Top right.
  55. SkPoint top_right(SkPoint::Make(rect.width(), 0.f));
  56. SkPoint top_right_end(top_right);
  57. top_right_end.offset(0.f, corner_radius);
  58. // Bottom right.
  59. SkPoint bottom_right(SkPoint::Make(rect.width(), rect.height()));
  60. SkPoint bottom_right_end(bottom_right);
  61. bottom_right_end.offset(-corner_radius, 0.f);
  62. // Bottom left.
  63. SkPoint bottom_left(SkPoint::Make(0.f, rect.height()));
  64. SkPoint bottom_left_end(bottom_left);
  65. bottom_left_end.offset(0.f, -corner_radius);
  66. // Top left.
  67. SkPoint top_left(SkPoint::Make(0.f, 0.f));
  68. SkPoint top_left_end(top_left);
  69. top_left_end.offset(corner_radius, 0.f);
  70. // Build path in the order specified above.
  71. return SkPathBuilder()
  72. .moveTo(top_center)
  73. .arcTo(top_right, top_right_end, corner_radius)
  74. .arcTo(bottom_right, bottom_right_end, corner_radius)
  75. .arcTo(bottom_left, bottom_left_end, corner_radius)
  76. .arcTo(top_left, top_left_end, corner_radius)
  77. .close()
  78. .offset(rect.x(), rect.y())
  79. .detach();
  80. }
  81. // Returns the size for the inner icon given `layer` dimensions.
  82. // NOTE: this method should only be called when v2 animations are enabled.
  83. float GetInnerIconSize(const ui::Layer* layer) {
  84. const gfx::Size& size = layer->size();
  85. return kInnerIconSizeScaleFactor * std::min(size.width(), size.height());
  86. }
  87. // Returns the stroke width for the inner icon given `layer` dimensions.
  88. // NOTE: this method should only be called when v2 animations are enabled.
  89. float GetInnerRingStrokeWidth(const ui::Layer* layer) {
  90. const gfx::Size& size = layer->size();
  91. return kInnerRingStrokeWidthScaleFactor *
  92. std::min(size.width(), size.height());
  93. }
  94. // Returns the opacity for the outer ring given the current `progress`.
  95. float GetOuterRingOpacity(const absl::optional<float>& progress) {
  96. return progress != ProgressIndicator::kProgressComplete ? kOuterRingOpacity
  97. : 1.f;
  98. }
  99. // Returns the stroke width for the outer ring given `layer` dimensions and
  100. // the current `progress`.
  101. float GetOuterRingStrokeWidth(const ui::Layer* layer,
  102. const absl::optional<float>& progress) {
  103. if (progress != ProgressIndicator::kProgressComplete) {
  104. const gfx::Size& size = layer->size();
  105. return kOuterRingStrokeWidthScaleFactor *
  106. std::min(size.width(), size.height());
  107. }
  108. return kOuterRingStrokeWidth;
  109. }
  110. // DefaultProgressIndicatorAnimationRegistry -----------------------------------
  111. // A default implementation of `ProgressIndicatorAnimationRegistry` which is
  112. // associated with a single `ProgressIndicator` and manage progress animations
  113. // as needed.
  114. class DefaultProgressIndicatorAnimationRegistry
  115. : public ProgressIndicatorAnimationRegistry {
  116. public:
  117. DefaultProgressIndicatorAnimationRegistry() = default;
  118. DefaultProgressIndicatorAnimationRegistry(
  119. const DefaultProgressIndicatorAnimationRegistry&) = delete;
  120. DefaultProgressIndicatorAnimationRegistry& operator=(
  121. const DefaultProgressIndicatorAnimationRegistry&) = delete;
  122. ~DefaultProgressIndicatorAnimationRegistry() = default;
  123. // Sets the `progress_indicator` for which this registry manages animations.
  124. // NOTE: This method may be called only once.
  125. void SetProgressIndicator(ProgressIndicator* progress_indicator) {
  126. DCHECK(progress_indicator);
  127. DCHECK(!progress_indicator_);
  128. progress_indicator_ = progress_indicator;
  129. progress_changed_subscription_ =
  130. progress_indicator_->AddProgressChangedCallback(base::BindRepeating(
  131. &DefaultProgressIndicatorAnimationRegistry::OnProgressChanged,
  132. weak_ptr_factory_.GetWeakPtr()));
  133. }
  134. private:
  135. // Invoked on changes to `progress_indicator_` progress.
  136. void OnProgressChanged() {
  137. const absl::optional<float>& progress = progress_indicator_->progress();
  138. if (!progress.has_value()) {
  139. // Progress is indeterminate.
  140. EnsureProgressIconAnimation();
  141. EnsureProgressRingAnimationOfType(
  142. ProgressRingAnimation::Type::kIndeterminate);
  143. } else if (progress != ProgressIndicator::kProgressComplete) {
  144. // Progress is determinate.
  145. EnsureProgressIconAnimation();
  146. EraseProgressRingAnimation();
  147. } else if (previous_progress_ != ProgressIndicator::kProgressComplete) {
  148. // Progress is complete.
  149. EraseProgressIconAnimation();
  150. EnsureProgressRingAnimationOfType(ProgressRingAnimation::Type::kPulse);
  151. }
  152. previous_progress_ = progress;
  153. }
  154. // Invoked on update of the specified `animation`.
  155. void OnProgressRingAnimationUpdated(ProgressRingAnimation* animation) {
  156. if (animation->IsAnimating())
  157. return;
  158. // On completion, `animation` can be removed from the registry. This cannot
  159. // be done directly from `animation`'s subscription callback, so post a task
  160. // to delete `animation` as soon as possible.
  161. base::SequencedTaskRunnerHandle::Get()->PostTask(
  162. FROM_HERE,
  163. base::BindOnce(
  164. [](const base::WeakPtr<DefaultProgressIndicatorAnimationRegistry>&
  165. registry,
  166. ProgressRingAnimation* animation) {
  167. if (!registry)
  168. return;
  169. auto* key = registry->progress_indicator_;
  170. if (registry->GetProgressRingAnimationForKey(key) == animation)
  171. registry->SetProgressRingAnimationForKey(key, nullptr);
  172. },
  173. weak_ptr_factory_.GetWeakPtr(), animation));
  174. }
  175. // Ensures that a progress icon animation exists and is started.
  176. void EnsureProgressIconAnimation() {
  177. if (!GetProgressIconAnimationForKey(progress_indicator_)) {
  178. SetProgressIconAnimationForKey(progress_indicator_,
  179. std::make_unique<ProgressIconAnimation>())
  180. ->Start();
  181. }
  182. }
  183. // Ensures that a progress ring animation of the specified `type` exists and
  184. // is started.
  185. void EnsureProgressRingAnimationOfType(ProgressRingAnimation::Type type) {
  186. auto* ring_animation = GetProgressRingAnimationForKey(progress_indicator_);
  187. if (ring_animation && ring_animation->type() == type)
  188. return;
  189. auto animation = ProgressRingAnimation::CreateOfType(type);
  190. // NOTE: `animation` is owned by `this` so it is safe to use a raw pointer
  191. // and subscription-less callback.
  192. animation->AddUnsafeAnimationUpdatedCallback(
  193. base::BindRepeating(&DefaultProgressIndicatorAnimationRegistry::
  194. OnProgressRingAnimationUpdated,
  195. base::Unretained(this), animation.get()));
  196. SetProgressRingAnimationForKey(progress_indicator_, std::move(animation))
  197. ->Start();
  198. }
  199. // Erases any existing progress icon animation.
  200. void EraseProgressIconAnimation() {
  201. SetProgressIconAnimationForKey(progress_indicator_, nullptr);
  202. }
  203. // Erases any existing progress ring animation.
  204. void EraseProgressRingAnimation() {
  205. SetProgressRingAnimationForKey(progress_indicator_, nullptr);
  206. }
  207. // The progress indicator for which to manage animations and a subscription
  208. // to receive notification of progress change events.
  209. ProgressIndicator* progress_indicator_ = nullptr;
  210. base::CallbackListSubscription progress_changed_subscription_;
  211. // Instantiate `previous_progress_` to completion to avoid starting a pulse
  212. // animation on first progress update.
  213. absl::optional<float> previous_progress_ =
  214. ProgressIndicator::kProgressComplete;
  215. base::WeakPtrFactory<DefaultProgressIndicatorAnimationRegistry>
  216. weak_ptr_factory_{this};
  217. };
  218. // DefaultProgressIndicator ----------------------------------------------------
  219. // A default implementation of `ProgressIndicator` which paints indication of
  220. // progress returned by the specified `progress_callback_`. NOTE: This instance
  221. // comes pre-wired with an animation `registry_` that will manage progress
  222. // animations as needed.
  223. class DefaultProgressIndicator : public ProgressIndicator {
  224. public:
  225. DefaultProgressIndicator(
  226. std::unique_ptr<DefaultProgressIndicatorAnimationRegistry> registry,
  227. base::RepeatingCallback<absl::optional<float>()> progress_callback)
  228. : ProgressIndicator(/*registry=*/registry.get(),
  229. /*animation_key=*/this),
  230. registry_(std::move(registry)),
  231. progress_callback_(std::move(progress_callback)) {
  232. registry_->SetProgressIndicator(this);
  233. }
  234. DefaultProgressIndicator(const DefaultProgressIndicator&) = delete;
  235. DefaultProgressIndicator& operator=(const DefaultProgressIndicator&) = delete;
  236. ~DefaultProgressIndicator() override = default;
  237. private:
  238. // ProgressIndicator:
  239. absl::optional<float> CalculateProgress() const override {
  240. return progress_callback_.Run();
  241. }
  242. std::unique_ptr<DefaultProgressIndicatorAnimationRegistry> registry_;
  243. base::RepeatingCallback<absl::optional<float>()> progress_callback_;
  244. };
  245. } // namespace
  246. // ProgressIndicator -----------------------------------------------------------
  247. // static
  248. constexpr char ProgressIndicator::kClassName[];
  249. constexpr float ProgressIndicator::kProgressComplete;
  250. ProgressIndicator::ProgressIndicator(
  251. ProgressIndicatorAnimationRegistry* animation_registry,
  252. const void* animation_key)
  253. : animation_registry_(animation_registry), animation_key_(animation_key) {
  254. if (!animation_registry_)
  255. return;
  256. // Register to be notified of changes to the icon animation associated with
  257. // this progress indicator's `animation_key_`. Note that it is safe to use a
  258. // raw pointer here since `this` owns the subscription.
  259. icon_animation_changed_subscription_ =
  260. animation_registry_->AddProgressIconAnimationChangedCallbackForKey(
  261. animation_key_,
  262. base::BindRepeating(
  263. &ProgressIndicator::OnProgressIconAnimationChanged,
  264. base::Unretained(this)));
  265. // If an `icon_animation` is already registered, perform additional
  266. // initialization.
  267. ProgressIconAnimation* icon_animation =
  268. animation_registry_->GetProgressIconAnimationForKey(animation_key_);
  269. if (icon_animation)
  270. OnProgressIconAnimationChanged(icon_animation);
  271. // Register to be notified of changes to the ring animation associated with
  272. // this progress indicator's `animation_key_`. Note that it is safe to use a
  273. // raw pointer here since `this` owns the subscription.
  274. ring_animation_changed_subscription_ =
  275. animation_registry_->AddProgressRingAnimationChangedCallbackForKey(
  276. animation_key_,
  277. base::BindRepeating(
  278. &ProgressIndicator::OnProgressRingAnimationChanged,
  279. base::Unretained(this)));
  280. // If `ring_animation` is already registered, perform additional
  281. // initialization.
  282. ProgressRingAnimation* ring_animation =
  283. animation_registry_->GetProgressRingAnimationForKey(animation_key_);
  284. if (ring_animation)
  285. OnProgressRingAnimationChanged(ring_animation);
  286. }
  287. ProgressIndicator::~ProgressIndicator() = default;
  288. // static
  289. std::unique_ptr<ProgressIndicator> ProgressIndicator::CreateDefaultInstance(
  290. base::RepeatingCallback<absl::optional<float>()> progress_callback) {
  291. return std::make_unique<DefaultProgressIndicator>(
  292. std::make_unique<DefaultProgressIndicatorAnimationRegistry>(),
  293. std::move(progress_callback));
  294. }
  295. base::CallbackListSubscription ProgressIndicator::AddProgressChangedCallback(
  296. base::RepeatingClosureList::CallbackType callback) {
  297. return progress_changed_callback_list_.Add(std::move(callback));
  298. }
  299. ui::Layer* ProgressIndicator::CreateLayer() {
  300. DCHECK(!layer());
  301. auto layer = std::make_unique<ui::Layer>(ui::LAYER_TEXTURED);
  302. layer->set_delegate(this);
  303. layer->SetFillsBoundsOpaquely(false);
  304. layer->SetName(kClassName);
  305. Reset(std::move(layer));
  306. return this->layer();
  307. }
  308. void ProgressIndicator::DestroyLayer() {
  309. if (layer())
  310. ReleaseLayer();
  311. }
  312. void ProgressIndicator::InvalidateLayer() {
  313. if (layer())
  314. layer()->SchedulePaint(gfx::Rect(layer()->size()));
  315. }
  316. void ProgressIndicator::SetInnerIconVisible(bool visible) {
  317. if (inner_icon_visible_ == visible)
  318. return;
  319. inner_icon_visible_ = visible;
  320. // It's not necessary to invalidate the `layer()` if progress is complete
  321. // since the inner icon is only painted while progress is incomplete.
  322. if (progress_ != kProgressComplete)
  323. InvalidateLayer();
  324. }
  325. void ProgressIndicator::OnDeviceScaleFactorChanged(float old_scale,
  326. float new_scale) {
  327. InvalidateLayer();
  328. }
  329. void ProgressIndicator::OnPaintLayer(const ui::PaintContext& context) {
  330. // Look up the associated `ring_animation` (if one exists).
  331. ProgressRingAnimation* ring_animation =
  332. animation_registry_
  333. ? animation_registry_->GetProgressRingAnimationForKey(animation_key_)
  334. : nullptr;
  335. // Unless `this` is animating, nothing will paint if `progress_` is complete.
  336. if (progress_ == kProgressComplete && !ring_animation)
  337. return;
  338. float start, end, outer_ring_opacity;
  339. if (ring_animation) {
  340. start = ring_animation->start_position();
  341. end = ring_animation->end_position();
  342. outer_ring_opacity = ring_animation->outer_ring_opacity();
  343. } else {
  344. start = 0.f;
  345. end = progress_.value();
  346. outer_ring_opacity = 1.f;
  347. }
  348. DCHECK_GE(start, 0.f);
  349. DCHECK_LE(start, 1.f);
  350. DCHECK_GE(end, 0.f);
  351. DCHECK_LE(end, 1.f);
  352. DCHECK_GE(outer_ring_opacity, 0.f);
  353. DCHECK_LE(outer_ring_opacity, 1.f);
  354. ui::PaintRecorder recorder(context, layer()->size());
  355. gfx::Canvas* canvas = recorder.canvas();
  356. // The `canvas` should be flipped for RTL.
  357. gfx::ScopedCanvas scoped_canvas(recorder.canvas());
  358. scoped_canvas.FlipIfRTL(layer()->size().width());
  359. // Look up the associated `icon_animation` (if one exists).
  360. ProgressIconAnimation* icon_animation =
  361. animation_registry_
  362. ? animation_registry_->GetProgressIconAnimationForKey(animation_key_)
  363. : nullptr;
  364. if (icon_animation) {
  365. const float opacity = icon_animation->opacity();
  366. DCHECK_GE(opacity, 0.f);
  367. DCHECK_LE(opacity, 1.f);
  368. canvas->SaveLayerAlpha(SK_AlphaOPAQUE * opacity);
  369. }
  370. float outer_ring_stroke_width = GetOuterRingStrokeWidth(layer(), progress_);
  371. gfx::RectF bounds(gfx::SizeF(layer()->size()));
  372. bounds.Inset(gfx::InsetsF(outer_ring_stroke_width / 2.f));
  373. SkPath path(CreateRoundedRectPath(
  374. bounds, /*radius=*/std::min(bounds.width(), bounds.height()) / 2.f));
  375. cc::PaintFlags flags;
  376. flags.setAntiAlias(true);
  377. flags.setStrokeCap(cc::PaintFlags::Cap::kDefault_Cap);
  378. flags.setStrokeWidth(outer_ring_stroke_width);
  379. flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
  380. const SkColor color = AshColorProvider::Get()->GetControlsLayerColor(
  381. AshColorProvider::ControlsLayerType::kFocusRingColor);
  382. // Outer ring.
  383. flags.setColor(SkColorSetA(
  384. color,
  385. SK_AlphaOPAQUE * GetOuterRingOpacity(progress_) * outer_ring_opacity));
  386. if (start <= end) {
  387. // If `start` <= `end`, only a single path segment is necessary.
  388. canvas->DrawPath(CreatePathSegment(path, start, end), flags);
  389. } else {
  390. // If `start` > `end`, two path segments are used to give the illusion of a
  391. // single progress ring. This works around limitations of `SkPathMeasure`
  392. // which require that `start` be <= `end`.
  393. canvas->DrawPath(CreatePathSegment(path, start, 1.f), flags);
  394. canvas->DrawPath(CreatePathSegment(path, 0.f, end), flags);
  395. }
  396. // The inner ring and inner icon should be absent once progress completes.
  397. // This would occur if the progress ring is animating post completion.
  398. if (progress_ == kProgressComplete)
  399. return;
  400. float inner_ring_stroke_width = GetInnerRingStrokeWidth(layer());
  401. if (icon_animation) {
  402. inner_ring_stroke_width *=
  403. icon_animation->inner_ring_stroke_width_scale_factor();
  404. }
  405. const bool inner_ring_visible =
  406. !cc::MathUtil::IsWithinEpsilon(inner_ring_stroke_width, 0.f);
  407. // Inner ring.
  408. if (inner_ring_visible) {
  409. bounds.Inset(gfx::InsetsF(
  410. (outer_ring_stroke_width + inner_ring_stroke_width) / 2.f));
  411. path = CreateRoundedRectPath(
  412. bounds, /*radius=*/std::min(bounds.width(), bounds.height()) / 2.f);
  413. flags.setColor(color);
  414. flags.setStrokeWidth(inner_ring_stroke_width);
  415. canvas->DrawPath(path, flags);
  416. }
  417. // Inner icon.
  418. if (inner_icon_visible_) {
  419. float inner_icon_size = GetInnerIconSize(layer());
  420. gfx::RectF inner_icon_bounds(gfx::SizeF(layer()->size()));
  421. inner_icon_bounds.ClampToCenteredSize(
  422. gfx::SizeF(inner_icon_size, inner_icon_size));
  423. if (icon_animation) {
  424. inner_icon_bounds.Offset(
  425. /*horizontal=*/0.f,
  426. /*vertical=*/icon_animation->inner_icon_translate_y_scale_factor() *
  427. inner_icon_size);
  428. }
  429. gfx::Transform transform;
  430. transform.Translate(inner_icon_bounds.x(), inner_icon_bounds.y());
  431. canvas->Transform(transform);
  432. gfx::PaintVectorIcon(canvas, kHoldingSpaceDownloadIcon, inner_icon_size,
  433. color);
  434. }
  435. }
  436. void ProgressIndicator::UpdateVisualState() {
  437. const auto previous_progress = progress_;
  438. // Cache `progress_`.
  439. progress_ = CalculateProgress();
  440. if (progress_.has_value()) {
  441. DCHECK_GE(progress_.value(), 0.f);
  442. DCHECK_LE(progress_.value(), 1.f);
  443. }
  444. // Notify `progress_` changes.
  445. if (progress_ != previous_progress)
  446. progress_changed_callback_list_.Notify();
  447. }
  448. void ProgressIndicator::OnProgressIconAnimationChanged(
  449. ProgressIconAnimation* animation) {
  450. // Trigger repaint of this progress indicator on `animation` updates. Note
  451. // that it is safe to use a raw pointer here since `this` owns the
  452. // subscription.
  453. if (animation) {
  454. icon_animation_updated_subscription_ =
  455. animation->AddAnimationUpdatedCallback(base::BindRepeating(
  456. &ProgressIndicator::InvalidateLayer, base::Unretained(this)));
  457. }
  458. InvalidateLayer();
  459. }
  460. void ProgressIndicator::OnProgressRingAnimationChanged(
  461. ProgressRingAnimation* animation) {
  462. // Trigger repaint of this progress indicator on `animation` updates. Note
  463. // that it is safe to use a raw pointer here since `this` owns the
  464. // subscription.
  465. if (animation) {
  466. ring_animation_updated_subscription_ =
  467. animation->AddAnimationUpdatedCallback(base::BindRepeating(
  468. &ProgressIndicator::InvalidateLayer, base::Unretained(this)));
  469. }
  470. InvalidateLayer();
  471. }
  472. } // namespace ash