David Black | 71ab74d | 2018-04-12 06:52:31 | [diff] [blame] | 1 | // Copyright 2018 The Chromium Authors. All rights reserved. |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 5 | #include "ash/assistant/assistant_controller.h" |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 6 | |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 7 | #include "ash/assistant/model/assistant_interaction_model_observer.h" |
| 8 | #include "ash/assistant/model/assistant_ui_element.h" |
David Black | f7f8d201 | 2018-04-25 19:48:02 | [diff] [blame] | 9 | #include "ash/assistant/ui/assistant_bubble.h" |
David Black | e1a4b9ed | 2018-04-19 19:55:08 | [diff] [blame] | 10 | #include "ash/session/session_controller.h" |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 11 | #include "ash/shell.h" |
| 12 | #include "ash/shell_delegate.h" |
David Black | e1a4b9ed | 2018-04-19 19:55:08 | [diff] [blame] | 13 | #include "base/unguessable_token.h" |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 14 | |
| 15 | namespace ash { |
| 16 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 17 | AssistantController::AssistantController() |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 18 | : assistant_controller_binding_(this), |
David Black | f7f8d201 | 2018-04-25 19:48:02 | [diff] [blame] | 19 | assistant_event_subscriber_binding_(this), |
| 20 | assistant_bubble_(std::make_unique<AssistantBubble>(this)) { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 21 | AddInteractionModelObserver(this); |
Qiang Xu | bd6d84e7 | 2018-05-09 01:31:51 | [diff] [blame] | 22 | Shell::Get()->highlighter_controller()->AddObserver(this); |
David Black | 71ab74d | 2018-04-12 06:52:31 | [diff] [blame] | 23 | } |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 24 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 25 | AssistantController::~AssistantController() { |
Qiang Xu | bd6d84e7 | 2018-05-09 01:31:51 | [diff] [blame] | 26 | Shell::Get()->highlighter_controller()->RemoveObserver(this); |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 27 | RemoveInteractionModelObserver(this); |
| 28 | |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 29 | assistant_controller_binding_.Close(); |
| 30 | assistant_event_subscriber_binding_.Close(); |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 31 | } |
| 32 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 33 | void AssistantController::BindRequest( |
| 34 | mojom::AssistantControllerRequest request) { |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 35 | assistant_controller_binding_.Bind(std::move(request)); |
| 36 | } |
| 37 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 38 | void AssistantController::SetAssistant( |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 39 | chromeos::assistant::mojom::AssistantPtr assistant) { |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 40 | assistant_ = std::move(assistant); |
| 41 | |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 42 | // Subscribe to Assistant events. |
| 43 | chromeos::assistant::mojom::AssistantEventSubscriberPtr ptr; |
| 44 | assistant_event_subscriber_binding_.Bind(mojo::MakeRequest(&ptr)); |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 45 | assistant_->AddAssistantEventSubscriber(std::move(ptr)); |
| 46 | } |
| 47 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 48 | void AssistantController::SetAssistantCardRenderer( |
David Black | e1a4b9ed | 2018-04-19 19:55:08 | [diff] [blame] | 49 | mojom::AssistantCardRendererPtr assistant_card_renderer) { |
| 50 | assistant_card_renderer_ = std::move(assistant_card_renderer); |
| 51 | } |
| 52 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 53 | void AssistantController::RenderCard( |
David Black | e1a4b9ed | 2018-04-19 19:55:08 | [diff] [blame] | 54 | const base::UnguessableToken& id_token, |
| 55 | mojom::AssistantCardParamsPtr params, |
| 56 | mojom::AssistantCardRenderer::RenderCallback callback) { |
| 57 | DCHECK(assistant_card_renderer_); |
| 58 | |
| 59 | const mojom::UserSession* user_session = |
| 60 | Shell::Get()->session_controller()->GetUserSession(0); |
| 61 | |
| 62 | if (!user_session) { |
| 63 | LOG(WARNING) << "Unable to retrieve active user session."; |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | AccountId account_id = user_session->user_info->account_id; |
| 68 | |
| 69 | assistant_card_renderer_->Render(account_id, id_token, std::move(params), |
| 70 | std::move(callback)); |
| 71 | } |
| 72 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 73 | void AssistantController::ReleaseCard(const base::UnguessableToken& id_token) { |
David Black | e1a4b9ed | 2018-04-19 19:55:08 | [diff] [blame] | 74 | DCHECK(assistant_card_renderer_); |
| 75 | assistant_card_renderer_->Release(id_token); |
| 76 | } |
| 77 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 78 | void AssistantController::ReleaseCards( |
David Black | 3bfd96860 | 2018-04-24 22:07:46 | [diff] [blame] | 79 | const std::vector<base::UnguessableToken>& id_tokens) { |
| 80 | DCHECK(assistant_card_renderer_); |
| 81 | assistant_card_renderer_->ReleaseAll(id_tokens); |
| 82 | } |
| 83 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 84 | void AssistantController::AddInteractionModelObserver( |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 85 | AssistantInteractionModelObserver* observer) { |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 86 | assistant_interaction_model_.AddObserver(observer); |
| 87 | } |
| 88 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 89 | void AssistantController::RemoveInteractionModelObserver( |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 90 | AssistantInteractionModelObserver* observer) { |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 91 | assistant_interaction_model_.RemoveObserver(observer); |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 92 | } |
| 93 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 94 | void AssistantController::StartInteraction() { |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 95 | // TODO(dmblack): Instruct underlying service to start listening if current |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 96 | // input modality is VOICE. |
| 97 | if (assistant_interaction_model_.interaction_state() == |
| 98 | InteractionState::kInactive) { |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 99 | OnInteractionStarted(); |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 100 | } |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 101 | } |
| 102 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 103 | void AssistantController::StopInteraction() { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 104 | if (assistant_interaction_model_.interaction_state() != |
| 105 | InteractionState::kInactive) { |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 106 | OnInteractionDismissed(); |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 107 | } |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 108 | } |
| 109 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 110 | void AssistantController::ToggleInteraction() { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 111 | if (assistant_interaction_model_.interaction_state() == |
| 112 | InteractionState::kInactive) { |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 113 | StartInteraction(); |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 114 | } else { |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 115 | StopInteraction(); |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 119 | void AssistantController::OnInteractionStateChanged( |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 120 | InteractionState interaction_state) { |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 121 | if (interaction_state == InteractionState::kInactive) { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 122 | assistant_interaction_model_.ClearInteraction(); |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 123 | |
| 124 | // TODO(dmblack): Input modality should default back to the user's |
| 125 | // preferred input modality. |
| 126 | assistant_interaction_model_.SetInputModality(InputModality::kVoice); |
| 127 | } |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 128 | } |
| 129 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 130 | void AssistantController::OnHighlighterEnabledChanged(bool enabled) { |
Qiang Xu | bd6d84e7 | 2018-05-09 01:31:51 | [diff] [blame] | 131 | // TODO(warx): add a reason enum to distinguish the case of deselecting the |
| 132 | // tool and done with a stylus selection. |
Qiang Xu | bd6d84e7 | 2018-05-09 01:31:51 | [diff] [blame] | 133 | assistant_interaction_model_.SetInputModality(InputModality::kStylus); |
| 134 | assistant_interaction_model_.SetInteractionState( |
| 135 | enabled ? InteractionState::kActive : InteractionState::kInactive); |
| 136 | } |
| 137 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 138 | void AssistantController::OnInteractionStarted() { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 139 | assistant_interaction_model_.SetInteractionState(InteractionState::kActive); |
David Black | 4d28a35 | 2018-04-15 22:00:14 | [diff] [blame] | 140 | } |
| 141 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 142 | void AssistantController::OnInteractionFinished( |
| 143 | chromeos::assistant::mojom::AssistantInteractionResolution resolution) {} |
David Black | f7f8d201 | 2018-04-25 19:48:02 | [diff] [blame] | 144 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 145 | void AssistantController::OnInteractionDismissed() { |
David Black | 5318ec1 | 2018-05-02 01:54:32 | [diff] [blame] | 146 | assistant_interaction_model_.SetInteractionState(InteractionState::kInactive); |
David Black | 1d7c203 | 2018-05-09 18:01:02 | [diff] [blame] | 147 | |
| 148 | // When the user-facing interaction is dismissed, we instruct the service to |
| 149 | // terminate any listening, speaking, or query in flight. |
| 150 | DCHECK(assistant_); |
| 151 | assistant_->StopActiveInteraction(); |
David Black | 4d28a35 | 2018-04-15 22:00:14 | [diff] [blame] | 152 | } |
| 153 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 154 | void AssistantController::OnDialogPlateContentsChanged( |
David Black | b2df49c | 2018-05-03 23:52:36 | [diff] [blame] | 155 | const std::string& text) { |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 156 | if (text.empty()) { |
| 157 | // Note: This does not open the mic. It only updates the input modality to |
| 158 | // voice so that we will show the mic icon in the UI. |
| 159 | assistant_interaction_model_.SetInputModality(InputModality::kVoice); |
| 160 | } else { |
David Black | 1d7c203 | 2018-05-09 18:01:02 | [diff] [blame] | 161 | // If switching to keyboard modality from voice, we instruct the service to |
| 162 | // terminate any listening, speaking, or query in flight. |
| 163 | // TODO(dmblack): We probably want this same logic when switching to any |
| 164 | // modality from voice. Handle this instead in OnInputModalityChanged, but |
| 165 | // we will need to add a previous modality parameter to that API. |
| 166 | if (assistant_interaction_model_.input_modality() == |
| 167 | InputModality::kVoice) { |
| 168 | DCHECK(assistant_); |
| 169 | assistant_->StopActiveInteraction(); |
| 170 | } |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 171 | assistant_interaction_model_.SetInputModality(InputModality::kKeyboard); |
| 172 | assistant_interaction_model_.SetMicState(MicState::kClosed); |
| 173 | } |
David Black | b2df49c | 2018-05-03 23:52:36 | [diff] [blame] | 174 | } |
| 175 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 176 | void AssistantController::OnDialogPlateContentsCommitted( |
David Black | b2df49c | 2018-05-03 23:52:36 | [diff] [blame] | 177 | const std::string& text) { |
| 178 | Query query; |
| 179 | query.high_confidence_text = text; |
| 180 | |
| 181 | assistant_interaction_model_.ClearInteraction(); |
| 182 | assistant_interaction_model_.SetQuery(query); |
| 183 | |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 184 | // Note: This does not open the mic. It only updates the input modality to |
| 185 | // voice so that we will show the mic icon in the UI. |
| 186 | assistant_interaction_model_.SetInputModality(InputModality::kVoice); |
| 187 | |
David Black | b2df49c | 2018-05-03 23:52:36 | [diff] [blame] | 188 | DCHECK(assistant_); |
| 189 | assistant_->SendTextQuery(text); |
| 190 | } |
| 191 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 192 | void AssistantController::OnHtmlResponse(const std::string& response) { |
David Black | 3bfd96860 | 2018-04-24 22:07:46 | [diff] [blame] | 193 | assistant_interaction_model_.AddUiElement( |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 194 | std::make_unique<AssistantCardElement>(response)); |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 195 | } |
| 196 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 197 | void AssistantController::OnSuggestionChipPressed(const std::string& text) { |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 198 | Query query; |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 199 | query.high_confidence_text = text; |
| 200 | |
| 201 | assistant_interaction_model_.ClearInteraction(); |
| 202 | assistant_interaction_model_.SetQuery(query); |
| 203 | |
| 204 | DCHECK(assistant_); |
| 205 | assistant_->SendTextQuery(text); |
| 206 | } |
| 207 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 208 | void AssistantController::OnSuggestionsResponse( |
David Black | 46ad4b7 | 2018-04-03 00:34:02 | [diff] [blame] | 209 | const std::vector<std::string>& response) { |
David Black | 71ab74d | 2018-04-12 06:52:31 | [diff] [blame] | 210 | assistant_interaction_model_.AddSuggestions(response); |
David Black | 46ad4b7 | 2018-04-03 00:34:02 | [diff] [blame] | 211 | } |
| 212 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 213 | void AssistantController::OnTextResponse(const std::string& response) { |
David Black | 3bfd96860 | 2018-04-24 22:07:46 | [diff] [blame] | 214 | assistant_interaction_model_.AddUiElement( |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 215 | std::make_unique<AssistantTextElement>(response)); |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 216 | } |
| 217 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 218 | void AssistantController::OnSpeechRecognitionStarted() { |
David Black | 71ab74d | 2018-04-12 06:52:31 | [diff] [blame] | 219 | assistant_interaction_model_.ClearInteraction(); |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 220 | assistant_interaction_model_.SetInputModality(InputModality::kVoice); |
| 221 | assistant_interaction_model_.SetMicState(MicState::kOpen); |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 222 | } |
| 223 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 224 | void AssistantController::OnSpeechRecognitionIntermediateResult( |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 225 | const std::string& high_confidence_text, |
| 226 | const std::string& low_confidence_text) { |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 227 | assistant_interaction_model_.SetQuery( |
| 228 | {high_confidence_text, low_confidence_text}); |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 229 | } |
| 230 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 231 | void AssistantController::OnSpeechRecognitionEndOfUtterance() { |
David Black | 873ec755 | 2018-05-05 00:16:27 | [diff] [blame] | 232 | assistant_interaction_model_.SetMicState(MicState::kClosed); |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 233 | } |
| 234 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 235 | void AssistantController::OnSpeechRecognitionFinalResult( |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 236 | const std::string& final_result) { |
David Black | d72bae46 | 2018-04-28 00:47:14 | [diff] [blame] | 237 | Query query; |
David Black | 4c3656c | 2018-04-19 17:31:47 | [diff] [blame] | 238 | query.high_confidence_text = final_result; |
| 239 | assistant_interaction_model_.SetQuery(query); |
David Black | 89ac6f8 | 2018-04-03 17:38:12 | [diff] [blame] | 240 | } |
| 241 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 242 | void AssistantController::OnSpeechLevelUpdated(float speech_level) { |
Alan Lau | 7bd29ced | 2018-03-29 18:39:51 | [diff] [blame] | 243 | // TODO(dmblack): Handle. |
| 244 | NOTIMPLEMENTED(); |
| 245 | } |
| 246 | |
David Black | 00fdce3 | 2018-05-10 20:59:14 | [diff] [blame^] | 247 | void AssistantController::OnOpenUrlResponse(const GURL& url) { |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 248 | Shell::Get()->shell_delegate()->OpenUrlFromArc(url); |
David Black | 990b0ac | 2018-05-01 18:08:08 | [diff] [blame] | 249 | StopInteraction(); |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 250 | } |
| 251 | |
David Black | e8f81504 | 2018-03-28 18:59:37 | [diff] [blame] | 252 | } // namespace ash |