blob: e888bab5cc4919518266d9ede477f861d64a6cc6 [file] [log] [blame]
David Black71ab74d2018-04-12 06:52:311// Copyright 2018 The Chromium Authors. All rights reserved.
David Blacke8f815042018-03-28 18:59:372// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
David Black00fdce32018-05-10 20:59:145#include "ash/assistant/assistant_controller.h"
David Blacke8f815042018-03-28 18:59:376
Meilin Wang49800222018-08-22 17:27:487#include <algorithm>
8#include <utility>
9
David Black6b4b1ad22018-08-21 02:45:0810#include "ash/assistant/assistant_cache_controller.h"
David Blacke802d4062018-06-29 01:59:0511#include "ash/assistant/assistant_controller_observer.h"
David Black9766ecd2018-06-20 18:29:1112#include "ash/assistant/assistant_interaction_controller.h"
wutao16db603c2018-07-10 06:43:5913#include "ash/assistant/assistant_notification_controller.h"
David Blackc22e4352018-07-11 21:29:4814#include "ash/assistant/assistant_screen_context_controller.h"
David Blackee6f32b52018-08-09 21:28:4615#include "ash/assistant/assistant_setup_controller.h"
David Black1d1b1b672018-06-20 21:41:5716#include "ash/assistant/assistant_ui_controller.h"
David Blackedaf0c32018-06-28 22:06:3917#include "ash/assistant/util/deep_link_util.h"
David Black2f7a70c32018-06-26 02:14:2218#include "ash/new_window_controller.h"
David Blacke1a4b9ed2018-04-19 19:55:0819#include "ash/session/session_controller.h"
David Blacke8f815042018-03-28 18:59:3720#include "ash/shell.h"
David Blackd08c0512018-08-21 17:18:2621#include "ash/utility/screenshot_controller.h"
Yue Libf33defd2018-08-01 21:04:0622#include "ash/voice_interaction/voice_interaction_controller.h"
Muyuan Li0070f44d2018-05-16 02:57:2323#include "base/bind.h"
24#include "base/memory/scoped_refptr.h"
David Blacke1a4b9ed2018-04-19 19:55:0825#include "base/unguessable_token.h"
David Blacke8f815042018-03-28 18:59:3726
27namespace ash {
28
David Black00fdce32018-05-10 20:59:1429AssistantController::AssistantController()
Muyuan Lied7c76402018-08-07 23:59:0030 : assistant_volume_control_binding_(this),
David Black02974f82018-08-21 22:56:0531 assistant_cache_controller_(
32 std::make_unique<AssistantCacheController>(this)),
Muyuan Lied7c76402018-08-07 23:59:0033 assistant_interaction_controller_(
David Black2f7a70c32018-06-26 02:14:2234 std::make_unique<AssistantInteractionController>(this)),
wutao16db603c2018-07-10 06:43:5935 assistant_notification_controller_(
36 std::make_unique<AssistantNotificationController>(this)),
David Blackc22e4352018-07-11 21:29:4837 assistant_screen_context_controller_(
David Black1f58bcc2018-07-12 17:59:4038 std::make_unique<AssistantScreenContextController>(this)),
David Blackee6f32b52018-08-09 21:28:4639 assistant_setup_controller_(
40 std::make_unique<AssistantSetupController>(this)),
David Blackc22e4352018-07-11 21:29:4841 assistant_ui_controller_(std::make_unique<AssistantUiController>(this)),
Yue Libf33defd2018-08-01 21:04:0642 voice_interaction_binding_(this),
wutao16db603c2018-07-10 06:43:5943 weak_factory_(this) {
Yue Libf33defd2018-08-01 21:04:0644 mojom::VoiceInteractionObserverPtr ptr;
45 voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
46 Shell::Get()->voice_interaction_controller()->AddObserver(std::move(ptr));
47
David Blackf946f832018-07-13 17:29:0948 AddObserver(this);
David Black1f58bcc2018-07-12 17:59:4049 NotifyConstructed();
Muyuan Lied7c76402018-08-07 23:59:0050 chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
David Black4703d7b2018-06-22 00:16:3951}
David Blacke8f815042018-03-28 18:59:3752
David Black4703d7b2018-06-22 00:16:3953AssistantController::~AssistantController() {
Muyuan Lied7c76402018-08-07 23:59:0054 chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
David Black1f58bcc2018-07-12 17:59:4055 NotifyDestroying();
David Blackf946f832018-07-13 17:29:0956 RemoveObserver(this);
David Black4703d7b2018-06-22 00:16:3957}
David Blacke8f815042018-03-28 18:59:3758
David Black00fdce32018-05-10 20:59:1459void AssistantController::BindRequest(
60 mojom::AssistantControllerRequest request) {
David Black75f9fec2018-05-17 22:10:3961 assistant_controller_bindings_.AddBinding(this, std::move(request));
David Blacke8f815042018-03-28 18:59:3762}
63
Muyuan Lied7c76402018-08-07 23:59:0064void AssistantController::BindRequest(
65 mojom::AssistantVolumeControlRequest request) {
66 assistant_volume_control_binding_.Bind(std::move(request));
67}
68
David Blacke802d4062018-06-29 01:59:0569void AssistantController::AddObserver(AssistantControllerObserver* observer) {
70 observers_.AddObserver(observer);
71}
72
73void AssistantController::RemoveObserver(
74 AssistantControllerObserver* observer) {
75 observers_.RemoveObserver(observer);
76}
77
David Black00fdce32018-05-10 20:59:1478void AssistantController::SetAssistant(
David Blacke8f815042018-03-28 18:59:3779 chromeos::assistant::mojom::AssistantPtr assistant) {
David Black4c3656c2018-04-19 17:31:4780 assistant_ = std::move(assistant);
81
David Black4703d7b2018-06-22 00:16:3982 // Provide reference to sub-controllers.
David Black9766ecd2018-06-20 18:29:1183 assistant_interaction_controller_->SetAssistant(assistant_.get());
wutao16db603c2018-07-10 06:43:5984 assistant_notification_controller_->SetAssistant(assistant_.get());
David Blackc22e4352018-07-11 21:29:4885 assistant_screen_context_controller_->SetAssistant(assistant_.get());
86 assistant_ui_controller_->SetAssistant(assistant_.get());
David Black4c3656c2018-04-19 17:31:4787}
88
David Black7ffd2842018-05-25 01:25:5389void AssistantController::SetAssistantImageDownloader(
90 mojom::AssistantImageDownloaderPtr assistant_image_downloader) {
91 assistant_image_downloader_ = std::move(assistant_image_downloader);
92}
93
David Blackee6f32b52018-08-09 21:28:4694// TODO(dmblack): Call SetAssistantSetup directly on AssistantSetupController
95// instead of going through AssistantController.
Yue Libf89f72a2018-06-18 16:51:1696void AssistantController::SetAssistantSetup(
97 mojom::AssistantSetupPtr assistant_setup) {
98 assistant_setup_ = std::move(assistant_setup);
David Blackee6f32b52018-08-09 21:28:4699 assistant_setup_controller_->SetAssistantSetup(assistant_setup_.get());
David Black9766ecd2018-06-20 18:29:11100}
101
102void AssistantController::SetWebContentsManager(
103 mojom::WebContentsManagerPtr web_contents_manager) {
104 web_contents_manager_ = std::move(web_contents_manager);
Yue Libf89f72a2018-06-18 16:51:16105}
106
David Blackc22e4352018-07-11 21:29:48107// TODO(dmblack): Expose AssistantScreenContextController over mojo rather
108// than implementing RequestScreenshot here in AssistantController.
Muyuan Li0070f44d2018-05-16 02:57:23109void AssistantController::RequestScreenshot(
110 const gfx::Rect& rect,
111 RequestScreenshotCallback callback) {
David Blackc22e4352018-07-11 21:29:48112 assistant_screen_context_controller_->RequestScreenshot(rect,
113 std::move(callback));
Muyuan Li0070f44d2018-05-16 02:57:23114}
115
Meilin Wang49800222018-08-22 17:27:48116void AssistantController::OpenAssistantSettings() {
117 // Launch Assistant settings via deeplink.
118 OpenUrl(assistant::util::CreateAssistantSettingsDeepLink());
119}
120
David Black5326a8eb2018-06-12 22:05:34121void AssistantController::ManageWebContents(
David Blacke1a4b9ed2018-04-19 19:55:08122 const base::UnguessableToken& id_token,
David Black5326a8eb2018-06-12 22:05:34123 mojom::ManagedWebContentsParamsPtr params,
124 mojom::WebContentsManager::ManageWebContentsCallback callback) {
125 DCHECK(web_contents_manager_);
David Blacke1a4b9ed2018-04-19 19:55:08126
127 const mojom::UserSession* user_session =
128 Shell::Get()->session_controller()->GetUserSession(0);
129
130 if (!user_session) {
131 LOG(WARNING) << "Unable to retrieve active user session.";
David Black5326a8eb2018-06-12 22:05:34132 std::move(callback).Run(base::nullopt);
David Blacke1a4b9ed2018-04-19 19:55:08133 return;
134 }
135
David Black5326a8eb2018-06-12 22:05:34136 // Supply account ID.
137 params->account_id = user_session->user_info->account_id;
138
139 // Specify that we will handle top level browser requests.
David Black5326a8eb2018-06-12 22:05:34140 ash::mojom::ManagedWebContentsOpenUrlDelegatePtr ptr;
David Black2f7a70c32018-06-26 02:14:22141 web_contents_open_url_delegate_bindings_.AddBinding(this,
142 mojo::MakeRequest(&ptr));
David Black5326a8eb2018-06-12 22:05:34143 params->open_url_delegate_ptr_info = ptr.PassInterface();
144
145 web_contents_manager_->ManageWebContents(id_token, std::move(params),
146 std::move(callback));
David Blacke1a4b9ed2018-04-19 19:55:08147}
148
David Black5326a8eb2018-06-12 22:05:34149void AssistantController::ReleaseWebContents(
150 const base::UnguessableToken& id_token) {
151 web_contents_manager_->ReleaseWebContents(id_token);
David Blacke1a4b9ed2018-04-19 19:55:08152}
153
David Black5326a8eb2018-06-12 22:05:34154void AssistantController::ReleaseWebContents(
David Black3bfd968602018-04-24 22:07:46155 const std::vector<base::UnguessableToken>& id_tokens) {
David Black5326a8eb2018-06-12 22:05:34156 web_contents_manager_->ReleaseAllWebContents(id_tokens);
David Black3bfd968602018-04-24 22:07:46157}
158
David Black1a42f162018-08-13 23:07:01159void AssistantController::NavigateWebContentsBack(
160 const base::UnguessableToken& id_token,
161 mojom::WebContentsManager::NavigateWebContentsBackCallback callback) {
162 web_contents_manager_->NavigateWebContentsBack(id_token, std::move(callback));
163}
164
David Black7ffd2842018-05-25 01:25:53165void AssistantController::DownloadImage(
166 const GURL& url,
167 mojom::AssistantImageDownloader::DownloadCallback callback) {
168 DCHECK(assistant_image_downloader_);
169
170 const mojom::UserSession* user_session =
171 Shell::Get()->session_controller()->GetUserSession(0);
172
173 if (!user_session) {
174 LOG(WARNING) << "Unable to retrieve active user session.";
David Black5326a8eb2018-06-12 22:05:34175 std::move(callback).Run(gfx::ImageSkia());
David Black7ffd2842018-05-25 01:25:53176 return;
177 }
178
179 AccountId account_id = user_session->user_info->account_id;
180 assistant_image_downloader_->Download(account_id, url, std::move(callback));
181}
182
David Blackb0749ca52018-07-13 21:30:02183void AssistantController::OnDeepLinkReceived(
184 assistant::util::DeepLinkType type,
185 const std::map<std::string, std::string>& params) {
Meilin Wang49800222018-08-22 17:27:48186 using assistant::util::DeepLinkType;
David Black7c07e30e2018-07-25 02:27:19187
David Blackb0749ca52018-07-13 21:30:02188 switch (type) {
David Black7c07e30e2018-07-25 02:27:19189 case DeepLinkType::kFeedback:
David Blackb0749ca52018-07-13 21:30:02190 // TODO(dmblack): Possibly use a new FeedbackSource (this method defaults
191 // to kFeedbackSourceAsh). This may be useful for differentiating feedback
192 // UI and behavior for Assistant.
193 Shell::Get()->new_window_controller()->OpenFeedbackPage();
194 break;
David Blackd08c0512018-08-21 17:18:26195 case DeepLinkType::kScreenshot:
196 // TODO(dmblack): The user probably doesn't want their screenshot to
197 // include Assistant so we may want to hide it before calling this API.
198 // Unfortunately, hiding our UI immediately beforehand doesn't resolve the
199 // issue, so we may need to introduce a delay or visibility change
200 // callback to remedy this. For now, keeping Assistant UI open since it
201 // will be included in the screenshot anyway.
202 Shell::Get()->screenshot_controller()->TakeScreenshotForAllRootWindows();
203 break;
David Black7c07e30e2018-07-25 02:27:19204 case DeepLinkType::kUnsupported:
David Blackee6f32b52018-08-09 21:28:46205 case DeepLinkType::kOnboarding:
David Black7c07e30e2018-07-25 02:27:19206 case DeepLinkType::kQuery:
207 case DeepLinkType::kReminders:
208 case DeepLinkType::kSettings:
David Blackd08c0512018-08-21 17:18:26209 case DeepLinkType::kWhatsOnMyScreen:
David Blackb0749ca52018-07-13 21:30:02210 // No action needed.
211 break;
David Black1859f9f2018-07-13 18:03:34212 }
David Blackf946f832018-07-13 17:29:09213}
214
David Black304cbdf2018-08-26 21:39:32215void AssistantController::ShouldOpenUrlFromTab(
216 const GURL& url,
David Blackf360d0b2018-09-18 19:23:15217 WindowOpenDisposition disposition,
David Black304cbdf2018-08-26 21:39:32218 ash::mojom::ManagedWebContentsOpenUrlDelegate::ShouldOpenUrlFromTabCallback
219 callback) {
220 // We always handle deep links ourselves.
221 if (assistant::util::IsDeepLinkUrl(url)) {
222 std::move(callback).Run(/*should_open=*/false);
223 NotifyDeepLinkReceived(url);
224 return;
225 }
226
David Blackf360d0b2018-09-18 19:23:15227 AssistantUiMode ui_mode = assistant_ui_controller_->model()->ui_mode();
228
David Black304cbdf2018-08-26 21:39:32229 // When in main UI mode, WebContents should not navigate as they are hosting
David Blackf360d0b2018-09-18 19:23:15230 // Assistant cards. Instead, we route navigation attempts to the browser. We
231 // also respect open |disposition| to launch in the browser if appropriate.
232 if (ui_mode == AssistantUiMode::kMainUi ||
233 disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
David Black304cbdf2018-08-26 21:39:32234 std::move(callback).Run(/*should_open=*/false);
235 OpenUrl(url);
236 return;
237 }
238
239 // In all other cases WebContents should be able to navigate freely.
240 std::move(callback).Run(/*should_open=*/true);
David Black2f7a70c32018-06-26 02:14:22241}
242
Muyuan Lied7c76402018-08-07 23:59:00243void AssistantController::SetVolume(int volume, bool user_initiated) {
244 volume = std::min(100, volume);
245 volume = std::max(volume, 0);
246 chromeos::CrasAudioHandler::Get()->SetOutputVolumePercent(volume);
247}
248
249void AssistantController::SetMuted(bool muted) {
250 chromeos::CrasAudioHandler::Get()->SetOutputMute(muted);
251}
252
253void AssistantController::AddVolumeObserver(mojom::VolumeObserverPtr observer) {
254 volume_observer_.AddPtr(std::move(observer));
255
256 int output_volume =
257 chromeos::CrasAudioHandler::Get()->GetOutputVolumePercent();
258 bool mute = chromeos::CrasAudioHandler::Get()->IsOutputMuted();
259 OnOutputMuteChanged(mute, false /* system_adjust */);
260 OnOutputNodeVolumeChanged(0 /* node */, output_volume);
261}
262
263void AssistantController::OnOutputMuteChanged(bool mute_on,
264 bool system_adjust) {
265 volume_observer_.ForAllPtrs([mute_on](mojom::VolumeObserver* observer) {
266 observer->OnMuteStateChanged(mute_on);
267 });
268}
269
270void AssistantController::OnOutputNodeVolumeChanged(uint64_t node, int volume) {
271 // |node| refers to the active volume device, which we don't care here.
272 volume_observer_.ForAllPtrs([volume](mojom::VolumeObserver* observer) {
273 observer->OnVolumeChanged(volume);
274 });
275}
276
David Black2f7a70c32018-06-26 02:14:22277void AssistantController::OpenUrl(const GURL& url) {
David Blackedaf0c32018-06-28 22:06:39278 if (assistant::util::IsDeepLinkUrl(url)) {
David Black1f58bcc2018-07-12 17:59:40279 NotifyDeepLinkReceived(url);
David Blackedaf0c32018-06-28 22:06:39280 return;
281 }
David Black2f7a70c32018-06-26 02:14:22282
David Blackedaf0c32018-06-28 22:06:39283 Shell::Get()->new_window_controller()->NewTabWithUrl(url);
David Black1f58bcc2018-07-12 17:59:40284 NotifyUrlOpened(url);
285}
David Blacke802d4062018-06-29 01:59:05286
David Black1f58bcc2018-07-12 17:59:40287void AssistantController::NotifyConstructed() {
288 for (AssistantControllerObserver& observer : observers_)
289 observer.OnAssistantControllerConstructed();
290}
291
292void AssistantController::NotifyDestroying() {
293 for (AssistantControllerObserver& observer : observers_)
294 observer.OnAssistantControllerDestroying();
295}
296
297void AssistantController::NotifyDeepLinkReceived(const GURL& deep_link) {
Meilin Wang49800222018-08-22 17:27:48298 using assistant::util::DeepLinkType;
David Blackb0749ca52018-07-13 21:30:02299
300 // Retrieve deep link type and parsed parameters.
Meilin Wang49800222018-08-22 17:27:48301 DeepLinkType type = assistant::util::GetDeepLinkType(deep_link);
David Blackb0749ca52018-07-13 21:30:02302 const std::map<std::string, std::string> params =
Meilin Wang49800222018-08-22 17:27:48303 assistant::util::GetDeepLinkParams(deep_link);
David Blackb0749ca52018-07-13 21:30:02304
David Black1f58bcc2018-07-12 17:59:40305 for (AssistantControllerObserver& observer : observers_)
David Blackb0749ca52018-07-13 21:30:02306 observer.OnDeepLinkReceived(type, params);
David Black1f58bcc2018-07-12 17:59:40307}
308
309void AssistantController::NotifyUrlOpened(const GURL& url) {
David Blacke802d4062018-06-29 01:59:05310 for (AssistantControllerObserver& observer : observers_)
311 observer.OnUrlOpened(url);
David Black2f7a70c32018-06-26 02:14:22312}
313
Yue Libf33defd2018-08-01 21:04:06314void AssistantController::OnVoiceInteractionStatusChanged(
315 mojom::VoiceInteractionState state) {
Xiaohui Chen05715212018-08-08 15:52:28316 if (state == mojom::VoiceInteractionState::NOT_READY)
Yue Libf33defd2018-08-01 21:04:06317 assistant_ui_controller_->HideUi(AssistantSource::kUnspecified);
318}
319
wutao16db603c2018-07-10 06:43:59320base::WeakPtr<AssistantController> AssistantController::GetWeakPtr() {
321 return weak_factory_.GetWeakPtr();
322}
323
David Blacke8f815042018-03-28 18:59:37324} // namespace ash