blob: 4ac1ca052857b556b24075d5cec700c7621c84a8 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/assistant/ui/main_stage/assistant_footer_view.h"
#include <utility>
#include "ash/assistant/assistant_controller.h"
#include "ash/assistant/assistant_setup_controller.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/main_stage/assistant_opt_in_view.h"
#include "ash/assistant/ui/main_stage/suggestion_container_view.h"
#include "ash/assistant/util/animation_util.h"
#include "ash/shell.h"
#include "ash/voice_interaction/voice_interaction_controller.h"
#include "base/bind.h"
#include "base/time/time.h"
#include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_animator.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
namespace {
// Appearance.
constexpr int kPreferredHeightDip = 48;
// Animation.
constexpr base::TimeDelta kAnimationFadeInDelay =
base::TimeDelta::FromMilliseconds(167);
constexpr base::TimeDelta kAnimationFadeInDuration =
base::TimeDelta::FromMilliseconds(167);
constexpr base::TimeDelta kAnimationFadeOutDuration =
base::TimeDelta::FromMilliseconds(167);
} // namespace
AssistantFooterView::AssistantFooterView(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller),
animation_observer_(std::make_unique<ui::CallbackLayerAnimationObserver>(
/*animation_started_callback=*/base::BindRepeating(
&AssistantFooterView::OnAnimationStarted,
base::Unretained(this)),
/*animation_ended_callback=*/base::BindRepeating(
&AssistantFooterView::OnAnimationEnded,
base::Unretained(this)))),
voice_interaction_observer_binding_(this) {
InitLayout();
// Observe voice interaction for changes to consent state.
mojom::VoiceInteractionObserverPtr ptr;
voice_interaction_observer_binding_.Bind(mojo::MakeRequest(&ptr));
Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
}
AssistantFooterView::~AssistantFooterView() = default;
const char* AssistantFooterView::GetClassName() const {
return "AssistantFooterView";
}
gfx::Size AssistantFooterView::CalculatePreferredSize() const {
return gfx::Size(INT_MAX, GetHeightForWidth(INT_MAX));
}
int AssistantFooterView::GetHeightForWidth(int width) const {
return kPreferredHeightDip;
}
void AssistantFooterView::InitLayout() {
SetLayoutManager(std::make_unique<views::FillLayout>());
// Initial view state is based on user consent state.
const bool setup_completed =
Shell::Get()->voice_interaction_controller()->setup_completed();
// Suggestion container.
suggestion_container_ = new SuggestionContainerView(assistant_controller_);
suggestion_container_->set_can_process_events_within_subtree(setup_completed);
// Suggestion container will be animated on its own layer.
suggestion_container_->SetPaintToLayer();
suggestion_container_->layer()->SetFillsBoundsOpaquely(false);
suggestion_container_->layer()->SetOpacity(setup_completed ? 1.f : 0.f);
suggestion_container_->SetVisible(setup_completed);
AddChildView(suggestion_container_);
// Opt in view.
opt_in_view_ = new AssistantOptInView();
opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
opt_in_view_->set_delegate(assistant_controller_->setup_controller());
// Opt in view will be animated on its own layer.
opt_in_view_->SetPaintToLayer();
opt_in_view_->layer()->SetFillsBoundsOpaquely(false);
opt_in_view_->layer()->SetOpacity(setup_completed ? 0.f : 1.f);
opt_in_view_->SetVisible(!setup_completed);
AddChildView(opt_in_view_);
}
void AssistantFooterView::OnVoiceInteractionSetupCompleted(bool completed) {
using assistant::util::CreateLayerAnimationSequence;
using assistant::util::CreateOpacityElement;
using assistant::util::StartLayerAnimationSequence;
// When the consent state changes, we need to hide/show the appropriate views.
views::View* hide_view =
completed ? static_cast<views::View*>(opt_in_view_)
: static_cast<views::View*>(suggestion_container_);
views::View* show_view =
completed ? static_cast<views::View*>(suggestion_container_)
: static_cast<views::View*>(opt_in_view_);
// Reset visibility to enable animation.
hide_view->SetVisible(true);
show_view->SetVisible(true);
// Hide the view for the previous consent state by fading to 0% opacity.
StartLayerAnimationSequence(hide_view->layer()->GetAnimator(),
CreateLayerAnimationSequence(CreateOpacityElement(
0.f, kAnimationFadeOutDuration)),
// Observe the animation.
animation_observer_.get());
// Show the view for the next consent state by fading to 100% opacity with
// delay.
StartLayerAnimationSequence(
show_view->layer()->GetAnimator(),
CreateLayerAnimationSequence(
ui::LayerAnimationElement::CreatePauseElement(
ui::LayerAnimationElement::AnimatableProperty::OPACITY,
kAnimationFadeInDelay),
CreateOpacityElement(1.f, kAnimationFadeInDuration)),
// Observe the animation.
animation_observer_.get());
// Set the observer to active to receive animation callback events.
animation_observer_->SetActive();
}
void AssistantFooterView::OnAnimationStarted(
const ui::CallbackLayerAnimationObserver& observer) {
// Our views should not process events while animating.
suggestion_container_->set_can_process_events_within_subtree(false);
opt_in_view_->set_can_process_events_within_subtree(false);
}
bool AssistantFooterView::OnAnimationEnded(
const ui::CallbackLayerAnimationObserver& observer) {
const bool setup_completed =
Shell::Get()->voice_interaction_controller()->setup_completed();
// Only the view relevant to our consent state should process events.
suggestion_container_->set_can_process_events_within_subtree(setup_completed);
suggestion_container_->SetVisible(setup_completed);
opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
opt_in_view_->SetVisible(!setup_completed);
// Return false to prevent the observer from destroying itself.
return false;
}
} // namespace ash