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
3 // found in the LICENSE file.
7 // Correspond to steps in the hotword opt-in flow.
8 /** @const */ var HOTWORD_AUDIO_HISTORY = 'hotword-audio-history-container';
9 /** @const */ var HOTWORD_ONLY_START = 'hotword-only-container';
10 /** @const */ var SPEECH_TRAINING = 'speech-training-container';
11 /** @const */ var FINISHED = 'finished-container';
14 * These flows correspond to the three LaunchModes as defined in
15 * chrome/browser/search/hotword_service.h and should be kept in sync
20 [HOTWORD_ONLY_START, FINISHED],
21 [HOTWORD_AUDIO_HISTORY, SPEECH_TRAINING, FINISHED],
22 [SPEECH_TRAINING, FINISHED]
26 * The launch mode. This enum needs to be kept in sync with that of
27 * the same name in hotword_service.h.
32 HOTWORD_AND_AUDIO_HISTORY: 1,
37 * Class to control the page flow of the always-on hotword and
38 * Audio History opt-in process.
42 this.currentStepIndex_ = -1;
43 this.currentFlow_ = [];
46 * Whether this flow is currently in the process of training a voice model.
47 * @private {LaunchMode}
49 this.launchMode_ = LaunchMode.HOTWORD_AND_AUDIO_HISTORY;
52 * Whether this flow is currently in the process of training a voice model.
55 this.training_ = false;
58 * Prefix of the element ids for the page that is currently training.
61 this.trainingPagePrefix_ = '';
65 * Advances the current step.
67 Flow.prototype.advanceStep = function() {
68 this.currentStepIndex_++;
69 if (this.currentStepIndex_ < this.currentFlow_.length)
70 this.showStep_.apply(this);
74 * Gets the appropriate flow and displays its first page.
76 Flow.prototype.startFlow = function() {
77 if (chrome.hotwordPrivate && chrome.hotwordPrivate.getLaunchState)
78 chrome.hotwordPrivate.getLaunchState(this.startFlowForMode_.bind(this));
82 * Starts the training process.
84 Flow.prototype.startTraining = function() {
85 // Don't start a training session if one already exists.
89 this.training_ = true;
90 if (this.launchMode_ == LaunchMode.HOTWORD_ONLY) {
91 this.trainingPagePrefix_ = 'hotword-only';
92 } else if (this.launchMode_ == LaunchMode.HOTWORD_AND_AUDIO_HISTORY ||
93 this.launchMode_ == LaunchMode.RETRAIN) {
94 this.trainingPagePrefix_ = 'speech-training';
97 if (chrome.hotwordPrivate.onHotwordTriggered) {
98 chrome.hotwordPrivate.onHotwordTriggered.addListener(
99 this.handleHotwordTrigger_.bind(this));
101 if (chrome.hotwordPrivate.startTraining)
102 chrome.hotwordPrivate.startTraining();
106 * Stops the training process.
108 Flow.prototype.stopTraining = function() {
112 this.training_ = false;
113 if (chrome.hotwordPrivate.onHotwordTriggered) {
114 chrome.hotwordPrivate.onHotwordTriggered.
115 removeListener(this.handleHotwordTrigger_);
117 if (chrome.hotwordPrivate.stopTraining)
118 chrome.hotwordPrivate.stopTraining();
122 * Handles the speaker model finalized event.
124 Flow.prototype.onSpeakerModelFinalized = function() {
127 if (chrome.hotwordPrivate.setAudioLoggingEnabled)
128 chrome.hotwordPrivate.setAudioLoggingEnabled(true, function() {});
130 if (chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled) {
131 chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled(true,
132 this.advanceStep.bind(this));
136 // ---- private methods:
139 * Completes the training process.
142 Flow.prototype.finalizeSpeakerModel_ = function() {
146 if (chrome.hotwordPrivate.finalizeSpeakerModel)
147 chrome.hotwordPrivate.finalizeSpeakerModel();
149 // TODO(kcarattini): Implement a notification that speaker model has been
150 // finalized instead of setting a timeout.
151 setTimeout(this.onSpeakerModelFinalized.bind(this), 2000);
155 * Handles a hotword trigger event and updates the training UI.
158 Flow.prototype.handleHotwordTrigger_ = function() {
160 $(this.trainingPagePrefix_ + '-training').querySelector('.listening');
161 // TODO(kcarattini): Localize this string.
162 curStep.querySelector('.text').textContent = 'Recorded';
163 curStep.classList.remove('listening');
164 curStep.classList.add('recorded');
167 $(this.trainingPagePrefix_ + '-training').querySelectorAll('.train');
168 var index = Array.prototype.indexOf.call(steps, curStep);
169 if (steps[index + 1]) {
170 steps[index + 1].classList.remove('not-started');
171 steps[index + 1].classList.add('listening');
175 // Only the last step makes it here.
176 var buttonElem = $(this.trainingPagePrefix_ + '-cancel-button');
177 // TODO(kcarattini): Localize this string.
178 buttonElem.textContent = 'Please wait ...';
179 buttonElem.classList.add('grayed-out');
180 buttonElem.classList.remove('finish-button');
182 this.finalizeSpeakerModel_();
186 * Gets and starts the appropriate flow for the launch mode.
187 * @param {chrome.hotwordPrivate.LaunchState} state Launch state of the
188 * Hotword Audio Verification App.
191 Flow.prototype.startFlowForMode_ = function(state) {
192 this.launchMode_ = state.launchMode;
193 assert(state.launchMode >= 0 && state.launchMode < FLOWS.length,
194 'Invalid Launch Mode.');
195 this.currentFlow_ = FLOWS[state.launchMode];
197 // If the flow begins with a a training step, then start the training flow.
198 if (state.launchMode == LaunchMode.HOTWORD_ONLY ||
199 state.launchMode == LaunchMode.RETRAIN) {
200 this.startTraining();
205 * Displays the current step. If the current step is not the first step,
206 * also hides the previous step.
209 Flow.prototype.showStep_ = function() {
210 var currentStep = this.currentFlow_[this.currentStepIndex_];
211 document.getElementById(currentStep).hidden = false;
213 var previousStep = null;
214 if (this.currentStepIndex_ > 0)
215 previousStep = this.currentFlow_[this.currentStepIndex_ - 1];
218 document.getElementById(previousStep).hidden = true;
220 chrome.app.window.current().show();