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

Finishes Assistant container opt in UI.

AssistantOptInView visibility state is synced to the user's consent
state as known by VoiceInteractionController. Note that it is possible
for VoiceInteractionController to get out of sync (b/111882861).

This CL also:
- Animates footer entry on Assistant launch.
- Animates transition of footer views when consent is granted/revoked.

See bug for demo.

Bug: b:111315696
Change-Id: I848c1301e13795711e8e1ff5916ac8e4020f4c0b
Reviewed-on: https://chromium-review.googlesource.com/1171853Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#582463}
parent ef7fb7cc
...@@ -4,12 +4,19 @@ ...@@ -4,12 +4,19 @@
#include "ash/assistant/ui/main_stage/assistant_footer_view.h" #include "ash/assistant/ui/main_stage/assistant_footer_view.h"
#include <memory>
#include "ash/assistant/assistant_controller.h" #include "ash/assistant/assistant_controller.h"
#include "ash/assistant/assistant_setup_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/assistant_opt_in_view.h"
#include "ash/assistant/ui/main_stage/suggestion_container_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" #include "ui/views/layout/fill_layout.h"
namespace ash { namespace ash {
...@@ -19,12 +26,33 @@ namespace { ...@@ -19,12 +26,33 @@ namespace {
// Appearance. // Appearance.
constexpr int kPreferredHeightDip = 48; 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 } // namespace
AssistantFooterView::AssistantFooterView( AssistantFooterView::AssistantFooterView(
AssistantController* assistant_controller) AssistantController* assistant_controller)
: assistant_controller_(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(); 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; AssistantFooterView::~AssistantFooterView() = default;
...@@ -45,19 +73,99 @@ void AssistantFooterView::ChildVisibilityChanged(views::View* child) { ...@@ -45,19 +73,99 @@ void AssistantFooterView::ChildVisibilityChanged(views::View* child) {
PreferredSizeChanged(); PreferredSizeChanged();
} }
// TODO(dmblack): Handle opted out/in state.
void AssistantFooterView::InitLayout() { void AssistantFooterView::InitLayout() {
SetLayoutManager(std::make_unique<views::FillLayout>()); 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.
suggestion_container_ = new SuggestionContainerView(assistant_controller_); 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);
AddChildView(suggestion_container_); AddChildView(suggestion_container_);
// Opt in view. // Opt in view.
opt_in_view_ = new AssistantOptInView(); opt_in_view_ = new AssistantOptInView();
opt_in_view_->SetVisible(false); opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
opt_in_view_->set_delegate(assistant_controller_->setup_controller()); 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);
AddChildView(opt_in_view_); AddChildView(opt_in_view_);
} }
void AssistantFooterView::OnVoiceInteractionSetupCompleted(bool completed) {
// 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_);
// When the motion spec is disabled, we don't animate the transition.
if (!assistant::ui::kIsMotionSpecEnabled) {
OnAnimationStarted(*animation_observer_);
hide_view->layer()->SetOpacity(0.f);
show_view->layer()->SetOpacity(1.f);
OnAnimationEnded(*animation_observer_);
return;
}
using namespace assistant::util;
// Hide the view for the previous consent state by fading to 0% opacity.
hide_view->layer()->GetAnimator()->StartAnimation(
CreateLayerAnimationSequence(
CreateOpacityElement(0.f, kAnimationFadeOutDuration)));
// 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);
opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
// Return false to prevent the observer from destroying itself.
return false;
}
} // namespace ash } // namespace ash
...@@ -5,16 +5,25 @@ ...@@ -5,16 +5,25 @@
#ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_FOOTER_VIEW_H_ #ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_FOOTER_VIEW_H_
#define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_FOOTER_VIEW_H_ #define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_FOOTER_VIEW_H_
#include <memory>
#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
#include "base/macros.h" #include "base/macros.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "ui/views/view.h" #include "ui/views/view.h"
namespace ui {
class CallbackLayerAnimationObserver;
} // namespace ui
namespace ash { namespace ash {
class AssistantController; class AssistantController;
class AssistantOptInView; class AssistantOptInView;
class SuggestionContainerView; class SuggestionContainerView;
class AssistantFooterView : public views::View { class AssistantFooterView : public views::View,
mojom::VoiceInteractionObserver {
public: public:
explicit AssistantFooterView(AssistantController* assistant_controller); explicit AssistantFooterView(AssistantController* assistant_controller);
~AssistantFooterView() override; ~AssistantFooterView() override;
...@@ -25,14 +34,32 @@ class AssistantFooterView : public views::View { ...@@ -25,14 +34,32 @@ class AssistantFooterView : public views::View {
void ChildPreferredSizeChanged(views::View* child) override; void ChildPreferredSizeChanged(views::View* child) override;
void ChildVisibilityChanged(views::View* child) override; void ChildVisibilityChanged(views::View* child) override;
// mojom::VoiceInteractionObserver:
void OnVoiceInteractionStatusChanged(
mojom::VoiceInteractionState state) override {}
void OnVoiceInteractionSettingsEnabled(bool enabled) override {}
void OnVoiceInteractionContextEnabled(bool enabled) override {}
void OnVoiceInteractionHotwordEnabled(bool enabled) override {}
void OnVoiceInteractionSetupCompleted(bool completed) override;
void OnAssistantFeatureAllowedChanged(
mojom::AssistantAllowedState state) override {}
private: private:
void InitLayout(); void InitLayout();
void OnAnimationStarted(const ui::CallbackLayerAnimationObserver& observer);
bool OnAnimationEnded(const ui::CallbackLayerAnimationObserver& observer);
AssistantController* const assistant_controller_; // Owned by Shell. AssistantController* const assistant_controller_; // Owned by Shell.
SuggestionContainerView* suggestion_container_; // Owned by view hierarchy. SuggestionContainerView* suggestion_container_; // Owned by view hierarchy.
AssistantOptInView* opt_in_view_; // Owned by view hierarchy. AssistantOptInView* opt_in_view_; // Owned by view hierarchy.
std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_;
mojo::Binding<mojom::VoiceInteractionObserver>
voice_interaction_observer_binding_;
DISALLOW_COPY_AND_ASSIGN(AssistantFooterView); DISALLOW_COPY_AND_ASSIGN(AssistantFooterView);
}; };
......
...@@ -67,6 +67,12 @@ constexpr base::TimeDelta kFooterAnimationFadeInDuration = ...@@ -67,6 +67,12 @@ constexpr base::TimeDelta kFooterAnimationFadeInDuration =
constexpr base::TimeDelta kFooterAnimationFadeOutDuration = constexpr base::TimeDelta kFooterAnimationFadeOutDuration =
base::TimeDelta::FromMilliseconds(100); base::TimeDelta::FromMilliseconds(100);
// Footer entry animation.
constexpr base::TimeDelta kFooterEntryAnimationFadeInDelay =
base::TimeDelta::FromMilliseconds(283);
constexpr base::TimeDelta kFooterEntryAnimationFadeInDuration =
base::TimeDelta::FromMilliseconds(167);
// Greeting animation. // Greeting animation.
constexpr base::TimeDelta kGreetingAnimationFadeInDelay = constexpr base::TimeDelta kGreetingAnimationFadeInDelay =
base::TimeDelta::FromMilliseconds(33); base::TimeDelta::FromMilliseconds(33);
...@@ -541,7 +547,7 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible, ...@@ -541,7 +547,7 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible,
AssistantSource source) { AssistantSource source) {
if (visible) { if (visible) {
// When Assistant UI is shown and the motion spec is enabled, we animate in // When Assistant UI is shown and the motion spec is enabled, we animate in
// the appearance of the greeting label. // the appearance of the greeting label and footer.
if (assistant::ui::kIsMotionSpecEnabled) { if (assistant::ui::kIsMotionSpecEnabled) {
using namespace assistant::util; using namespace assistant::util;
...@@ -550,7 +556,7 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible, ...@@ -550,7 +556,7 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible,
gfx::Transform transform; gfx::Transform transform;
transform.Translate(0, kGreetingAnimationTranslationDip); transform.Translate(0, kGreetingAnimationTranslationDip);
// Set up or pre-animation values. // Set up our pre-animation values.
greeting_label_->layer()->SetOpacity(0.f); greeting_label_->layer()->SetOpacity(0.f);
greeting_label_->layer()->SetTransform(transform); greeting_label_->layer()->SetTransform(transform);
...@@ -566,6 +572,15 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible, ...@@ -566,6 +572,15 @@ void AssistantMainStage::OnUiVisibilityChanged(bool visible,
ui::LayerAnimationElement::AnimatableProperty::OPACITY, ui::LayerAnimationElement::AnimatableProperty::OPACITY,
kGreetingAnimationFadeInDelay), kGreetingAnimationFadeInDelay),
CreateOpacityElement(1.f, kGreetingAnimationFadeInDuration))}); CreateOpacityElement(1.f, kGreetingAnimationFadeInDuration))});
// Animate the footer from 0% to 100% opacity with delay.
footer_->layer()->SetOpacity(0.f);
footer_->layer()->GetAnimator()->StartAnimation(
CreateLayerAnimationSequence(
ui::LayerAnimationElement::CreatePauseElement(
ui::LayerAnimationElement::AnimatableProperty::OPACITY,
kFooterEntryAnimationFadeInDelay),
CreateOpacityElement(1.f, kFooterEntryAnimationFadeInDuration)));
} }
return; return;
} }
......
...@@ -36,9 +36,11 @@ views::StyledLabel::RangeStyleInfo CreateStyleInfo( ...@@ -36,9 +36,11 @@ views::StyledLabel::RangeStyleInfo CreateStyleInfo(
// AssistantOptInContainer ----------------------------------------------------- // AssistantOptInContainer -----------------------------------------------------
class AssistantOptInContainer : public views::View { class AssistantOptInContainer : public views::Button {
public: public:
AssistantOptInContainer() = default; explicit AssistantOptInContainer(views::ButtonListener* listener)
: views::Button(listener) {}
~AssistantOptInContainer() override = default; ~AssistantOptInContainer() override = default;
// views::View: // views::View:
...@@ -70,7 +72,7 @@ class AssistantOptInContainer : public views::View { ...@@ -70,7 +72,7 @@ class AssistantOptInContainer : public views::View {
// AssistantOptInView ---------------------------------------------------------- // AssistantOptInView ----------------------------------------------------------
AssistantOptInView::AssistantOptInView() : views::Button(/*listener=*/this) { AssistantOptInView::AssistantOptInView() {
InitLayout(); InitLayout();
} }
...@@ -96,7 +98,8 @@ void AssistantOptInView::InitLayout() { ...@@ -96,7 +98,8 @@ void AssistantOptInView::InitLayout() {
views::BoxLayout::MainAxisAlignment::MAIN_AXIS_ALIGNMENT_CENTER); views::BoxLayout::MainAxisAlignment::MAIN_AXIS_ALIGNMENT_CENTER);
// Container. // Container.
AssistantOptInContainer* container = new AssistantOptInContainer(); AssistantOptInContainer* container =
new AssistantOptInContainer(/*listener=*/this);
layout_manager = layout_manager =
container->SetLayoutManager(std::make_unique<views::BoxLayout>( container->SetLayoutManager(std::make_unique<views::BoxLayout>(
......
...@@ -27,7 +27,7 @@ class AssistantOptInDelegate { ...@@ -27,7 +27,7 @@ class AssistantOptInDelegate {
// AssistantOptInView ---------------------------------------------------------- // AssistantOptInView ----------------------------------------------------------
class AssistantOptInView : public views::Button, public views::ButtonListener { class AssistantOptInView : public views::View, public views::ButtonListener {
public: public:
AssistantOptInView(); AssistantOptInView();
~AssistantOptInView() override; ~AssistantOptInView() override;
......
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