1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182

ash / assistant / ui / main_stage / animated_container_view.h [blame]

// Copyright 2019 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_ASSISTANT_UI_MAIN_STAGE_ANIMATED_CONTAINER_VIEW_H_
#define ASH_ASSISTANT_UI_MAIN_STAGE_ANIMATED_CONTAINER_VIEW_H_

#include <memory>
#include <vector>

#include "ash/assistant/model/assistant_interaction_model_observer.h"
#include "ash/assistant/model/assistant_response_observer.h"
#include "ash/assistant/ui/base/assistant_scroll_view.h"
#include "ash/public/cpp/assistant/controller/assistant_controller.h"
#include "ash/public/cpp/assistant/controller/assistant_controller_observer.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chromeos/ash/services/libassistant/public/cpp/assistant_suggestion.h"
#include "ui/base/metadata/metadata_header_macros.h"

namespace ui {
class CallbackLayerAnimationObserver;
}  // namespace ui

namespace ash {

class AssistantResponse;
class AssistantUiElement;
class AssistantViewDelegate;
class ElementAnimator;

// A view that will observe the AssistantResponse and which will use
// ElementAnimator to animate each child view.
//
// To use this you should implement HandleUiElement() and/or HandleSuggestion()
// to:
//    - Add new child views as appropriate.
//    - Return animators for any newly created views.
//
// More in detail, this is what will happen:
//    1) When AssistantInteractionModelObserver::OnCommittedQueryChanged() is
//       observed, FadeOut() is called on all ElementAnimator instances.
//       Furthermore all views will stop processing events (like click).
//    2) When AssistantInteractionModelObserver::OnResponseChanged() is
//       observed, we wait until the FadeOut() animations are complete.
//    3) Next AnimateOut() is invoked on all ElementAnimator instances.
//    4) When these animations are complete, all child views are removed.
//    5) OnAllViewsRemoved() is invoked to inform the derived class all child
//       views are removed.
//    6) Next HandleSuggestion() and HandleUiElement() is called for the new
//       AssistantResponse. In those methods, the derived class should add the
//       child views for the new response, as well as return ElementAnimator
//       instances.
//    7) When all new child views have been added, AnimateIn() is invoked on
//       all ElementAnimator instances.
//    8) Finally when this animation is complete the derived class is informed
//       through OnAllViewsAnimatedIn().
class COMPONENT_EXPORT(ASSISTANT_UI) AnimatedContainerView
    : public AssistantScrollView,
      public AssistantScrollView::Observer,
      public AssistantControllerObserver,
      public AssistantInteractionModelObserver,
      public AssistantResponseObserver {
  METADATA_HEADER(AnimatedContainerView, AssistantScrollView)

 public:
  using AssistantSuggestion = assistant::AssistantSuggestion;

  explicit AnimatedContainerView(AssistantViewDelegate* delegate);
  AnimatedContainerView(const AnimatedContainerView&) = delete;
  AnimatedContainerView& operator=(const AnimatedContainerView&) = delete;
  ~AnimatedContainerView() override;

  // AssistantScrollView:
  void PreferredSizeChanged() override;
  void OnChildViewRemoved(View* observed_view, View* child) override;

  // AssistantControllerObserver:
  void OnAssistantControllerDestroying() override;

  // AssistantInteractionModelObserver:
  void OnCommittedQueryChanged(const AssistantQuery& query) override;
  void OnResponseChanged(const scoped_refptr<AssistantResponse>&) override;
  void OnResponseCleared() override;

  // AssistantResponseObserver:
  void OnUiElementAdded(const AssistantUiElement* ui_element) override;
  void OnSuggestionsAdded(
      const std::vector<AssistantSuggestion>& suggestions) override;

  // Remove all current responses/views.
  // This will abort all in progress animations, and remove all the child views
  // and their animators.
  void RemoveAllViews();

 protected:
  // Callback called when all (new) views have been added.
  // This is called when the animate-in animations are done.
  virtual void OnAllViewsAnimatedIn() {}

  // Callback called when all (old) views have been removed.
  // This is called when the exit animations are done.
  virtual void OnAllViewsRemoved() {}

  // Callback called to create a view for a UI element.
  // The implementer should:
  //    - Create and add the appropriate views::View.
  //    - Return an ElementAnimator to animate the view. Note that it is
  //      permissible to return |nullptr| if no managed animation is desired.
  virtual std::unique_ptr<ElementAnimator> HandleUiElement(
      const AssistantUiElement* ui_element);

  // Callback called to create a view for a suggestion.
  // The implementer should:
  //    - Create and add the appropriate views::View.
  //    - Return an ElementAnimator to animate the view. Note that it is
  //      permissible to return |nullptr| if no managed animation is desired.
  virtual std::unique_ptr<ElementAnimator> HandleSuggestion(
      const AssistantSuggestion& suggestion);

  AssistantViewDelegate* delegate() { return delegate_; }

 private:
  class ScopedDisablePreferredSizeChanged;
  void SetPropagatePreferredSizeChanged(bool propagate);

  void ChangeResponse(const scoped_refptr<const AssistantResponse>& response);
  void AddResponse(scoped_refptr<const AssistantResponse> response);

  bool IsAnimatingViews() const;
  void AddElementAnimatorAndAnimateInView(std::unique_ptr<ElementAnimator>);
  void FadeOutViews();

  void EnableInteractions() { SetInteractionsEnabled(true); }
  void DisableInteractions() { SetInteractionsEnabled(false); }
  void SetInteractionsEnabled(bool enabled);

  static bool AnimateInObserverCallback(
      const base::WeakPtr<AnimatedContainerView>& weak_ptr,
      const ui::CallbackLayerAnimationObserver& observer);
  static bool AnimateOutObserverCallback(
      const base::WeakPtr<AnimatedContainerView>& weak_ptr,
      const ui::CallbackLayerAnimationObserver& observer);
  static bool FadeOutObserverCallback(
      const base::WeakPtr<AnimatedContainerView>& weak_ptr,
      const ui::CallbackLayerAnimationObserver& observer);

  const raw_ptr<AssistantViewDelegate>
      delegate_;  // Owned by AssistantController.

  // The animators used to trigger the animations of the individual views.
  std::vector<std::unique_ptr<ElementAnimator>> animators_;

  // Whether we should allow propagation of PreferredSizeChanged events.
  // Because we only animate views in/out in batches, we can prevent
  // over-propagation of PreferredSizeChanged events by waiting until the
  // entirety of a response has been added/removed before propagating. This
  // reduces layout passes.
  bool propagate_preferred_size_changed_ = true;

  // Whether the animate-out animation is in progress.
  bool animate_out_in_progress_ = false;

  // Whether the fade-out animation is in progress.
  bool fade_out_in_progress_ = false;

  // Shared pointers to the response that is currently on stage as well as the
  // queued response to be presented following the former's animated exit. We
  // use shared pointers to ensure that underlying views are not destroyed
  // before we have an opportunity to remove their associated views.
  scoped_refptr<const AssistantResponse> response_;
  scoped_refptr<const AssistantResponse> queued_response_;

  base::ScopedObservation<AssistantController, AssistantControllerObserver>
      assistant_controller_observation_{this};

  base::WeakPtrFactory<AnimatedContainerView> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_ASSISTANT_UI_MAIN_STAGE_ANIMATED_CONTAINER_VIEW_H_