Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 cr.define('hotword', function() { | 5 cr.define('hotword', function() { |
| 6 'use strict'; | 6 'use strict'; |
| 7 | 7 |
| 8 /** | 8 /** |
| 9 * Trivial container class for session information. | |
| 10 * @constructor | |
| 11 * @struct | |
| 12 * @private | |
| 13 */ | |
| 14 function Session_(source, triggerCb) { | |
| 15 this.source = source; | |
| 16 this.triggerCb = triggerCb; | |
| 17 } | |
| 18 | |
| 19 /** | |
| 9 * Class to manage hotwording state. Starts/stops the hotword detector based | 20 * Class to manage hotwording state. Starts/stops the hotword detector based |
| 10 * on user settings, session requests, and any other factors that play into | 21 * on user settings, session requests, and any other factors that play into |
| 11 * whether or not hotwording should be running. | 22 * whether or not hotwording should be running. |
| 12 * @constructor | 23 * @constructor |
| 13 * @struct | 24 * @struct |
| 14 */ | 25 */ |
| 15 function StateManager() { | 26 function StateManager() { |
| 16 /** | 27 /** |
| 17 * Current state. | 28 * Current state. |
| 18 * @private {hotword.StateManager.State_} | 29 * @private {hotword.StateManager.State_} |
| 19 */ | 30 */ |
| 20 this.state_ = State_.STOPPED; | 31 this.state_ = State_.STOPPED; |
| 21 | 32 |
| 22 /** | 33 /** |
| 23 * Current hotwording status. | 34 * Current hotwording status. |
| 24 * @private {?chrome.hotwordPrivate.StatusDetails} | 35 * @private {?chrome.hotwordPrivate.StatusDetails} |
| 25 */ | 36 */ |
| 26 this.hotwordStatus_ = null; | 37 this.hotwordStatus_ = null; |
| 27 | 38 |
| 28 /** | 39 /** |
| 29 * NaCl plugin manager. | 40 * NaCl plugin manager. |
| 30 * @private {?hotword.NaClManager} | 41 * @private {?hotword.NaClManager} |
| 31 */ | 42 */ |
| 32 this.pluginManager_ = null; | 43 this.pluginManager_ = null; |
| 33 | 44 |
| 34 /** | 45 /** |
| 35 * Source of the current hotword session. | 46 * Currently active hotwording sessions. |
| 36 * @private {?hotword.constants.SessionSource} | 47 * @private {!Array.<hotword.Session_>} |
| 37 */ | 48 */ |
| 38 this.sessionSource_ = null; | 49 this.sessions_ = []; |
| 39 | 50 |
| 40 /** | 51 /** |
| 41 * Callback to run when the hotword detector has successfully started. | 52 * Callbacks to run when the hotword detector has successfully started. |
| 42 * @private {!function()} | 53 * @private {!Array.<function()>} |
| 43 */ | 54 */ |
| 44 this.sessionStartedCb_ = null; | 55 this.sessionStartedCbs_ = []; |
|
rpetterson
2014/09/25 02:23:38
why aren't these stored in the Session_ type?
Anand Mistry (off Chromium)
2014/09/25 05:23:42
There could be cases where you have several of the
| |
| 56 | |
| 57 /** | |
| 58 * Event that fires when the hotwording status has changed. | |
| 59 */ | |
| 60 this.onStatusChanged = new chrome.Event(); | |
| 45 | 61 |
| 46 /** | 62 /** |
| 47 * Hotword trigger audio notification... a.k.a The Chime (tm). | 63 * Hotword trigger audio notification... a.k.a The Chime (tm). |
| 48 * @private {!Audio} | 64 * @private {!Audio} |
| 49 */ | 65 */ |
| 50 this.chime_ = document.createElement('audio'); | 66 this.chime_ = document.createElement('audio'); |
| 51 | 67 |
| 52 // Get the initial status. | 68 // Get the initial status. |
| 53 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); | 69 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); |
| 54 | 70 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 73 StateManager.prototype = { | 89 StateManager.prototype = { |
| 74 /** | 90 /** |
| 75 * Request status details update. Intended to be called from the | 91 * Request status details update. Intended to be called from the |
| 76 * hotwordPrivate.onEnabledChanged() event. | 92 * hotwordPrivate.onEnabledChanged() event. |
| 77 */ | 93 */ |
| 78 updateStatus: function() { | 94 updateStatus: function() { |
| 79 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); | 95 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); |
| 80 }, | 96 }, |
| 81 | 97 |
| 82 /** | 98 /** |
| 99 * @return {boolean} True if hotwording is enabled. | |
| 100 */ | |
| 101 isEnabled: function() { | |
| 102 assert(this.hotwordStatus_); | |
| 103 return this.hotwordStatus_.enabled; | |
| 104 }, | |
| 105 | |
| 106 /** | |
| 107 * @return {boolean} True if always-on hotwording is enabled. | |
| 108 */ | |
| 109 isAlwaysOnEnabled: function() { | |
| 110 assert(this.hotwordStatus_); | |
| 111 return this.hotwordStatus_.enabled && | |
| 112 this.hotwordStatus_.alwaysOnEnabled; | |
| 113 }, | |
| 114 | |
| 115 /** | |
| 83 * Callback for hotwordPrivate.getStatus() function. | 116 * Callback for hotwordPrivate.getStatus() function. |
| 84 * @param {chrome.hotwordPrivate.StatusDetails} status Current hotword | 117 * @param {chrome.hotwordPrivate.StatusDetails} status Current hotword |
| 85 * status. | 118 * status. |
| 86 * @private | 119 * @private |
| 87 */ | 120 */ |
| 88 handleStatus_: function(status) { | 121 handleStatus_: function(status) { |
| 89 this.hotwordStatus_ = status; | 122 this.hotwordStatus_ = status; |
| 90 this.updateStateFromStatus_(); | 123 this.updateStateFromStatus_(); |
| 124 | |
| 125 this.onStatusChanged.dispatch(); | |
| 91 }, | 126 }, |
| 92 | 127 |
| 93 /** | 128 /** |
| 94 * Updates state based on the current status. | 129 * Updates state based on the current status. |
| 95 * @private | 130 * @private |
| 96 */ | 131 */ |
| 97 updateStateFromStatus_: function() { | 132 updateStateFromStatus_: function() { |
| 98 if (!this.hotwordStatus_) | 133 if (!this.hotwordStatus_) |
| 99 return; | 134 return; |
| 100 | 135 |
| 101 if (this.hotwordStatus_.enabled) { | 136 if (this.hotwordStatus_.enabled) { |
| 102 // Start the detector if there's a session, and shut it down if there | 137 // Start the detector if there's a session, and shut it down if there |
| 103 // isn't. | 138 // isn't. |
| 104 // TODO(amistry): Support stacking sessions. This can happen when the | |
| 105 // user opens google.com or the NTP, then opens the launcher. Opening | |
| 106 // google.com will create one session, and opening the launcher will | |
| 107 // create the second. Closing the launcher should re-activate the | |
| 108 // google.com session. | |
| 109 // NOTE(amistry): With always-on, we want a different behaviour with | 139 // NOTE(amistry): With always-on, we want a different behaviour with |
| 110 // sessions since the detector should always be running. The exception | 140 // sessions since the detector should always be running. The exception |
| 111 // being when the user triggers by saying 'Ok Google'. In that case, the | 141 // being when the user triggers by saying 'Ok Google'. In that case, the |
| 112 // detector stops, so starting/stopping the launcher session should | 142 // detector stops, so starting/stopping the launcher session should |
| 113 // restart the detector. | 143 // restart the detector. |
| 114 if (this.sessionSource_) | 144 if (this.sessions_.length) |
| 115 this.startDetector_(); | 145 this.startDetector_(); |
| 116 else | 146 else |
| 117 this.shutdownDetector_(); | 147 this.shutdownDetector_(); |
| 118 } else { | 148 } else { |
| 119 // Not enabled. Shut down if running. | 149 // Not enabled. Shut down if running. |
| 120 this.shutdownDetector_(); | 150 this.shutdownDetector_(); |
| 121 } | 151 } |
| 122 }, | 152 }, |
| 123 | 153 |
| 124 /** | 154 /** |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 * Start the recognizer plugin. Assumes the plugin has been loaded and is | 202 * Start the recognizer plugin. Assumes the plugin has been loaded and is |
| 173 * ready to start. | 203 * ready to start. |
| 174 * @private | 204 * @private |
| 175 */ | 205 */ |
| 176 startRecognizer_: function() { | 206 startRecognizer_: function() { |
| 177 assert(this.pluginManager_); | 207 assert(this.pluginManager_); |
| 178 if (this.state_ != State_.RUNNING) { | 208 if (this.state_ != State_.RUNNING) { |
| 179 this.state_ = State_.RUNNING; | 209 this.state_ = State_.RUNNING; |
| 180 this.pluginManager_.startRecognizer(); | 210 this.pluginManager_.startRecognizer(); |
| 181 } | 211 } |
| 182 if (this.sessionStartedCb_) { | 212 for (var i = 0; i < this.sessionStartedCbs_.length; i++) { |
| 183 this.sessionStartedCb_(); | 213 this.sessionStartedCbs_[i](); |
| 184 this.sessionStartedCb_ = null; | |
| 185 } | 214 } |
| 215 this.sessionStartedCbs_ = []; | |
| 186 }, | 216 }, |
| 187 | 217 |
| 188 /** | 218 /** |
| 189 * Shuts down and removes the plugin manager, if it exists. | 219 * Shuts down and removes the plugin manager, if it exists. |
| 190 * @private | 220 * @private |
| 191 */ | 221 */ |
| 192 shutdownPluginManager_: function() { | 222 shutdownPluginManager_: function() { |
| 193 if (this.pluginManager_) { | 223 if (this.pluginManager_) { |
| 194 this.pluginManager_.shutdown(); | 224 this.pluginManager_.shutdown(); |
| 195 this.pluginManager_ = null; | 225 this.pluginManager_ = null; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 234 * @private | 264 * @private |
| 235 */ | 265 */ |
| 236 onTrigger_: function() { | 266 onTrigger_: function() { |
| 237 assert(this.pluginManager_); | 267 assert(this.pluginManager_); |
| 238 // Detector implicitly stops when the hotword is detected. | 268 // Detector implicitly stops when the hotword is detected. |
| 239 this.state_ = State_.STOPPED; | 269 this.state_ = State_.STOPPED; |
| 240 | 270 |
| 241 // Play the chime. | 271 // Play the chime. |
| 242 this.chime_.play(); | 272 this.chime_.play(); |
| 243 | 273 |
| 244 chrome.hotwordPrivate.notifyHotwordRecognition('search', function() {}); | 274 // Implicitly clear the top session. A session needs to be started in |
| 245 | 275 // order to restart the detector. |
| 246 // Implicitly clear the session. A session needs to be started in order to | 276 if (this.sessions_.length) { |
| 247 // restart the detector. | 277 var session = this.sessions_.pop(); |
| 248 this.sessionSource_ = null; | 278 if (session.triggerCb) |
| 249 this.sessionStartedCb_ = null; | 279 session.triggerCb(); |
| 280 } | |
| 250 }, | 281 }, |
| 251 | 282 |
| 252 /** | 283 /** |
| 284 * Remove a hotwording session from the given source. | |
| 285 * @param {!hotword.constants.SessionSource} source Source of the hotword | |
| 286 * session request. | |
| 287 * @private | |
| 288 */ | |
| 289 removeSession_: function(source) { | |
| 290 for (var i = 0; i < this.sessions_.length; i++) { | |
| 291 if (this.sessions_[i].source == source) { | |
| 292 this.sessions_.splice(i, 1); | |
| 293 break; | |
| 294 } | |
| 295 } | |
| 296 }, | |
| 297 | |
| 298 /** | |
| 253 * Start a hotwording session. | 299 * Start a hotwording session. |
| 254 * @param {!hotword.constants.SessionSource} source Source of the hotword | 300 * @param {!hotword.constants.SessionSource} source Source of the hotword |
| 255 * session request. | 301 * session request. |
| 256 * @param {!function()} startedCb Callback invoked when the session has | 302 * @param {!function()} startedCb Callback invoked when the session has |
| 257 * been started successfully. | 303 * been started successfully. |
| 258 */ | 304 */ |
| 259 startSession: function(source, startedCb) { | 305 startSession: function(source, startedCb, triggerCb) { |
| 260 this.sessionSource_ = source; | 306 this.removeSession_(source); |
| 261 this.sessionStartedCb_ = startedCb; | 307 this.sessions_.push(new Session_(source, triggerCb)); |
| 308 this.sessionStartedCbs_.push(startedCb); | |
| 262 this.updateStateFromStatus_(); | 309 this.updateStateFromStatus_(); |
| 263 }, | 310 }, |
| 264 | 311 |
| 265 /** | 312 /** |
| 266 * Stops a hotwording session. | 313 * Stops a hotwording session. |
| 267 * @param {!hotword.constants.SessionSource} source Source of the hotword | 314 * @param {!hotword.constants.SessionSource} source Source of the hotword |
| 268 * session request. | 315 * session request. |
| 269 */ | 316 */ |
| 270 stopSession: function(source) { | 317 stopSession: function(source) { |
| 271 this.sessionSource_ = null; | 318 this.removeSession_(source); |
| 272 this.sessionStartedCb_ = null; | |
| 273 this.updateStateFromStatus_(); | 319 this.updateStateFromStatus_(); |
| 274 } | 320 } |
| 275 }; | 321 }; |
| 276 | 322 |
| 277 return { | 323 return { |
| 278 StateManager: StateManager | 324 StateManager: StateManager |
| 279 }; | 325 }; |
| 280 }); | 326 }); |
| OLD | NEW |