Commit b106fdfd authored by David Black's avatar David Black Committed by Commit Bot

Fade out previous Assistant response.

The motion spec dictates that the stage should not be cleared when a
new query is committed. Instead, the previous query response should
fade out while we wait for the next response to come in (which should
also be animated).

This CL does not implement the entire motion spec, only the portion
that fades out the previous query response while we are waiting for
the next response to arrive.

Changes are guarded behind kIsMotionSpecEnabled.

See bug for spec/demo.

Bug: b:112091713
Change-Id: I88cb4703f8b8b697ce87ac5a70162853d8826244
Reviewed-on: https://chromium-review.googlesource.com/1159223Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#580605}
parent 9691382f
......@@ -10,6 +10,7 @@
#include "ash/assistant/model/assistant_query.h"
#include "ash/assistant/model/assistant_response.h"
#include "ash/assistant/model/assistant_ui_element.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/util/deep_link_util.h"
#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
#include "ash/shell.h"
......@@ -173,10 +174,12 @@ void AssistantInteractionController::OnInteractionStarted(
assistant_interaction_model_.CommitPendingQuery();
assistant_interaction_model_.SetMicState(MicState::kClosed);
// Clear the interaction to wipe the stage.
assistant_interaction_model_.ClearInteraction(
/*retain_committed_query=*/true,
/*retain_pending_query=*/false);
if (!assistant::ui::kIsMotionSpecEnabled) {
// Clear the interaction to wipe the stage.
assistant_interaction_model_.ClearInteraction(
/*retain_committed_query=*/true,
/*retain_pending_query=*/false);
}
}
// Start caching a new Assistant response for the interaction.
......@@ -297,10 +300,12 @@ void AssistantInteractionController::OnSpeechRecognitionFinalResult(
std::make_unique<AssistantVoiceQuery>(final_result));
assistant_interaction_model_.CommitPendingQuery();
// Clear the interaction to wipe the stage.
assistant_interaction_model_.ClearInteraction(
/*retain_committed_query=*/true,
/*retain_pending_response=*/true);
if (!assistant::ui::kIsMotionSpecEnabled) {
// Clear the interaction to wipe the stage.
assistant_interaction_model_.ClearInteraction(
/*retain_committed_query=*/true,
/*retain_pending_response=*/true);
}
}
void AssistantInteractionController::OnSpeechLevelUpdated(float speech_level) {
......
......@@ -202,12 +202,6 @@ void AssistantMainStage::InitQueryLayoutContainer(
query_layout_container_->set_can_process_events_within_subtree(false);
query_layout_container_->SetLayoutManager(std::make_unique<StackLayout>());
// The children of the query layout container will be animated but should be
// clipped by their parent's bounds.
query_layout_container_->SetPaintToLayer();
query_layout_container_->layer()->SetFillsBoundsOpaquely(false);
query_layout_container_->layer()->SetMasksToBounds(true);
AddChildView(query_layout_container_);
}
......
......@@ -84,6 +84,8 @@ void SuggestionContainerView::InitLayout() {
void SuggestionContainerView::OnResponseChanged(
const AssistantResponse& response) {
OnResponseCleared();
using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
for (const std::pair<int, const AssistantSuggestion*>& suggestion :
response.GetSuggestions()) {
......
......@@ -11,11 +11,20 @@
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/main_stage/assistant_header_view.h"
#include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
#include "ash/assistant/util/animation_util.h"
#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
#include "base/base64.h"
#include "base/callback.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer_animator.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
......@@ -24,10 +33,70 @@ namespace {
// Appearance.
constexpr int kPaddingHorizontalDip = 32;
// Animation.
constexpr float kCardElementAnimationFadeOutOpacity = 0.26f;
constexpr float kTextElementAnimationFadeOutOpacity = 0.f;
constexpr base::TimeDelta kUiElementAnimationFadeOutDuration =
base::TimeDelta::FromMilliseconds(167);
// WebContents.
constexpr char kDataUriPrefix[] = "data:text/html;base64,";
// CardElementViewHolder -------------------------------------------------------
class CardElementViewHolder : public views::NativeViewHost,
public views::ViewObserver {
public:
explicit CardElementViewHolder(views::View* card_element_view)
: card_element_view_(card_element_view) {
views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
params.name = GetClassName();
params.delegate = new views::WidgetDelegateView();
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
child_widget_ = std::make_unique<views::Widget>();
child_widget_->Init(params);
contents_view_ = params.delegate->GetContentsView();
contents_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
contents_view_->AddChildView(card_element_view_);
card_element_view_->AddObserver(this);
}
~CardElementViewHolder() override {
card_element_view_->RemoveObserver(this);
}
// views::NativeViewHost:
const char* GetClassName() const override { return "CardElementViewHolder"; }
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* view) override {
contents_view_->SetPreferredSize(view->GetPreferredSize());
SetPreferredSize(view->GetPreferredSize());
}
void Attach() {
views::NativeViewHost::Attach(child_widget_->GetNativeView());
}
private:
views::View* const card_element_view_; // Owned by WebContentsManager.
std::unique_ptr<views::Widget> child_widget_;
views::View* contents_view_ = nullptr; // Owned by |child_widget_|.
DISALLOW_COPY_AND_ASSIGN(CardElementViewHolder);
};
} // namespace
// UiElementContainerView ------------------------------------------------------
UiElementContainerView::UiElementContainerView(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller),
......@@ -64,6 +133,21 @@ void UiElementContainerView::InitLayout() {
AddChildView(assistant_header_view_.get());
}
void UiElementContainerView::OnCommittedQueryChanged(
const AssistantQuery& query) {
if (!assistant::ui::kIsMotionSpecEnabled)
return;
// When a query is committed, we fade out the views for the previous response
// until the next Assistant response has been received.
using namespace assistant::util;
for (const std::pair<ui::Layer*, float>& pair : ui_element_layers_) {
pair.first->GetAnimator()->StartAnimation(
CreateLayerAnimationSequence(CreateOpacityElement(
/*opacity=*/pair.second, kUiElementAnimationFadeOutDuration)));
}
}
void UiElementContainerView::OnResponseChanged(
const AssistantResponse& response) {
OnResponseCleared();
......@@ -86,6 +170,8 @@ void UiElementContainerView::OnResponseCleared() {
render_request_weak_factory_.InvalidateWeakPtrs();
RemoveAllChildViews(/*delete_children=*/true);
ui_element_layers_.clear();
AddChildView(assistant_header_view_.get());
PreferredSizeChanged();
......@@ -161,8 +247,21 @@ void UiElementContainerView::OnCardReady(
// When the card has been rendered in the same process, its view is
// available in the AnswerCardContentsRegistry's token-to-view map.
if (app_list::AnswerCardContentsRegistry::Get()) {
AddChildView(app_list::AnswerCardContentsRegistry::Get()->GetView(
embed_token.value()));
CardElementViewHolder* view_holder = new CardElementViewHolder(
app_list::AnswerCardContentsRegistry::Get()->GetView(
embed_token.value()));
AddChildView(view_holder);
view_holder->Attach();
if (assistant::ui::kIsMotionSpecEnabled) {
// The view will be animated on its own layer, so we need to do some
// initial layer setup and cache the layer with its desired opacity.
view_holder->native_view()->layer()->SetFillsBoundsOpaquely(false);
ui_element_layers_.push_back(
std::pair<ui::Layer*, float>(view_holder->native_view()->layer(),
kCardElementAnimationFadeOutOpacity));
}
}
// TODO(dmblack): Handle Mash case.
......@@ -177,8 +276,18 @@ void UiElementContainerView::OnTextElementAdded(
const AssistantTextElement* text_element) {
DCHECK(!is_processing_ui_element_);
AddChildView(new AssistantTextElementView(text_element));
views::View* text_element_view = new AssistantTextElementView(text_element);
if (assistant::ui::kIsMotionSpecEnabled) {
// The view will be animated on its own layer, so we need to do some initial
// layer setup and cache the layer with its desired opacity.
text_element_view->SetPaintToLayer();
text_element_view->layer()->SetFillsBoundsOpaquely(false);
ui_element_layers_.push_back(std::pair<ui::Layer*, float>(
text_element_view->layer(), kTextElementAnimationFadeOutOpacity));
}
AddChildView(text_element_view);
PreferredSizeChanged();
}
......
......@@ -21,6 +21,7 @@ class AssistantHeaderView;
class AssistantResponse;
class AssistantTextElement;
class AssistantUiElement;
enum class AssistantUiElementType;
// UiElementContainerView is the child of AssistantMainView concerned with
// laying out text views and embedded card views in response to Assistant
......@@ -35,6 +36,7 @@ class UiElementContainerView : public views::View,
void ChildPreferredSizeChanged(views::View* child) override;
// AssistantInteractionModelObserver:
void OnCommittedQueryChanged(const AssistantQuery& query) override;
void OnResponseChanged(const AssistantResponse& response) override;
void OnResponseCleared() override;
......@@ -70,6 +72,10 @@ class UiElementContainerView : public views::View,
// are added to |pending_ui_element_list_| and processed later.
bool is_processing_ui_element_ = false;
// UI elements will be animated on their own layers. We track the desired
// opacity to which each layer should be animated.
std::vector<std::pair<ui::Layer*, float>> ui_element_layers_;
// Weak pointer factory used for card rendering requests.
base::WeakPtrFactory<UiElementContainerView> render_request_weak_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment