1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/assistant/ui/dialog_plate/mic_view.h"
6 
7 #include <memory>
8 
9 #include "ash/assistant/model/assistant_interaction_model.h"
10 #include "ash/assistant/model/assistant_ui_model.h"
11 #include "ash/assistant/ui/logo_view/logo_view.h"
12 #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
13 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
14 #include "ui/views/layout/box_layout.h"
15 #include "ui/views/layout/fill_layout.h"
16 
17 namespace ash {
18 
19 namespace {
20 
21 // Appearance.
22 // The desired height for the LogoView icon is 24dip in mic state to match the
23 // static mic button in DialogPlate. The |kMicIcon| resource used for the static
24 // button has different internal padding than does that of the icon drawn by
25 // LogoView, so we add 2dip for visual consistency.
26 constexpr int kIconSizeDip = 26;
27 constexpr int kPreferredSizeDip = 32;
28 
29 }  // namespace
30 
MicView(AssistantButtonListener * listener,AssistantButtonId button_id)31 MicView::MicView(AssistantButtonListener* listener, AssistantButtonId button_id)
32     : AssistantButton(listener, button_id) {
33   InitLayout();
34 
35   assistant_controller_observer_.Add(AssistantController::Get());
36   AssistantInteractionController::Get()->GetModel()->AddObserver(this);
37 }
38 
~MicView()39 MicView::~MicView() {
40   if (AssistantInteractionController::Get())
41     AssistantInteractionController::Get()->GetModel()->RemoveObserver(this);
42 }
43 
GetClassName() const44 const char* MicView::GetClassName() const {
45   return "MicView";
46 }
47 
CalculatePreferredSize() const48 gfx::Size MicView::CalculatePreferredSize() const {
49   return gfx::Size(kPreferredSizeDip, GetHeightForWidth(kPreferredSizeDip));
50 }
51 
GetHeightForWidth(int width) const52 int MicView::GetHeightForWidth(int width) const {
53   return kPreferredSizeDip;
54 }
55 
OnAssistantControllerDestroying()56 void MicView::OnAssistantControllerDestroying() {
57   AssistantInteractionController::Get()->GetModel()->RemoveObserver(this);
58   assistant_controller_observer_.Remove(AssistantController::Get());
59 }
60 
OnMicStateChanged(MicState mic_state)61 void MicView::OnMicStateChanged(MicState mic_state) {
62   is_user_speaking_ = false;
63   UpdateState(/*animate=*/true);
64 }
65 
OnSpeechLevelChanged(float speech_level_db)66 void MicView::OnSpeechLevelChanged(float speech_level_db) {
67   // TODO: Work with UX to determine the threshold.
68   constexpr float kSpeechLevelThreshold = -60.0f;
69   if (speech_level_db < kSpeechLevelThreshold)
70     return;
71 
72   logo_view_->SetSpeechLevel(speech_level_db);
73   if (!is_user_speaking_) {
74     is_user_speaking_ = true;
75     UpdateState(/*animate=*/true);
76   }
77 }
78 
InitLayout()79 void MicView::InitLayout() {
80   SetLayoutManager(std::make_unique<views::FillLayout>());
81 
82   // Logo view container.
83   auto logo_view_container = std::make_unique<views::View>();
84   logo_view_container->SetCanProcessEventsWithinSubtree(false);
85 
86   views::BoxLayout* layout_manager =
87       logo_view_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
88           views::BoxLayout::Orientation::kVertical));
89 
90   layout_manager->set_cross_axis_alignment(
91       views::BoxLayout::CrossAxisAlignment::kCenter);
92 
93   layout_manager->set_main_axis_alignment(
94       views::BoxLayout::MainAxisAlignment::kCenter);
95 
96   // Logo view.
97   logo_view_ = logo_view_container->AddChildView(LogoView::Create());
98   logo_view_->SetPreferredSize(gfx::Size(kIconSizeDip, kIconSizeDip));
99 
100   AddChildView(std::move(logo_view_container));
101 
102   // Initialize state.
103   UpdateState(/*animate=*/false);
104 }
105 
UpdateState(bool animate)106 void MicView::UpdateState(bool animate) {
107   const AssistantInteractionModel* interaction_model =
108       AssistantInteractionController::Get()->GetModel();
109 
110   if (animate) {
111     // If Assistant UI is not visible, we shouldn't attempt to animate state
112     // changes. We should instead advance immediately to the next state.
113     animate = AssistantUiController::Get()->GetModel()->visibility() ==
114               AssistantVisibility::kVisible;
115   }
116 
117   LogoView::State mic_state;
118   switch (interaction_model->mic_state()) {
119     case MicState::kClosed:
120       mic_state = LogoView::State::kMic;
121       break;
122     case MicState::kOpen:
123       mic_state = is_user_speaking_ ? LogoView::State::kUserSpeaks
124                                     : LogoView::State::kListening;
125       break;
126   }
127   logo_view_->SetState(mic_state, animate);
128 }
129 
130 }  // namespace ash
131