Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Side by Side Diff: chrome/browser/resources/hotword/state_manager.js

Issue 600523004: Support hotwording on google.com and the new tab page. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove unnecessary hasListener check. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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 * @param {!hotword.constants.SessionSource} source Source of the hotword
11 * session.
12 * @param {!function()} triggerCb Callback invoked when the hotword has
13 * triggered.
14 * @param {!function()} startedCb Callback invoked when the session has
15 * been started successfully.
16 * @constructor
17 * @struct
18 * @private
19 */
20 function Session_(source, triggerCb, startedCb) {
21 /**
22 * Source of the hotword session request.
23 * @private {!hotword.constants.SessionSource}
24 */
25 this.source = source;
26
27 /**
28 * Callback invoked when the hotword has triggered.
29 * @private {!function()}
30 */
31 this.triggerCb = triggerCb;
Dan Beam 2014/10/13 21:58:03 add _ if @private
Anand Mistry (off Chromium) 2014/10/13 22:30:16 Done.
32
33 /**
34 * Callback invoked when the session has been started successfully.
35 * @private {?function()}
36 */
37 this.startedCb = startedCb;
38 }
39
40 /**
9 * Class to manage hotwording state. Starts/stops the hotword detector based 41 * Class to manage hotwording state. Starts/stops the hotword detector based
10 * on user settings, session requests, and any other factors that play into 42 * on user settings, session requests, and any other factors that play into
11 * whether or not hotwording should be running. 43 * whether or not hotwording should be running.
12 * @constructor 44 * @constructor
13 * @struct 45 * @struct
14 */ 46 */
15 function StateManager() { 47 function StateManager() {
16 /** 48 /**
17 * Current state. 49 * Current state.
18 * @private {hotword.StateManager.State_} 50 * @private {hotword.StateManager.State_}
19 */ 51 */
20 this.state_ = State_.STOPPED; 52 this.state_ = State_.STOPPED;
21 53
22 /** 54 /**
23 * Current hotwording status. 55 * Current hotwording status.
24 * @private {?chrome.hotwordPrivate.StatusDetails} 56 * @private {?chrome.hotwordPrivate.StatusDetails}
25 */ 57 */
26 this.hotwordStatus_ = null; 58 this.hotwordStatus_ = null;
27 59
28 /** 60 /**
29 * NaCl plugin manager. 61 * NaCl plugin manager.
30 * @private {?hotword.NaClManager} 62 * @private {?hotword.NaClManager}
31 */ 63 */
32 this.pluginManager_ = null; 64 this.pluginManager_ = null;
33 65
34 /** 66 /**
35 * Source of the current hotword session. 67 * Currently active hotwording sessions.
36 * @private {?hotword.constants.SessionSource} 68 * @private {!Array.<hotword.Session_>}
37 */ 69 */
38 this.sessionSource_ = null; 70 this.sessions_ = [];
39 71
40 /** 72 /**
41 * Callback to run when the hotword detector has successfully started. 73 * Event that fires when the hotwording status has changed.
42 * @private {!function()} 74 * @type {!chrome.Event}
43 */ 75 */
44 this.sessionStartedCb_ = null; 76 this.onStatusChanged = new chrome.Event();
45 77
46 /** 78 /**
47 * Hotword trigger audio notification... a.k.a The Chime (tm). 79 * Hotword trigger audio notification... a.k.a The Chime (tm).
48 * @private {!Audio} 80 * @private {!Audio}
49 */ 81 */
50 this.chime_ = document.createElement('audio'); 82 this.chime_ = document.createElement('audio');
51 83
52 // Get the initial status. 84 // Get the initial status.
53 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); 85 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this));
54 86
(...skipping 18 matching lines...) Expand all
73 StateManager.prototype = { 105 StateManager.prototype = {
74 /** 106 /**
75 * Request status details update. Intended to be called from the 107 * Request status details update. Intended to be called from the
76 * hotwordPrivate.onEnabledChanged() event. 108 * hotwordPrivate.onEnabledChanged() event.
77 */ 109 */
78 updateStatus: function() { 110 updateStatus: function() {
79 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); 111 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this));
80 }, 112 },
81 113
82 /** 114 /**
115 * @return {boolean} True if hotwording is enabled.
116 */
117 isEnabled: function() {
118 assert(this.hotwordStatus_);
119 return this.hotwordStatus_.enabled;
120 },
121
122 /**
123 * @return {boolean} True if always-on hotwording is enabled.
124 */
125 isAlwaysOnEnabled: function() {
126 assert(this.hotwordStatus_);
127 return this.hotwordStatus_.enabled &&
128 this.hotwordStatus_.alwaysOnEnabled;
129 },
130
131 /**
83 * Callback for hotwordPrivate.getStatus() function. 132 * Callback for hotwordPrivate.getStatus() function.
84 * @param {chrome.hotwordPrivate.StatusDetails} status Current hotword 133 * @param {chrome.hotwordPrivate.StatusDetails} status Current hotword
85 * status. 134 * status.
86 * @private 135 * @private
87 */ 136 */
88 handleStatus_: function(status) { 137 handleStatus_: function(status) {
89 hotword.debug('New hotword status', status); 138 hotword.debug('New hotword status', status);
90 this.hotwordStatus_ = status; 139 this.hotwordStatus_ = status;
91 this.updateStateFromStatus_(); 140 this.updateStateFromStatus_();
141
142 this.onStatusChanged.dispatch();
92 }, 143 },
93 144
94 /** 145 /**
95 * Updates state based on the current status. 146 * Updates state based on the current status.
96 * @private 147 * @private
97 */ 148 */
98 updateStateFromStatus_: function() { 149 updateStateFromStatus_: function() {
99 if (!this.hotwordStatus_) 150 if (!this.hotwordStatus_)
100 return; 151 return;
101 152
102 if (this.hotwordStatus_.enabled) { 153 if (this.hotwordStatus_.enabled) {
103 // Start the detector if there's a session, and shut it down if there 154 // Start the detector if there's a session, and shut it down if there
104 // isn't. 155 // isn't.
105 // TODO(amistry): Support stacking sessions. This can happen when the
106 // user opens google.com or the NTP, then opens the launcher. Opening
107 // google.com will create one session, and opening the launcher will
108 // create the second. Closing the launcher should re-activate the
109 // google.com session.
110 // NOTE(amistry): With always-on, we want a different behaviour with 156 // NOTE(amistry): With always-on, we want a different behaviour with
111 // sessions since the detector should always be running. The exception 157 // sessions since the detector should always be running. The exception
112 // being when the user triggers by saying 'Ok Google'. In that case, the 158 // being when the user triggers by saying 'Ok Google'. In that case, the
113 // detector stops, so starting/stopping the launcher session should 159 // detector stops, so starting/stopping the launcher session should
114 // restart the detector. 160 // restart the detector.
115 if (this.sessionSource_) 161 if (this.sessions_.length)
116 this.startDetector_(); 162 this.startDetector_();
117 else 163 else
118 this.shutdownDetector_(); 164 this.shutdownDetector_();
119 } else { 165 } else {
120 // Not enabled. Shut down if running. 166 // Not enabled. Shut down if running.
121 this.shutdownDetector_(); 167 this.shutdownDetector_();
122 } 168 }
123 }, 169 },
124 170
125 /** 171 /**
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 * Start the recognizer plugin. Assumes the plugin has been loaded and is 219 * Start the recognizer plugin. Assumes the plugin has been loaded and is
174 * ready to start. 220 * ready to start.
175 * @private 221 * @private
176 */ 222 */
177 startRecognizer_: function() { 223 startRecognizer_: function() {
178 assert(this.pluginManager_, 'No NaCl plugin loaded'); 224 assert(this.pluginManager_, 'No NaCl plugin loaded');
179 if (this.state_ != State_.RUNNING) { 225 if (this.state_ != State_.RUNNING) {
180 this.state_ = State_.RUNNING; 226 this.state_ = State_.RUNNING;
181 this.pluginManager_.startRecognizer(); 227 this.pluginManager_.startRecognizer();
182 } 228 }
183 if (this.sessionStartedCb_) { 229 for (var i = 0; i < this.sessions_.length; i++) {
184 this.sessionStartedCb_(); 230 var session = this.sessions_[i];
185 this.sessionStartedCb_ = null; 231 if (session.startedCb) {
232 session.startedCb();
233 session.startedCb = null;
234 }
186 } 235 }
187 }, 236 },
188 237
189 /** 238 /**
190 * Shuts down and removes the plugin manager, if it exists. 239 * Shuts down and removes the plugin manager, if it exists.
191 * @private 240 * @private
192 */ 241 */
193 shutdownPluginManager_: function() { 242 shutdownPluginManager_: function() {
194 if (this.pluginManager_) { 243 if (this.pluginManager_) {
195 this.pluginManager_.shutdown(); 244 this.pluginManager_.shutdown();
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 */ 285 */
237 onTrigger_: function() { 286 onTrigger_: function() {
238 hotword.debug('Hotword triggered!'); 287 hotword.debug('Hotword triggered!');
239 assert(this.pluginManager_, 'No NaCl plugin loaded on trigger'); 288 assert(this.pluginManager_, 'No NaCl plugin loaded on trigger');
240 // Detector implicitly stops when the hotword is detected. 289 // Detector implicitly stops when the hotword is detected.
241 this.state_ = State_.STOPPED; 290 this.state_ = State_.STOPPED;
242 291
243 // Play the chime. 292 // Play the chime.
244 this.chime_.play(); 293 this.chime_.play();
245 294
246 chrome.hotwordPrivate.notifyHotwordRecognition('search', function() {}); 295 // Implicitly clear the top session. A session needs to be started in
247 296 // order to restart the detector.
248 // Implicitly clear the session. A session needs to be started in order to 297 if (this.sessions_.length) {
249 // restart the detector. 298 var session = this.sessions_.pop();
250 this.sessionSource_ = null; 299 if (session.triggerCb)
251 this.sessionStartedCb_ = null; 300 session.triggerCb();
301 }
252 }, 302 },
253 303
254 /** 304 /**
305 * Remove a hotwording session from the given source.
306 * @param {!hotword.constants.SessionSource} source Source of the hotword
307 * session request.
308 * @private
309 */
310 removeSession_: function(source) {
311 for (var i = 0; i < this.sessions_.length; i++) {
312 if (this.sessions_[i].source == source) {
313 this.sessions_.splice(i, 1);
314 break;
315 }
316 }
317 },
318
319 /**
255 * Start a hotwording session. 320 * Start a hotwording session.
256 * @param {!hotword.constants.SessionSource} source Source of the hotword 321 * @param {!hotword.constants.SessionSource} source Source of the hotword
257 * session request. 322 * session request.
258 * @param {!function()} startedCb Callback invoked when the session has 323 * @param {!function()} startedCb Callback invoked when the session has
259 * been started successfully. 324 * been started successfully.
325 * @param {!function()} triggerCb Callback invoked when the hotword has
326 * triggered.
260 */ 327 */
261 startSession: function(source, startedCb) { 328 startSession: function(source, startedCb, triggerCb) {
262 hotword.debug('Starting session for source: ' + source); 329 hotword.debug('Starting session for source: ' + source);
263 this.sessionSource_ = source; 330 this.removeSession_(source);
264 this.sessionStartedCb_ = startedCb; 331 this.sessions_.push(new Session_(source, triggerCb, startedCb));
265 this.updateStateFromStatus_(); 332 this.updateStateFromStatus_();
266 }, 333 },
267 334
268 /** 335 /**
269 * Stops a hotwording session. 336 * Stops a hotwording session.
270 * @param {!hotword.constants.SessionSource} source Source of the hotword 337 * @param {!hotword.constants.SessionSource} source Source of the hotword
271 * session request. 338 * session request.
272 */ 339 */
273 stopSession: function(source) { 340 stopSession: function(source) {
274 hotword.debug('Stopping session for source: ' + source); 341 hotword.debug('Stopping session for source: ' + source);
275 this.sessionSource_ = null; 342 this.removeSession_(source);
276 this.sessionStartedCb_ = null;
277 this.updateStateFromStatus_(); 343 this.updateStateFromStatus_();
278 } 344 }
279 }; 345 };
280 346
281 return { 347 return {
282 StateManager: StateManager 348 StateManager: StateManager
283 }; 349 };
284 }); 350 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698