OLD | NEW |
---|---|
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 (function() { | 5 (function() { |
6 | 6 |
7 // Correspond to steps in the hotword opt-in flow. | 7 // Correspond to steps in the hotword opt-in flow. |
8 /** @const */ var HOTWORD_AUDIO_HISTORY = 'hotword-audio-history-container'; | 8 /** @const */ var HOTWORD_AUDIO_HISTORY = 'hotword-audio-history-container'; |
9 /** @const */ var HOTWORD_ONLY_START = 'hotword-only-container'; | 9 /** @const */ var HOTWORD_ONLY_START = 'hotword-only-container'; |
10 /** @const */ var AUDIO_HISTORY_START = 'audio-history-container'; | 10 /** @const */ var AUDIO_HISTORY_START = 'audio-history-container'; |
11 /** @const */ var SPEECH_TRAINING = 'speech-training-container'; | 11 /** @const */ var SPEECH_TRAINING = 'speech-training-container'; |
12 /** @const */ var FINISHED = 'finished-container'; | 12 /** @const */ var FINISHED = 'finished-container'; |
13 | 13 |
14 /** | 14 /** |
15 * These flows correspond to the three LaunchModes as defined in | 15 * These flows correspond to the three LaunchModes as defined in |
16 * chrome/browser/search/hotword_service.h and should be kept in sync | 16 * chrome/browser/search/hotword_service.h and should be kept in sync |
17 * with them. | 17 * with them. |
18 * @const | 18 * @const |
19 */ | 19 */ |
20 var FLOWS = [ | 20 var FLOWS = [ |
21 // TODO(kcarattini): Remove the first flow, since we will not be | |
22 // managing the Audio History Setting in Chrome anymore. | |
23 [AUDIO_HISTORY_START], | |
24 [HOTWORD_ONLY_START, FINISHED], | 21 [HOTWORD_ONLY_START, FINISHED], |
25 [HOTWORD_AUDIO_HISTORY, SPEECH_TRAINING, FINISHED], | 22 [HOTWORD_AUDIO_HISTORY, SPEECH_TRAINING, FINISHED], |
26 [SPEECH_TRAINING, FINISHED] | 23 [SPEECH_TRAINING, FINISHED] |
27 ]; | 24 ]; |
28 | 25 |
29 /** | 26 /** |
27 * The launch mode. This enum needs to be kept in sync with that of | |
28 * the same name in hotword_service.h. | |
29 * @enum {number} | |
30 */ | |
31 var LaunchMode = { | |
32 HOTWORD_ONLY: 0, | |
33 HOTWORD_AND_AUDIO_HISTORY: 1, | |
34 RETRAIN: 2 | |
35 }; | |
36 | |
37 /** | |
30 * Class to control the page flow of the always-on hotword and | 38 * Class to control the page flow of the always-on hotword and |
31 * Audio History opt-in process. | 39 * Audio History opt-in process. |
32 * @constructor | 40 * @constructor |
33 */ | 41 */ |
34 function Flow() { | 42 function Flow() { |
35 this.currentStepIndex_ = -1; | 43 this.currentStepIndex_ = -1; |
36 this.currentFlow_ = []; | 44 this.currentFlow_ = []; |
45 | |
46 /** | |
47 * Whether this flow is currently in the process of training a voice model. | |
48 * @private {LaunchMode} | |
49 */ | |
50 this.launchMode_ = LaunchMode.HOTWORD_AND_AUDIO_HISTORY; | |
51 | |
52 /** | |
53 * Whether this flow is currently in the process of training a voice model. | |
54 * @private {boolean} | |
55 */ | |
56 this.training_ = false; | |
57 | |
58 /** | |
59 * Prefix of the element ids for the page that is currently training. | |
60 * @private {string} | |
61 */ | |
62 this.trainingPagePrefix_ = ''; | |
37 } | 63 } |
38 | 64 |
39 /** | 65 /** |
40 * Advances the current step. | 66 * Advances the current step. |
41 */ | 67 */ |
42 Flow.prototype.advanceStep = function() { | 68 Flow.prototype.advanceStep = function() { |
Dan Beam
2014/10/30 02:57:55
what happens if this is called while training? do
kcarattini
2014/10/30 05:56:18
It shouldn't matter if this is called while traini
| |
43 this.currentStepIndex_++; | 69 this.currentStepIndex_++; |
44 if (this.currentStepIndex_ < this.currentFlow_.length) | 70 if (this.currentStepIndex_ < this.currentFlow_.length) |
45 this.showStep_.apply(this); | 71 this.showStep_.apply(this); |
46 }; | 72 }; |
47 | 73 |
48 /** | 74 /** |
49 * Gets the appropriate flow and displays its first page. | 75 * Gets the appropriate flow and displays its first page. |
50 */ | 76 */ |
51 Flow.prototype.startFlow = function() { | 77 Flow.prototype.startFlow = function() { |
52 if (chrome.hotwordPrivate && chrome.hotwordPrivate.getLaunchState) | 78 if (chrome.hotwordPrivate && chrome.hotwordPrivate.getLaunchState) |
53 chrome.hotwordPrivate.getLaunchState(this.startFlowForMode_.bind(this)); | 79 chrome.hotwordPrivate.getLaunchState(this.startFlowForMode_.bind(this)); |
54 }; | 80 }; |
55 | 81 |
82 /** | |
83 * Starts the training process. | |
84 */ | |
85 Flow.prototype.startTraining = function() { | |
Dan Beam
2014/10/30 02:57:54
should we check that !this.training_?
kcarattini
2014/10/30 05:56:17
Done.
| |
86 this.training_ = true; | |
87 if (this.launchMode_ == LaunchMode.HOTWORD_ONLY || | |
88 this.launchMode_ == LaunchMode.RETRAIN) { | |
89 this.trainingPagePrefix_ = 'hotword-only'; | |
90 } | |
91 if (this.launchMode_ == LaunchMode.HOTWORD_AND_AUDIO_HISTORY) | |
92 this.trainingPagePrefix_ = 'speech-training'; | |
93 | |
94 if (chrome.hotwordPrivate.onHotwordTriggered) | |
Dan Beam
2014/10/30 02:57:55
curlies
kcarattini
2014/10/30 05:56:18
Done.
| |
95 chrome.hotwordPrivate.onHotwordTriggered.addListener( | |
96 this.handleHotwordTrigger.bind(this)); | |
97 if (chrome.hotwordPrivate.startTraining) | |
98 chrome.hotwordPrivate.startTraining(); | |
99 }; | |
100 | |
101 /** | |
102 * Stops the training process. | |
103 */ | |
104 Flow.prototype.stopTraining = function() { | |
Dan Beam
2014/10/30 02:57:54
should this actually be a check to ensure this.tra
kcarattini
2014/10/30 05:56:18
No, since this is called from all events that clos
| |
105 if (this.training_) { | |
106 this.training_ = false; | |
107 if (chrome.hotwordPrivate.onHotwordTriggered) { | |
108 chrome.hotwordPrivate.onHotwordTriggered. | |
109 removeListener(this.handleHotwordTrigger); | |
110 } | |
111 if (chrome.hotwordPrivate.stopTraining) | |
112 chrome.hotwordPrivate.stopTraining(); | |
113 } | |
114 }; | |
115 | |
116 /** | |
117 * Handles the speaker model finalized event. | |
118 */ | |
119 Flow.prototype.onSpeakerModelFinalized = function() { | |
120 this.stopTraining(); | |
121 | |
122 if (chrome.hotwordPrivate.setAudioLoggingEnabled) | |
123 chrome.hotwordPrivate.setAudioLoggingEnabled(true, function() {}); | |
124 | |
125 if (chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled) { | |
126 chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled(true, | |
127 this.advanceStep.bind(this)); | |
128 } | |
129 }; | |
130 | |
131 /** | |
132 * Completes the training process. | |
Dan Beam
2014/10/30 02:57:54
@private, finalizeSpeakerModel_
kcarattini
2014/10/30 05:56:17
Done.
| |
133 */ | |
134 Flow.prototype.finalizeSpeakerModel = function() { | |
135 if (this.training_) { | |
Dan Beam
2014/10/30 02:57:54
nit:
if (!this.training_)
return;
kcarattini
2014/10/30 05:56:18
Done.
| |
136 if (chrome.hotwordPrivate.finalizeSpeakerModel) | |
137 chrome.hotwordPrivate.finalizeSpeakerModel(); | |
138 | |
139 // TODO(kcarattini): Implement a notification that speaker model has been | |
140 // finalized instead of setting a timeout. | |
141 setTimeout(this.onSpeakerModelFinalized.bind(this), 2000); | |
142 } | |
143 }; | |
144 | |
145 /** | |
146 * Handles a hotword trigger event and updates the training UI. | |
Dan Beam
2014/10/30 02:57:54
handleHotwordTrigger_, @private
kcarattini
2014/10/30 05:56:17
Done.
| |
147 */ | |
148 Flow.prototype.handleHotwordTrigger = function() { | |
149 var curStep = | |
150 $(this.trainingPagePrefix_ + '-training').querySelector('.listening'); | |
151 // TODO(kcarattini): Localize this string. | |
152 curStep.querySelector('.text').textContent = 'Recorded'; | |
153 curStep.classList.remove('listening'); | |
154 curStep.classList.add('recorded'); | |
155 | |
156 var steps = | |
157 $(this.trainingPagePrefix_ + '-training').querySelectorAll('.train'); | |
158 var index = Array.prototype.indexOf.call(steps, curStep); | |
159 if (steps[index + 1]) { | |
160 steps[index + 1].classList.remove('not-started'); | |
161 steps[index + 1].classList.add('listening'); | |
162 return; | |
163 } | |
Dan Beam
2014/10/30 02:57:54
// Only the last step makes it here.
(or somethin
kcarattini
2014/10/30 05:56:17
Done.
| |
164 | |
165 // Update the 'Cancel' button. | |
166 var buttonElem = $(this.trainingPagePrefix_ + '-cancel-button'); | |
167 // TODO(kcarattini): Localize this string. | |
168 buttonElem.textContent = 'Please wait ...'; | |
169 buttonElem.classList.add('grayed-out'); | |
170 buttonElem.classList.remove('finish-button'); | |
171 | |
172 this.finalizeSpeakerModel(); | |
173 }; | |
174 | |
56 // ---- private methods: | 175 // ---- private methods: |
57 | 176 |
58 /** | 177 /** |
59 * Gets and starts the appropriate flow for the launch mode. | 178 * Gets and starts the appropriate flow for the launch mode. |
Dan Beam
2014/10/30 02:57:54
@param {WhateverTypeStateIs} state Doc comment abo
kcarattini
2014/10/30 05:56:18
Done.
| |
60 * @private | 179 * @private |
61 */ | 180 */ |
62 Flow.prototype.startFlowForMode_ = function(state) { | 181 Flow.prototype.startFlowForMode_ = function(state) { |
182 this.launchMode_ = state.launchMode; | |
63 assert(state.launchMode >= 0 && state.launchMode < FLOWS.length, | 183 assert(state.launchMode >= 0 && state.launchMode < FLOWS.length, |
64 'Invalid Launch Mode.'); | 184 'Invalid Launch Mode.'); |
65 this.currentFlow_ = FLOWS[state.launchMode]; | 185 this.currentFlow_ = FLOWS[state.launchMode]; |
66 this.advanceStep(); | 186 this.advanceStep(); |
187 // If the flow begins with a a training step, then start the training flow. | |
188 if (state.launchMode == LaunchMode.HOTWORD_ONLY || | |
189 state.launchMode == LaunchMode.SPEECH_TRAINING) | |
Dan Beam
2014/10/30 02:57:54
curlies
kcarattini
2014/10/30 05:56:17
Done.
| |
190 this.startTraining(); | |
67 }; | 191 }; |
68 | 192 |
69 /** | 193 /** |
70 * Displays the current step. If the current step is not the first step, | 194 * Displays the current step. If the current step is not the first step, |
71 * also hides the previous step. | 195 * also hides the previous step. |
72 * @private | 196 * @private |
73 */ | 197 */ |
74 Flow.prototype.showStep_ = function() { | 198 Flow.prototype.showStep_ = function() { |
75 var currentStep = this.currentFlow_[this.currentStepIndex_]; | 199 var currentStep = this.currentFlow_[this.currentStepIndex_]; |
76 var previousStep = null; | 200 var previousStep = null; |
77 if (this.currentStepIndex_ > 0) | 201 if (this.currentStepIndex_ > 0) |
78 previousStep = this.currentFlow_[this.currentStepIndex_ - 1]; | 202 previousStep = this.currentFlow_[this.currentStepIndex_ - 1]; |
79 | 203 |
80 if (previousStep) | 204 if (previousStep) |
81 document.getElementById(previousStep).hidden = true; | 205 document.getElementById(previousStep).hidden = true; |
82 | 206 |
83 document.getElementById(currentStep).hidden = false; | 207 document.getElementById(currentStep).hidden = false; |
84 }; | 208 }; |
85 | 209 |
86 window.Flow = Flow; | 210 window.Flow = Flow; |
87 })(); | 211 })(); |
OLD | NEW |