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

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

Powered by Google App Engine
This is Rietveld 408576698