910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_DESKS_DESK_ANIMATION_BASE_H_
#define ASH_WM_DESKS_DESK_ANIMATION_BASE_H_

#include "ash/ash_export.h"
#include "ash/public/cpp/metrics_util.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_histogram_enums.h"
#include "ash/wm/desks/root_window_desk_switch_animator.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/compositor/compositor_metrics_tracker.h"

namespace ash {

class DesksController;

// An abstract class that handles the shared operations need to be performed
// when doing an animation that causes a desk switch animation. Subclasses
// such as DeskActivationAnimation and DeskRemovalAnimation implement the
// abstract interface of this class to handle the unique operations specific to
// each animation type.
class ASH_EXPORT DeskAnimationBase
    : public RootWindowDeskSwitchAnimator::Delegate {
 public:
  DeskAnimationBase(DesksController* controller,
                    int ending_desk_index,
                    bool is_continuous_gesture_animation);
  DeskAnimationBase(const DeskAnimationBase&) = delete;
  DeskAnimationBase& operator=(const DeskAnimationBase&) = delete;
  ~DeskAnimationBase() override;

  int ending_desk_index() const { return ending_desk_index_; }
  int visible_desk_changes() const { return visible_desk_changes_; }

  // Launches the animation. This should be done once all animators
  // are created and added to `desk_switch_animators_`. This is to avoid any
  // potential race conditions that might happen if one animator finished phase
  // (1) of the animation while other animators are still being constructed.
  void Launch();

  // Replaces a current animation with an animation to an adjacent desk. By
  // default returns false as most animations do not support replacing.
  virtual bool Replace(bool moving_left, DesksSwitchSource source);

  // Updates a current animation by shifting its animating layer.
  // |scroll_delta_x| is the amount of scroll change since the last scroll
  // update event. Returns false if the animation does not support updating.
  virtual bool UpdateSwipeAnimation(float scroll_delta_x);

  // Ends a current animation, animating to a desk determined by the
  // implementation. Returns false if the animation does not support ending.
  virtual bool EndSwipeAnimation();

  // Returns true if entering/exiting overview during the animation is allowed.
  virtual bool CanEnterOverview() const;
  virtual bool CanEndOverview() const;

  // RootWindowDeskSwitchAnimator::Delegate:
  void OnStartingDeskScreenshotTaken(int ending_desk_index) override;
  void OnEndingDeskScreenshotTaken() override;
  void OnDeskSwitchAnimationFinished() override;

  void set_skip_notify_controller_on_animation_finished_for_testing(bool val) {
    skip_notify_controller_on_animation_finished_for_testing_ = val;
  }

  RootWindowDeskSwitchAnimator* GetDeskSwitchAnimatorAtIndexForTesting(
      size_t index) const;

 protected:
  // This will set `is_overview_toggle_allowed_` before and after calling
  // `ActivateDeskInternal()`, allowing exiting/entering overview during the
  // animation.
  void ActivateDeskDuringAnimation(const Desk* desk,
                                   bool update_window_activation);

  // Immediately switches to the target desk and notifies the desk controller
  // that the animation is done, which will end up deleting `this`.
  void ActivateTargetDeskWithoutAnimation();

  // Returns true if any of the animators have failed, for any reason. In this
  // case, we will abort what we're doing and switch to the target desk without
  // animation.
  bool AnimatorFailed() const;

  // Abstract functions that can be overridden by child classes to do different
  // things when phase (1), and phase (3) completes. Note that
  // `OnDeskSwitchAnimationFinishedInternal()` will be called before the desks
  // screenshot layers, stored in `desk_switch_animators_`, are destroyed.
  virtual void OnStartingDeskScreenshotTakenInternal(int ending_desk_index) = 0;
  virtual void OnDeskSwitchAnimationFinishedInternal() = 0;

  // Since performance here matters, we have to use the UMA histograms macros to
  // report the histograms, but each macro use has to be associated with exactly
  // one histogram name. These functions allow subclasses to return callbacks
  // that report each histogram using the macro with their desired name.
  using LatencyReportCallback =
      base::OnceCallback<void(const base::TimeDelta& latency)>;
  virtual LatencyReportCallback GetLatencyReportCallback() const = 0;
  virtual metrics_util::ReportCallback GetSmoothnessReportCallback() const = 0;

  const raw_ptr<DesksController> controller_;

  // An animator object per each root. Once all the animations are complete,
  // this list is cleared.
  std::vector<std::unique_ptr<RootWindowDeskSwitchAnimator>>
      desk_switch_animators_;

  // The index of the desk that will be active after this animation ends.
  int ending_desk_index_;

  // True if this animation is a continuous gesture animation. Update and End
  // only work when this is true, and we do not start the animation when
  // OnEndingDeskScreenshotTaken is called.
  const bool is_continuous_gesture_animation_;

  // Used when the animation is a continuous gesture animation. True when
  // `EndSwipeAnimation()` has been called and a fast swipe was detected, and
  // reset to false if `Replace()` has been called. A fast swipe is one where
  // the user starts and ends the swipe gesture within half a second. If this is
  // false, we do not start the animation when `OnEndingDeskScreenshotTaken` is
  // called.
  bool did_continuous_gesture_end_fast_ = false;

  // Used for metrics collection to track how many desks changes a user has seen
  // during the animation. This is different from the number of desk activations
  // as the user may switch desks but not activate it if the desk already has a
  // screenshot taken. This will only change for an activation animation, not a
  // remove animation.
  int visible_desk_changes_ = 0;

  // Used for allowing us to enter or exit overview during a desk animation. If
  // there is an ongoing desk animation, we want to prevent unwanted exit or
  // enter overview toggling so that we don't end up in a strange or unexpected
  // state. Toggling overview is only allowed when we are doing an internal desk
  // activation, where we manually set the overview states of the old active
  // desk and the new active desk.
  bool is_overview_toggle_allowed_ = false;

  // Used for the Ash.Desks.AnimationLatency.* histograms. Null if no animation
  // is being prepared. In a continuous desk animation, the latency is reported
  // only for the first desk switch, and `launch_time_` is null thereafter.
  base::TimeTicks launch_time_;

  // ThroughputTracker used for measuring this animation smoothness.
  std::optional<ui::ThroughputTracker> throughput_tracker_;

  // If true, do not notify |controller_| when
  // OnDeskSwitchAnimationFinished() is called. This class and
  // DeskController are tied together in production code, but may not be in a
  // test scenario.
  bool skip_notify_controller_on_animation_finished_for_testing_ = false;

  // Used to pause occlusion updates while taking starting desk screenshot.
  std::unique_ptr<aura::WindowOcclusionTracker::ScopedPause>
      pauser_for_screenshot_;
};

}  // namespace ash

#endif  // ASH_WM_DESKS_DESK_ANIMATION_BASE_H_