172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// Use of this source code is governed by a BSD-style license that can be
3bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// found in the LICENSE file.
4bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
5dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/speech/speech_input_manager.h"
6bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <map>
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <string>
921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/lazy_instance.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/ref_counted.h"
1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/synchronization/lock.h"
133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h"
14201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/utf_string_conversions.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/browser_process.h"
163f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/platform_util.h"
17201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/prefs/pref_service.h"
18bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_input_bubble_controller.h"
19bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/tab_contents/tab_util.h"
203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/common/chrome_switches.h"
21201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/common/pref_names.h"
22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/speech/speech_recognizer.h"
24bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "grit/generated_resources.h"
25bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "media/audio/audio_manager.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
27bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
28201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_WIN)
29201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/installer/util/wmi.h"
30201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif
31201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace speech_input {
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochnamespace {
35201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Asynchronously fetches the PC and audio hardware/driver info if
37201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// the user has opted into UMA. This information is sent with speech input
38201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// requests to the server for identifying and improving quality issues with
39201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// specific device configurations.
4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclass OptionalRequestInfo
4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : public base::RefCountedThreadSafe<OptionalRequestInfo> {
42201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch public:
4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  OptionalRequestInfo() : can_report_metrics_(false) {}
44201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
45201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  void Refresh() {
46201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
47201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // UMA opt-in can be checked only from the UI thread, so switch to that.
48201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        NewRunnableMethod(this,
5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                          &OptionalRequestInfo::CheckUMAAndGetHardwareInfo));
51201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
52201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
53201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  void CheckUMAAndGetHardwareInfo() {
54201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    if (g_browser_process->local_state()->GetBoolean(
56201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        prefs::kMetricsReportingEnabled)) {
57201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      // Access potentially slow OS calls from the FILE thread.
58201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          NewRunnableMethod(this, &OptionalRequestInfo::GetHardwareInfo));
60201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    }
61201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
62201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
63201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  void GetHardwareInfo() {
64201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock lock(lock_);
6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    can_report_metrics_ = true;
6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_WIN)
68201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    value_ = UTF16ToUTF8(
69201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        installer::WMIComputerSystem::GetModel() + L"|" +
70201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        AudioManager::GetAudioManager()->GetAudioInputDeviceModel());
7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#else  // defined(OS_WIN)
7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    value_ = UTF16ToUTF8(
7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        AudioManager::GetAudioManager()->GetAudioInputDeviceModel());
7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif  // defined(OS_WIN)
75201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
76201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
77201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  std::string value() {
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock lock(lock_);
79201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    return value_;
80201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
81201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool can_report_metrics() {
8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock lock(lock_);
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return can_report_metrics_;
8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
87201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch private:
8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Lock lock_;
89201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  std::string value_;
9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool can_report_metrics_;
91201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo);
93201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch};
94201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
95bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenclass SpeechInputManagerImpl : public SpeechInputManager,
96bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                               public SpeechInputBubbleControllerDelegate,
97bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                               public SpeechRecognizerDelegate {
98bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public:
99bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // SpeechInputManager methods.
100bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void StartRecognition(SpeechInputManagerDelegate* delegate,
101bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                int caller_id,
102bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                int render_process_id,
103bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                int render_view_id,
104513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                const gfx::Rect& element_rect,
105513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                const std::string& language,
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                const std::string& grammar,
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                const std::string& origin_url);
108bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void CancelRecognition(int caller_id);
109bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void StopRecording(int caller_id);
11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  virtual void CancelAllRequestsWithDelegate(
11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      SpeechInputManagerDelegate* delegate);
112bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
113bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // SpeechRecognizer::Delegate methods.
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void DidStartReceivingAudio(int caller_id);
115513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  virtual void SetRecognitionResult(int caller_id,
116513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                    bool error,
117513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                    const SpeechInputResultArray& result);
118bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void DidCompleteRecording(int caller_id);
119bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void DidCompleteRecognition(int caller_id);
120bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void OnRecognizerError(int caller_id,
121bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                 SpeechRecognizer::ErrorCode error);
122bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void DidCompleteEnvironmentEstimation(int caller_id);
123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  virtual void SetInputVolume(int caller_id, float volume, float noise_volume);
124bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
125bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // SpeechInputBubbleController::Delegate methods.
126bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void InfoBubbleButtonClicked(int caller_id,
127bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                       SpeechInputBubble::Button button);
128bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual void InfoBubbleFocusChanged(int caller_id);
129bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
130bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen private:
131bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  struct SpeechInputRequest {
132bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    SpeechInputManagerDelegate* delegate;
133bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    scoped_refptr<SpeechRecognizer> recognizer;
134bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    bool is_active;  // Set to true when recording or recognition is going on.
135bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  };
136bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
137bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // Private constructor to enforce singleton.
13821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  friend struct base::DefaultLazyInstanceTraits<SpeechInputManagerImpl>;
139bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  SpeechInputManagerImpl();
140bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  virtual ~SpeechInputManagerImpl();
141bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
142bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bool HasPendingRequest(int caller_id) const;
143bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  SpeechInputManagerDelegate* GetDelegate(int caller_id) const;
144bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
145bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  void CancelRecognitionAndInformDelegate(int caller_id);
146bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
147bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // Starts/restarts recognition for an existing request.
148bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  void StartRecognitionForRequest(int caller_id);
149bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
150bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap;
151bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  SpeechRecognizerMap requests_;
152bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  int recording_caller_id_;
153bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  scoped_refptr<SpeechInputBubbleController> bubble_controller_;
15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  scoped_refptr<OptionalRequestInfo> optional_request_info_;
155bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen};
156bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbase::LazyInstance<SpeechInputManagerImpl> g_speech_input_manager_impl(
15821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    base::LINKER_INITIALIZED);
159bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}  // namespace
16172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
16221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenSpeechInputManager* SpeechInputManager::Get() {
16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return g_speech_input_manager_impl.Pointer();
164ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch}
165ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch
166dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputManager::ShowAudioInputSettings() {
167dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Since AudioManager::ShowAudioInputSettings can potentially launch external
168dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // processes, do that in the FILE thread to not block the calling threads.
169dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
170dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    BrowserThread::PostTask(
171dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        BrowserThread::FILE, FROM_HERE,
172dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        NewRunnableFunction(&SpeechInputManager::ShowAudioInputSettings));
173dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return;
1743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
176dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  DCHECK(AudioManager::GetAudioManager()->CanShowAudioInputSettings());
177dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (AudioManager::GetAudioManager()->CanShowAudioInputSettings())
178dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    AudioManager::GetAudioManager()->ShowAudioInputSettings();
1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
1803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
181bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::SpeechInputManagerImpl()
182bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    : recording_caller_id_(0),
183bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      bubble_controller_(new SpeechInputBubbleController(
184bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen          ALLOW_THIS_IN_INITIALIZER_LIST(this))) {
185bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
186bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
187bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::~SpeechInputManagerImpl() {
188bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  while (requests_.begin() != requests_.end())
189bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    CancelRecognition(requests_.begin()->first);
190bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
191bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
192bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenbool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const {
193bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  return requests_.find(caller_id) != requests_.end();
194bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
195bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
196bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate(
197bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int caller_id) const {
198bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  return requests_.find(caller_id)->second.delegate;
199bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
200bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
201bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognition(
202bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    SpeechInputManagerDelegate* delegate,
203bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int caller_id,
204bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int render_process_id,
205bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int render_view_id,
206513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const gfx::Rect& element_rect,
207513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const std::string& language,
20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& grammar,
20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& origin_url) {
210bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(!HasPendingRequest(caller_id));
211bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
212bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id,
213bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                   element_rect);
214bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!optional_request_info_.get()) {
21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    optional_request_info_ = new OptionalRequestInfo();
217201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // Since hardware info is optional with speech input requests, we start an
218201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // asynchronous fetch here and move on with recording audio. This first
219201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // speech input request would send an empty string for hardware info and
220201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // subsequent requests may have the hardware info available if the fetch
221201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // completed before them. This way we don't end up stalling the user with
222201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // a long wait and disk seeks when they click on a UI element and start
223201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // speaking.
22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    optional_request_info_->Refresh();
225201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
226201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
227bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  SpeechInputRequest* request = &requests_[caller_id];
228bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  request->delegate = delegate;
22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  request->recognizer = new SpeechRecognizer(
23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      this, caller_id, language, grammar, optional_request_info_->value(),
23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      optional_request_info_->can_report_metrics() ? origin_url : "");
232bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  request->is_active = false;
233bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
234bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  StartRecognitionForRequest(caller_id);
235bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
236bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
237bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) {
238bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
239bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
240bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // If we are currently recording audio for another caller, abort that cleanly.
241bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (recording_caller_id_)
242bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    CancelRecognitionAndInformDelegate(recording_caller_id_);
243bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
244bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) {
245bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    bubble_controller_->SetBubbleMessage(
246bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen        caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC));
247bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  } else {
248bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    recording_caller_id_ = caller_id;
249bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    requests_[caller_id].is_active = true;
250bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    requests_[caller_id].recognizer->StartRecording();
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bubble_controller_->SetBubbleWarmUpMode(caller_id);
252bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  }
253bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
254bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
255bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognition(int caller_id) {
256bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
257bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (requests_[caller_id].is_active)
258bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    requests_[caller_id].recognizer->CancelRecognition();
259bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  requests_.erase(caller_id);
260bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (recording_caller_id_ == caller_id)
261bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    recording_caller_id_ = 0;
262bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bubble_controller_->CloseBubble(caller_id);
263bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
264bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
26572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid SpeechInputManagerImpl::CancelAllRequestsWithDelegate(
26672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    SpeechInputManagerDelegate* delegate) {
26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SpeechRecognizerMap::iterator it = requests_.begin();
26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  while (it != requests_.end()) {
26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (it->second.delegate == delegate) {
27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      CancelRecognition(it->first);
27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // This map will have very few elements so it is simpler to restart.
27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      it = requests_.begin();
27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ++it;
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
279bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StopRecording(int caller_id) {
280bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
281bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  requests_[caller_id].recognizer->StopRecording();
282bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
283bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
284513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SpeechInputManagerImpl::SetRecognitionResult(
285513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    int caller_id, bool error, const SpeechInputResultArray& result) {
286bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
287513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  GetDelegate(caller_id)->SetRecognitionResult(caller_id, result);
288bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
289bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
290bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecording(int caller_id) {
291bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(recording_caller_id_ == caller_id);
292bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
293bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  recording_caller_id_ = 0;
294bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  GetDelegate(caller_id)->DidCompleteRecording(caller_id);
295bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bubble_controller_->SetBubbleRecognizingMode(caller_id);
296bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
297bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
298bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) {
299bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  GetDelegate(caller_id)->DidCompleteRecognition(caller_id);
300bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  requests_.erase(caller_id);
301bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bubble_controller_->CloseBubble(caller_id);
302bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
303bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
304bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::OnRecognizerError(
305bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int caller_id, SpeechRecognizer::ErrorCode error) {
306bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (caller_id == recording_caller_id_)
307bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    recording_caller_id_ = 0;
308bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
309bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  requests_[caller_id].is_active = false;
310bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
311dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  struct ErrorMessageMapEntry {
312dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SpeechRecognizer::ErrorCode error;
313dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    int message_id;
314dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  };
315dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ErrorMessageMapEntry error_message_map[] = {
316dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    {
317dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE, IDS_SPEECH_INPUT_MIC_ERROR
318dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }, {
319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH, IDS_SPEECH_INPUT_NO_SPEECH
320dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }, {
321dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS, IDS_SPEECH_INPUT_NO_RESULTS
322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }, {
323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      SpeechRecognizer::RECOGNIZER_ERROR_NETWORK, IDS_SPEECH_INPUT_NET_ERROR
324dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  };
326dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(error_message_map); ++i) {
327dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (error_message_map[i].error == error) {
328dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      bubble_controller_->SetBubbleMessage(
329dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen          caller_id,
330dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen          l10n_util::GetStringUTF16(error_message_map[i].message_id));
331bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      return;
332dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
333bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  }
334dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
335dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  NOTREACHED() << "unknown error " << error;
336bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
337bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputManagerImpl::DidStartReceivingAudio(int caller_id) {
339bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
340bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(recording_caller_id_ == caller_id);
341bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  bubble_controller_->SetBubbleRecordingMode(caller_id);
342bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
343bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) {
345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(recording_caller_id_ == caller_id);
347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
349dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputManagerImpl::SetInputVolume(int caller_id, float volume,
350dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                            float noise_volume) {
351bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK(HasPendingRequest(caller_id));
352bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  DCHECK_EQ(recording_caller_id_, caller_id);
353bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
354dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  bubble_controller_->SetBubbleInputVolume(caller_id, volume, noise_volume);
355bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
356bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
357bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) {
358bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id);
359bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  CancelRecognition(caller_id);
360bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  cur_delegate->DidCompleteRecording(caller_id);
361bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  cur_delegate->DidCompleteRecognition(caller_id);
362bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
363bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
364bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleButtonClicked(
365bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    int caller_id, SpeechInputBubble::Button button) {
366731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
367bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // Ignore if the caller id was not in our active recognizers list because the
368bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // user might have clicked more than once, or recognition could have been
369bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // cancelled due to other reasons before the user click was processed.
370bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (!HasPendingRequest(caller_id))
371bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    return;
372bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
373bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (button == SpeechInputBubble::BUTTON_CANCEL) {
374bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    CancelRecognitionAndInformDelegate(caller_id);
375bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) {
376bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    StartRecognitionForRequest(caller_id);
377bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  }
378bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
379bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
380bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleFocusChanged(int caller_id) {
381731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
382bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // Ignore if the caller id was not in our active recognizers list because the
383bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // user might have clicked more than once, or recognition could have been
384bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  // ended due to other reasons before the user click was processed.
385bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (HasPendingRequest(caller_id)) {
386bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    // If this is an ongoing recording or if we were displaying an error message
387bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    // to the user, abort it since user has switched focus. Otherwise
388bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    // recognition has started and keep that going so user can start speaking to
389bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    // another element while this gets the results in parallel.
390bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) {
391bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      CancelRecognitionAndInformDelegate(caller_id);
392bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    }
393bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  }
394bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
395bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
396bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}  // namespace speech_input
397