1 // Copyright 2014 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.
5 #include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h"
9 #include "base/i18n/rtl.h"
10 #include "base/lazy_instance.h"
11 #include "base/prefs/pref_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/search/hotword_audio_history_handler.h"
14 #include "chrome/browser/search/hotword_client.h"
15 #include "chrome/browser/search/hotword_service.h"
16 #include "chrome/browser/search/hotword_service_factory.h"
17 #include "chrome/browser/ui/app_list/app_list_service.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "content/public/browser/speech_recognition_session_preamble.h"
22 #include "extensions/browser/event_router.h"
23 #include "ui/base/l10n/l10n_util.h"
25 namespace extensions
{
27 namespace hotword_private_constants
{
28 const char kHotwordServiceUnavailable
[] = "Hotword Service is unavailable.";
29 const char kHotwordEventServiceUnavailable
[] =
30 "Hotword Private Event Service is unavailable.";
31 } // hotword_private_constants
33 namespace OnEnabledChanged
=
34 api::hotword_private::OnEnabledChanged
;
36 static base::LazyInstance
<
37 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
> > g_factory
=
38 LAZY_INSTANCE_INITIALIZER
;
40 HotwordPrivateEventService::HotwordPrivateEventService(
41 content::BrowserContext
* context
)
42 : profile_(Profile::FromBrowserContext(context
)) {
43 pref_change_registrar_
.Init(profile_
->GetPrefs());
44 pref_change_registrar_
.Add(
45 prefs::kHotwordSearchEnabled
,
46 base::Bind(&HotwordPrivateEventService::OnEnabledChanged
,
47 base::Unretained(this)));
48 pref_change_registrar_
.Add(
49 prefs::kHotwordAlwaysOnSearchEnabled
,
50 base::Bind(&HotwordPrivateEventService::OnEnabledChanged
,
51 base::Unretained(this)));
54 HotwordPrivateEventService::~HotwordPrivateEventService() {
57 void HotwordPrivateEventService::Shutdown() {
61 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
>*
62 HotwordPrivateEventService::GetFactoryInstance() {
63 return g_factory
.Pointer();
67 const char* HotwordPrivateEventService::service_name() {
68 return "HotwordPrivateEventService";
71 void HotwordPrivateEventService::OnEnabledChanged(
72 const std::string
& pref_name
) {
73 DCHECK(pref_name
== std::string(prefs::kHotwordSearchEnabled
) ||
74 pref_name
== std::string(prefs::kHotwordAlwaysOnSearchEnabled
) ||
75 pref_name
== std::string(
76 hotword_internal::kHotwordTrainingEnabled
));
77 SignalEvent(OnEnabledChanged::kEventName
);
80 void HotwordPrivateEventService::OnHotwordSessionRequested() {
81 SignalEvent(api::hotword_private::OnHotwordSessionRequested::kEventName
);
84 void HotwordPrivateEventService::OnHotwordSessionStopped() {
85 SignalEvent(api::hotword_private::OnHotwordSessionStopped::kEventName
);
88 void HotwordPrivateEventService::OnFinalizeSpeakerModel() {
89 SignalEvent(api::hotword_private::OnFinalizeSpeakerModel::kEventName
);
92 void HotwordPrivateEventService::OnSpeakerModelSaved() {
93 SignalEvent(api::hotword_private::OnSpeakerModelSaved::kEventName
);
96 void HotwordPrivateEventService::OnHotwordTriggered() {
97 SignalEvent(api::hotword_private::OnHotwordTriggered::kEventName
);
100 void HotwordPrivateEventService::SignalEvent(const std::string
& event_name
) {
101 EventRouter
* router
= EventRouter::Get(profile_
);
102 if (!router
|| !router
->HasEventListener(event_name
))
104 scoped_ptr
<base::ListValue
> args(new base::ListValue());
105 scoped_ptr
<Event
> event(new Event(event_name
, args
.Pass()));
106 router
->BroadcastEvent(event
.Pass());
109 bool HotwordPrivateSetEnabledFunction::RunSync() {
110 scoped_ptr
<api::hotword_private::SetEnabled::Params
> params(
111 api::hotword_private::SetEnabled::Params::Create(*args_
));
112 EXTENSION_FUNCTION_VALIDATE(params
.get());
114 PrefService
* prefs
= GetProfile()->GetPrefs();
115 prefs
->SetBoolean(prefs::kHotwordSearchEnabled
, params
->state
);
119 bool HotwordPrivateSetAudioLoggingEnabledFunction::RunSync() {
120 scoped_ptr
<api::hotword_private::SetAudioLoggingEnabled::Params
> params(
121 api::hotword_private::SetAudioLoggingEnabled::Params::Create(*args_
));
122 EXTENSION_FUNCTION_VALIDATE(params
.get());
124 // TODO(kcarattini): Sync the chrome pref with the account-level
125 // Audio History setting.
126 PrefService
* prefs
= GetProfile()->GetPrefs();
127 prefs
->SetBoolean(prefs::kHotwordAudioLoggingEnabled
, params
->state
);
131 bool HotwordPrivateSetHotwordAlwaysOnSearchEnabledFunction::RunSync() {
132 scoped_ptr
<api::hotword_private::SetHotwordAlwaysOnSearchEnabled::Params
>
133 params(api::hotword_private::SetHotwordAlwaysOnSearchEnabled::Params::
135 EXTENSION_FUNCTION_VALIDATE(params
.get());
137 PrefService
* prefs
= GetProfile()->GetPrefs();
138 prefs
->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, params
->state
);
142 bool HotwordPrivateGetStatusFunction::RunSync() {
143 api::hotword_private::StatusDetails result
;
145 HotwordService
* hotword_service
=
146 HotwordServiceFactory::GetForProfile(GetProfile());
147 if (!hotword_service
) {
148 result
.available
= false;
149 result
.enabled
= false;
150 result
.audio_logging_enabled
= false;
151 result
.always_on_enabled
= false;
152 result
.user_is_active
= false;
154 result
.available
= hotword_service
->IsServiceAvailable();
155 result
.enabled
= hotword_service
->IsSometimesOnEnabled();
156 result
.audio_logging_enabled
= hotword_service
->IsOptedIntoAudioLogging();
157 result
.training_enabled
= hotword_service
->IsTraining();
158 result
.always_on_enabled
= hotword_service
->IsAlwaysOnEnabled();
159 result
.user_is_active
= hotword_service
->UserIsActive();
162 PrefService
* prefs
= GetProfile()->GetPrefs();
163 result
.enabled_set
= prefs
->HasPrefPath(prefs::kHotwordSearchEnabled
);
164 result
.experimental_hotword_enabled
=
165 HotwordService::IsExperimentalHotwordingEnabled();
167 SetResult(result
.ToValue().release());
171 bool HotwordPrivateSetHotwordSessionStateFunction::RunSync() {
172 scoped_ptr
<api::hotword_private::SetHotwordSessionState::Params
> params(
173 api::hotword_private::SetHotwordSessionState::Params::Create(*args_
));
174 EXTENSION_FUNCTION_VALIDATE(params
.get());
176 HotwordService
* hotword_service
=
177 HotwordServiceFactory::GetForProfile(GetProfile());
178 if (hotword_service
&&
179 hotword_service
->client() &&
180 !hotword_service
->IsTraining())
181 hotword_service
->client()->OnHotwordStateChanged(params
->started
);
185 bool HotwordPrivateNotifyHotwordRecognitionFunction::RunSync() {
186 scoped_ptr
<api::hotword_private::NotifyHotwordRecognition::Params
> params(
187 api::hotword_private::NotifyHotwordRecognition::Params::Create(*args_
));
188 EXTENSION_FUNCTION_VALIDATE(params
.get());
190 scoped_refptr
<content::SpeechRecognitionSessionPreamble
> preamble
;
191 if (params
->log
.get() &&
192 !params
->log
->buffer
.empty() &&
193 params
->log
->channels
== 1) {
194 // TODO(amistry): Convert multi-channel preamble log into mono.
195 preamble
= new content::SpeechRecognitionSessionPreamble();
196 preamble
->sample_rate
= params
->log
->sample_rate
;
197 preamble
->sample_depth
= params
->log
->bytes_per_sample
;
198 preamble
->sample_data
.swap(params
->log
->buffer
);
201 HotwordService
* hotword_service
=
202 HotwordServiceFactory::GetForProfile(GetProfile());
203 if (hotword_service
) {
204 if (hotword_service
->IsTraining()) {
205 hotword_service
->NotifyHotwordTriggered();
206 } else if (hotword_service
->client()) {
207 hotword_service
->client()->OnHotwordRecognized(preamble
);
208 } else if (HotwordService::IsExperimentalHotwordingEnabled() &&
209 hotword_service
->IsAlwaysOnEnabled()) {
210 Browser
* browser
= GetCurrentBrowser();
211 // If a Browser does not exist, fall back to the universally available,
212 // but not recommended, way.
213 AppListService
* app_list_service
= AppListService::Get(
214 browser
? browser
->host_desktop_type() : chrome::GetActiveDesktop());
215 CHECK(app_list_service
);
216 app_list_service
->ShowForVoiceSearch(GetProfile(), preamble
);
222 bool HotwordPrivateGetLaunchStateFunction::RunSync() {
223 HotwordService
* hotword_service
=
224 HotwordServiceFactory::GetForProfile(GetProfile());
225 if (!hotword_service
) {
226 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
230 api::hotword_private::LaunchState result
;
232 hotword_service
->GetHotwordAudioVerificationLaunchMode();
233 SetResult(result
.ToValue().release());
237 bool HotwordPrivateStartTrainingFunction::RunSync() {
238 HotwordService
* hotword_service
=
239 HotwordServiceFactory::GetForProfile(GetProfile());
240 if (!hotword_service
) {
241 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
245 hotword_service
->StartTraining();
249 bool HotwordPrivateFinalizeSpeakerModelFunction::RunSync() {
250 HotwordService
* hotword_service
=
251 HotwordServiceFactory::GetForProfile(GetProfile());
252 if (!hotword_service
) {
253 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
257 hotword_service
->FinalizeSpeakerModel();
261 bool HotwordPrivateNotifySpeakerModelSavedFunction::RunSync() {
262 HotwordPrivateEventService
* event_service
=
263 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
>::Get(
265 if (!event_service
) {
266 error_
= hotword_private_constants::kHotwordEventServiceUnavailable
;
270 event_service
->OnSpeakerModelSaved();
274 bool HotwordPrivateStopTrainingFunction::RunSync() {
275 HotwordService
* hotword_service
=
276 HotwordServiceFactory::GetForProfile(GetProfile());
277 if (!hotword_service
) {
278 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
282 hotword_service
->StopTraining();
286 bool HotwordPrivateGetLocalizedStringsFunction::RunSync() {
287 base::DictionaryValue
* localized_strings
= new base::DictionaryValue();
289 localized_strings
->SetString(
291 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_CLOSE
));
292 localized_strings
->SetString(
294 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_CANCEL
));
295 localized_strings
->SetString(
297 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_TITLE
));
298 localized_strings
->SetString(
300 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_SUBTITLE
));
301 localized_strings
->SetString(
303 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_DESCRIPTION
));
304 localized_strings
->SetString(
305 "introDescriptionAudioHistoryEnabled",
306 l10n_util::GetStringUTF16(
307 IDS_HOTWORD_OPT_IN_INTRO_DESCRIPTION_AUDIO_HISTORY_ENABLED
));
308 localized_strings
->SetString(
310 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_START
));
311 localized_strings
->SetString(
313 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_TITLE
));
314 localized_strings
->SetString(
315 "audioHistoryDescription1",
316 l10n_util::GetStringUTF16(
317 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_1
));
318 localized_strings
->SetString(
319 "audioHistoryDescription2",
320 l10n_util::GetStringUTF16(
321 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_2
));
322 localized_strings
->SetString(
323 "audioHistoryDescription3",
324 l10n_util::GetStringUTF16(
325 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_3
));
326 localized_strings
->SetString(
328 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_AGREE
));
329 localized_strings
->SetString(
331 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_WAIT
));
332 localized_strings
->SetString(
334 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_ERROR
));
335 localized_strings
->SetString(
337 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_TITLE
));
338 localized_strings
->SetString(
339 "trainingDescription",
340 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_DESCRIPTION
));
341 localized_strings
->SetString(
343 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_SPEAK
));
344 localized_strings
->SetString(
345 "trainingFirstPrompt",
346 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_FIRST_PROMPT
));
347 localized_strings
->SetString(
348 "trainingMiddlePrompt",
349 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_MIDDLE_PROMPT
));
350 localized_strings
->SetString(
351 "trainingLastPrompt",
352 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_LAST_PROMPT
));
353 localized_strings
->SetString(
355 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_RECORDED
));
356 localized_strings
->SetString(
358 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_TIMEOUT
));
359 localized_strings
->SetString(
361 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_RETRY
));
362 localized_strings
->SetString(
364 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_TITLE
));
365 localized_strings
->SetString(
367 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_INTRO
));
368 localized_strings
->SetString(
370 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_ITEM_1
));
371 localized_strings
->SetString(
373 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_ITEM_2
));
374 localized_strings
->SetString(
376 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_SETTINGS
));
377 localized_strings
->SetString(
378 "finishedAudioHistory",
379 l10n_util::GetStringUTF16(
380 IDS_HOTWORD_OPT_IN_FINISHED_AUDIO_HISTORY
));
381 localized_strings
->SetString(
383 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISH
));
384 localized_strings
->SetString(
386 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_WAIT
));
388 localized_strings
->SetString("textdirection",
389 base::i18n::IsRTL() ? "rtl" : "ltr");
391 SetResult(localized_strings
);
395 bool HotwordPrivateSetAudioHistoryEnabledFunction::RunAsync() {
396 scoped_ptr
<api::hotword_private::SetAudioHistoryEnabled::Params
> params(
397 api::hotword_private::SetAudioHistoryEnabled::Params::Create(*args_
));
398 EXTENSION_FUNCTION_VALIDATE(params
.get());
400 HotwordService
* hotword_service
=
401 HotwordServiceFactory::GetForProfile(GetProfile());
402 if (!hotword_service
|| !hotword_service
->GetAudioHistoryHandler()) {
403 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
407 hotword_service
->GetAudioHistoryHandler()->SetAudioHistoryEnabled(
410 &HotwordPrivateSetAudioHistoryEnabledFunction::SetResultAndSendResponse
,
415 void HotwordPrivateSetAudioHistoryEnabledFunction::SetResultAndSendResponse(
416 bool success
, bool new_enabled_value
) {
417 api::hotword_private::AudioHistoryState result
;
418 result
.success
= success
;
419 result
.enabled
= new_enabled_value
;
420 SetResult(result
.ToValue().release());
424 bool HotwordPrivateGetAudioHistoryEnabledFunction::RunAsync() {
425 HotwordService
* hotword_service
=
426 HotwordServiceFactory::GetForProfile(GetProfile());
427 if (!hotword_service
|| !hotword_service
->GetAudioHistoryHandler()) {
428 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
432 hotword_service
->GetAudioHistoryHandler()->GetAudioHistoryEnabled(base::Bind(
433 &HotwordPrivateGetAudioHistoryEnabledFunction::SetResultAndSendResponse
,
439 void HotwordPrivateGetAudioHistoryEnabledFunction::SetResultAndSendResponse(
440 bool success
, bool new_enabled_value
) {
441 api::hotword_private::AudioHistoryState result
;
442 result
.success
= success
;
443 result
.enabled
= new_enabled_value
;
444 SetResult(result
.ToValue().release());
448 } // namespace extensions