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/assistant_controller_impl.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "ash/accessibility/accessibility_controller_impl.h"
11 #include "ash/assistant/util/deep_link_util.h"
12 #include "ash/public/cpp/android_intent_helper.h"
13 #include "ash/public/cpp/ash_pref_names.h"
14 #include "ash/public/cpp/new_window_delegate.h"
15 #include "ash/public/mojom/assistant_volume_control.mojom.h"
16 #include "ash/session/session_controller_impl.h"
17 #include "ash/shell.h"
18 #include "ash/utility/screenshot_controller.h"
19 #include "base/bind.h"
20 #include "base/memory/scoped_refptr.h"
21 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
22 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
23 #include "chromeos/services/assistant/public/cpp/features.h"
24 #include "components/prefs/pref_registry_simple.h"
25 #include "net/traffic_annotation/network_traffic_annotation.h"
26 #include "url/gurl.h"
27
28 namespace ash {
29
AssistantControllerImpl()30 AssistantControllerImpl::AssistantControllerImpl() {
31 assistant_state_controller_.AddObserver(this);
32 chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
33 AddObserver(this);
34
35 // The Assistant service needs to have accessibility state synced with ash
36 // and be notified of any accessibility status changes in the future to
37 // provide an opportunity to turn on/off A11Y features.
38 Shell::Get()->accessibility_controller()->AddObserver(this);
39
40 NotifyConstructed();
41 }
42
~AssistantControllerImpl()43 AssistantControllerImpl::~AssistantControllerImpl() {
44 NotifyDestroying();
45
46 chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
47 Shell::Get()->accessibility_controller()->RemoveObserver(this);
48 assistant_state_controller_.RemoveObserver(this);
49 RemoveObserver(this);
50 }
51
52 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)53 void AssistantControllerImpl::RegisterProfilePrefs(
54 PrefRegistrySimple* registry) {
55 AssistantInteractionControllerImpl::RegisterProfilePrefs(registry);
56 AssistantUiControllerImpl::RegisterProfilePrefs(registry);
57 }
58
BindReceiver(mojo::PendingReceiver<mojom::AssistantVolumeControl> receiver)59 void AssistantControllerImpl::BindReceiver(
60 mojo::PendingReceiver<mojom::AssistantVolumeControl> receiver) {
61 assistant_volume_control_receiver_.Bind(std::move(receiver));
62 }
63
SetAssistant(chromeos::assistant::Assistant * assistant)64 void AssistantControllerImpl::SetAssistant(
65 chromeos::assistant::Assistant* assistant) {
66 assistant_ = assistant;
67
68 // Provide reference to sub-controllers.
69 assistant_alarm_timer_controller_.SetAssistant(assistant);
70 assistant_interaction_controller_.SetAssistant(assistant);
71 assistant_notification_controller_.SetAssistant(assistant);
72 assistant_screen_context_controller_.SetAssistant(assistant);
73 assistant_ui_controller_.SetAssistant(assistant);
74
75 OnAccessibilityStatusChanged();
76
77 if (assistant) {
78 for (AssistantControllerObserver& observer : observers_)
79 observer.OnAssistantReady();
80 }
81 }
82
SendAssistantFeedback(bool assistant_debug_info_allowed,const std::string & feedback_description,const std::string & screenshot_png)83 void AssistantControllerImpl::SendAssistantFeedback(
84 bool assistant_debug_info_allowed,
85 const std::string& feedback_description,
86 const std::string& screenshot_png) {
87 chromeos::assistant::AssistantFeedback assistant_feedback;
88 assistant_feedback.assistant_debug_info_allowed =
89 assistant_debug_info_allowed;
90 assistant_feedback.description = feedback_description;
91 assistant_feedback.screenshot_png = screenshot_png;
92 assistant_->SendAssistantFeedback(std::move(assistant_feedback));
93 }
94
StartSpeakerIdEnrollmentFlow()95 void AssistantControllerImpl::StartSpeakerIdEnrollmentFlow() {
96 setup_controller()->StartOnboarding(false /* relaunch */,
97 FlowType::kSpeakerIdEnrollment);
98 }
99
DownloadImage(const GURL & url,ImageDownloader::DownloadCallback callback)100 void AssistantControllerImpl::DownloadImage(
101 const GURL& url,
102 ImageDownloader::DownloadCallback callback) {
103 constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag =
104 net::DefineNetworkTrafficAnnotation("image_downloader", R"(
105 "semantics: {
106 sender: "Google Assistant"
107 description:
108 "The Google Assistant requires dynamic loading of images to "
109 "provide a media rich user experience. Images are downloaded "
110 "on an as needed basis."
111 trigger:
112 "Generally triggered in direct response to a user issued "
113 "query. A single query may necessitate the downloading of "
114 "multiple images."
115 destination: GOOGLE_OWNED_SERVICE
116 }
117 "policy": {
118 cookies_allowed: NO
119 setting:
120 "The Google Assistant can be enabled/disabled in Chrome "
121 "Settings and is subject to eligibility requirements."
122 })");
123
124 ImageDownloader::Get()->Download(url, kNetworkTrafficAnnotationTag,
125 std::move(callback));
126 }
127
128 void AssistantControllerImpl::AddObserver(
129 AssistantControllerObserver* observer) {
130 observers_.AddObserver(observer);
131 }
132
133 void AssistantControllerImpl::RemoveObserver(
134 AssistantControllerObserver* observer) {
135 observers_.RemoveObserver(observer);
136 }
137
138 void AssistantControllerImpl::OpenUrl(const GURL& url,
139 bool in_background,
140 bool from_server) {
141 if (assistant::util::IsDeepLinkUrl(url)) {
142 NotifyDeepLinkReceived(url);
143 return;
144 }
145
146 auto* android_helper = AndroidIntentHelper::GetInstance();
147 if (IsAndroidIntent(url) && !android_helper) {
148 NOTREACHED();
149 return;
150 }
151
152 // Give observers an opportunity to perform any necessary handling before we
153 // open the specified |url| in a new browser tab.
154 NotifyOpeningUrl(url, in_background, from_server);
155
156 if (IsAndroidIntent(url)) {
157 android_helper->LaunchAndroidIntent(url.spec());
158 } else {
159 // The new tab should be opened with a user activation since the user
160 // interacted with the Assistant to open the url. |in_background| describes
161 // the relationship between |url| and Assistant UI, not the browser. As
162 // such, the browser will always be instructed to open |url| in a new
163 // browser tab and Assistant UI state will be updated downstream to respect
164 // |in_background|.
165 NewWindowDelegate::GetInstance()->NewTabWithUrl(
166 url, /*from_user_interaction=*/true);
167 }
168 NotifyUrlOpened(url, from_server);
169 }
170
171 void AssistantControllerImpl::OpenAssistantSettings() {
172 // Launch Assistant settings via deeplink.
173 OpenUrl(assistant::util::CreateAssistantSettingsDeepLink(),
174 /*in_background=*/false, /*from_server=*/false);
175 }
176
177 base::WeakPtr<ash::AssistantController> AssistantControllerImpl::GetWeakPtr() {
178 return weak_factory_.GetWeakPtr();
179 }
180
181 void AssistantControllerImpl::OnDeepLinkReceived(
182 assistant::util::DeepLinkType type,
183 const std::map<std::string, std::string>& params) {
184 using assistant::util::DeepLinkParam;
185 using assistant::util::DeepLinkType;
186
187 switch (type) {
188 case DeepLinkType::kChromeSettings: {
189 // Chrome Settings deep links are opened in a new browser tab.
190 OpenUrl(
191 assistant::util::GetChromeSettingsUrl(
192 assistant::util::GetDeepLinkParam(params, DeepLinkParam::kPage)),
193 /*in_background=*/false, /*from_server=*/false);
194 break;
195 }
196 case DeepLinkType::kFeedback:
197 NewWindowDelegate::GetInstance()->OpenFeedbackPage(
198 /*from_assistant=*/true);
199
200 // Close the assistant UI so that the feedback page is visible.
201 assistant_ui_controller_.CloseUi(
202 chromeos::assistant::AssistantExitPoint::kUnspecified);
203 break;
204 case DeepLinkType::kScreenshot:
205 // We close the UI before taking the screenshot as it's probably not the
206 // user's intention to include the Assistant in the picture.
207 assistant_ui_controller_.CloseUi(
208 chromeos::assistant::AssistantExitPoint::kScreenshot);
209 Shell::Get()->screenshot_controller()->TakeScreenshotForAllRootWindows();
210 break;
211 case DeepLinkType::kTaskManager:
212 // Open task manager window.
213 NewWindowDelegate::GetInstance()->ShowTaskManager();
214 break;
215 case DeepLinkType::kUnsupported:
216 case DeepLinkType::kAlarmTimer:
217 case DeepLinkType::kLists:
218 case DeepLinkType::kNotes:
219 case DeepLinkType::kOnboarding:
220 case DeepLinkType::kProactiveSuggestions:
221 case DeepLinkType::kQuery:
222 case DeepLinkType::kReminders:
223 case DeepLinkType::kSettings:
224 case DeepLinkType::kWhatsOnMyScreen:
225 // No action needed.
226 break;
227 }
228 }
229
230 void AssistantControllerImpl::SetVolume(int volume, bool user_initiated) {
231 volume = std::min(100, volume);
232 volume = std::max(volume, 0);
233 chromeos::CrasAudioHandler::Get()->SetOutputVolumePercent(volume);
234 }
235
236 void AssistantControllerImpl::SetMuted(bool muted) {
237 chromeos::CrasAudioHandler::Get()->SetOutputMute(muted);
238 }
239
240 void AssistantControllerImpl::AddVolumeObserver(
241 mojo::PendingRemote<mojom::VolumeObserver> observer) {
242 volume_observers_.Add(std::move(observer));
243
244 int output_volume =
245 chromeos::CrasAudioHandler::Get()->GetOutputVolumePercent();
246 bool mute = chromeos::CrasAudioHandler::Get()->IsOutputMuted();
247 OnOutputMuteChanged(mute);
248 OnOutputNodeVolumeChanged(0 /* node */, output_volume);
249 }
250
251 void AssistantControllerImpl::OnOutputMuteChanged(bool mute_on) {
252 for (auto& observer : volume_observers_)
253 observer->OnMuteStateChanged(mute_on);
254 }
255
256 void AssistantControllerImpl::OnOutputNodeVolumeChanged(uint64_t node,
257 int volume) {
258 // |node| refers to the active volume device, which we don't care here.
259 for (auto& observer : volume_observers_)
260 observer->OnVolumeChanged(volume);
261 }
262
263 void AssistantControllerImpl::OnAccessibilityStatusChanged() {
264 if (!assistant_)
265 return;
266
267 // The Assistant service needs to be informed of changes to accessibility
268 // state so that it can turn on/off A11Y features appropriately.
269 assistant_->OnAccessibilityStatusChanged(
270 Shell::Get()->accessibility_controller()->spoken_feedback().enabled());
271 }
272
273 bool AssistantControllerImpl::IsAssistantReady() const {
274 return !!assistant_;
275 }
276
277 void AssistantControllerImpl::NotifyConstructed() {
278 for (AssistantControllerObserver& observer : observers_)
279 observer.OnAssistantControllerConstructed();
280 }
281
282 void AssistantControllerImpl::NotifyDestroying() {
283 for (AssistantControllerObserver& observer : observers_)
284 observer.OnAssistantControllerDestroying();
285 }
286
287 void AssistantControllerImpl::NotifyDeepLinkReceived(const GURL& deep_link) {
288 using assistant::util::DeepLinkType;
289
290 // Retrieve deep link type and parsed parameters.
291 DeepLinkType type = assistant::util::GetDeepLinkType(deep_link);
292 const std::map<std::string, std::string> params =
293 assistant::util::GetDeepLinkParams(deep_link);
294
295 for (AssistantControllerObserver& observer : observers_)
296 observer.OnDeepLinkReceived(type, params);
297 }
298
299 void AssistantControllerImpl::NotifyOpeningUrl(const GURL& url,
300 bool in_background,
301 bool from_server) {
302 for (AssistantControllerObserver& observer : observers_)
303 observer.OnOpeningUrl(url, in_background, from_server);
304 }
305
306 void AssistantControllerImpl::NotifyUrlOpened(const GURL& url,
307 bool from_server) {
308 for (AssistantControllerObserver& observer : observers_)
309 observer.OnUrlOpened(url, from_server);
310 }
311
312 void AssistantControllerImpl::OnAssistantStatusChanged(
313 chromeos::assistant::AssistantStatus status) {
314 if (status == chromeos::assistant::AssistantStatus::NOT_READY)
315 assistant_ui_controller_.CloseUi(
316 chromeos::assistant::AssistantExitPoint::kUnspecified);
317 }
318
319 void AssistantControllerImpl::OnLockedFullScreenStateChanged(bool enabled) {
320 if (enabled)
321 assistant_ui_controller_.CloseUi(
322 chromeos::assistant::AssistantExitPoint::kUnspecified);
323 }
324
325 void AssistantControllerImpl::BindVolumeControl(
326 mojo::PendingReceiver<mojom::AssistantVolumeControl> receiver) {
327 Shell::Get()->assistant_controller()->BindReceiver(std::move(receiver));
328 }
329
330 } // namespace ash
331