1 // Copyright (c) 2012 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/speech/speech_input_extension_api.h"
8 #include "base/values.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/speech/speech_input_extension_manager.h"
12 #include "chrome/common/chrome_notification_types.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/notification_details.h"
15 #include "content/public/browser/notification_source.h"
17 using content::BrowserThread
;
21 const char kLanguageKey
[] = "language";
22 const char kGrammarKey
[] = "grammar";
23 const char kFilterProfanitiesKey
[] = "filterProfanities";
25 const char kDefaultGrammar
[] = "builtin:dictation";
26 const bool kDefaultFilterProfanities
= true;
28 } // anonymous namespace
30 SpeechInputAsyncFunction::SpeechInputAsyncFunction(
34 int transition_notification
)
35 : start_state_(start_state
),
36 transition_state_(transition_state
),
37 end_state_(end_state
),
38 transition_notification_(transition_notification
),
39 expecting_transition_(false),
41 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED
,
42 content::Source
<Profile
>(profile()));
45 SpeechInputAsyncFunction::~SpeechInputAsyncFunction() {}
47 void SpeechInputAsyncFunction::Run() {
49 registrar_
.RemoveAll();
54 SpeechInputExtensionManager::State state
=
55 SpeechInputExtensionManager::GetForProfile(profile())->state();
57 // RunImpl should be always called once independently of the state we're in,
58 // otherwise we might miss requestDenied error situations.
59 if (!expecting_transition_
) {
60 SpeechInputExtensionManager::State state_before_call
= state
;
62 // Register before RunImpl to ensure it's received if generated.
63 if (state_before_call
== start_state_
) {
64 registrar_
.Add(this, transition_notification_
,
65 content::Source
<Profile
>(profile()));
66 AddRef(); // Balanced in Observe().
70 registrar_
.RemoveAll();
75 // RunImpl should always return false and set the appropriate error code
76 // when called in a state different to the start one.
77 DCHECK_EQ(state_before_call
, start_state_
);
79 state
= SpeechInputExtensionManager::GetForProfile(profile())->state();
80 DCHECK_EQ(state
, transition_state_
);
81 expecting_transition_
= true;
84 if (state
== transition_state_
)
87 DCHECK_EQ(state
, end_state_
);
88 registrar_
.RemoveAll();
92 void SpeechInputAsyncFunction::Observe(
94 const content::NotificationSource
& source
,
95 const content::NotificationDetails
& details
) {
96 DCHECK_EQ(profile(), content::Source
<Profile
>(source
).ptr());
98 if (type
== chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED
) {
99 SpeechInputExtensionManager::ExtensionError
* error_details
=
100 content::Details
<SpeechInputExtensionManager::ExtensionError
>(
102 if (error_details
->extension_id_
!= extension_id())
105 error_
= error_details
->error_
;
108 DCHECK_EQ(type
, transition_notification_
);
109 if (*content::Details
<std::string
>(details
).ptr() != extension_id())
111 DCHECK(expecting_transition_
);
115 Release(); // Balanced in Run().
118 StartSpeechInputFunction::StartSpeechInputFunction()
119 : SpeechInputAsyncFunction(SpeechInputExtensionManager::kIdle
,
120 SpeechInputExtensionManager::kStarting
,
121 SpeechInputExtensionManager::kRecording
,
122 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED
) {
125 bool StartSpeechInputFunction::RunImpl() {
126 std::string language
;
127 std::string grammar
= kDefaultGrammar
;
128 bool filter_profanities
= kDefaultFilterProfanities
;
130 if (!args_
->empty()) {
131 DictionaryValue
*options
;
132 if (!args_
->GetDictionary(0, &options
))
136 if (options
->HasKey(kLanguageKey
))
137 options
->GetString(kLanguageKey
, &language
);
138 if (options
->HasKey(kGrammarKey
))
139 options
->GetString(kGrammarKey
, &grammar
);
141 if (options
->HasKey(kFilterProfanitiesKey
)) {
142 options
->GetBoolean(kFilterProfanitiesKey
,
143 &filter_profanities
);
147 // Use the application locale if the language is empty or not provided.
148 if (language
.empty()) {
149 language
= g_browser_process
->GetApplicationLocale();
150 VLOG(1) << "Language not specified. Using application locale " << language
;
153 return SpeechInputExtensionManager::GetForProfile(profile())->Start(
154 extension_id(), language
, grammar
, filter_profanities
, &error_
);
157 StopSpeechInputFunction::StopSpeechInputFunction()
158 : SpeechInputAsyncFunction(SpeechInputExtensionManager::kRecording
,
159 SpeechInputExtensionManager::kStopping
,
160 SpeechInputExtensionManager::kIdle
,
161 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED
) {
164 bool StopSpeechInputFunction::RunImpl() {
165 return SpeechInputExtensionManager::GetForProfile(
166 profile())->Stop(extension_id(), &error_
);
169 void IsRecordingSpeechInputFunction::SetIsRecordingResult(bool result
) {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
171 SetResult(Value::CreateBooleanValue(result
));
175 void IsRecordingSpeechInputFunction::Run() {
176 SpeechInputExtensionManager::GetForProfile(profile())->IsRecording(
177 base::Bind(&IsRecordingSpeechInputFunction::SetIsRecordingResult
, this));
180 bool IsRecordingSpeechInputFunction::RunImpl() {
181 // The operation needs to be asynchronous because of thread requirements.
182 // This method does nothing, but it needs to be implemented anyway.