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

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: Cleanup for review. 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) {
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
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
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
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 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698