| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 var AutomationEvent = chrome.automation.AutomationEvent; | 5 var AutomationEvent = chrome.automation.AutomationEvent; |
| 6 var AutomationNode = chrome.automation.AutomationNode; | 6 var AutomationNode = chrome.automation.AutomationNode; |
| 7 var EventType = chrome.automation.EventType; | 7 var EventType = chrome.automation.EventType; |
| 8 var RoleType = chrome.automation.RoleType; | 8 var RoleType = chrome.automation.RoleType; |
| 9 | 9 |
| 10 /** | 10 /** |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 chrome.automation.getDesktop(function(desktop) { | 61 chrome.automation.getDesktop(function(desktop) { |
| 62 desktop.addEventListener( | 62 desktop.addEventListener( |
| 63 EventType.MOUSE_PRESSED, this.onMousePressed_.bind(this), true); | 63 EventType.MOUSE_PRESSED, this.onMousePressed_.bind(this), true); |
| 64 desktop.addEventListener( | 64 desktop.addEventListener( |
| 65 EventType.MOUSE_DRAGGED, this.onMouseDragged_.bind(this), true); | 65 EventType.MOUSE_DRAGGED, this.onMouseDragged_.bind(this), true); |
| 66 desktop.addEventListener( | 66 desktop.addEventListener( |
| 67 EventType.MOUSE_RELEASED, this.onMouseReleased_.bind(this), true); | 67 EventType.MOUSE_RELEASED, this.onMouseReleased_.bind(this), true); |
| 68 desktop.addEventListener( | 68 desktop.addEventListener( |
| 69 EventType.MOUSE_CANCELED, this.onMouseCanceled_.bind(this), true); | 69 EventType.MOUSE_CANCELED, this.onMouseCanceled_.bind(this), true); |
| 70 }.bind(this)); | 70 }.bind(this)); |
| 71 |
| 72 /** @private { ?string } */ |
| 73 this.voiceNameFromPrefs_ = null; |
| 74 |
| 75 /** @private { ?string } */ |
| 76 this.voiceNameFromLocale_ = null; |
| 77 |
| 78 /** @private { Set<string> } */ |
| 79 this.validVoiceNames_ = new Set(); |
| 80 |
| 81 /** @private { number } */ |
| 82 this.speechRate_ = 1.0; |
| 83 |
| 84 this.initPreferences_(); |
| 71 }; | 85 }; |
| 72 | 86 |
| 73 SelectToSpeak.prototype = { | 87 SelectToSpeak.prototype = { |
| 74 /** | 88 /** |
| 75 * Called when the mouse is pressed and the user is in a mode where | 89 * Called when the mouse is pressed and the user is in a mode where |
| 76 * select-to-speak is capturing mouse events (for example holding down | 90 * select-to-speak is capturing mouse events (for example holding down |
| 77 * Search). | 91 * Search). |
| 78 * | 92 * |
| 79 * @param {!AutomationEvent} evt | 93 * @param {!AutomationEvent} evt |
| 80 */ | 94 */ |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 | 196 |
| 183 /** | 197 /** |
| 184 * Enqueue speech commands for all of the given nodes. | 198 * Enqueue speech commands for all of the given nodes. |
| 185 * @param {Array<AutomationNode>} nodes The nodes to speak. | 199 * @param {Array<AutomationNode>} nodes The nodes to speak. |
| 186 */ | 200 */ |
| 187 startSpeechQueue_: function(nodes) { | 201 startSpeechQueue_: function(nodes) { |
| 188 chrome.tts.stop(); | 202 chrome.tts.stop(); |
| 189 for (var i = 0; i < nodes.length; i++) { | 203 for (var i = 0; i < nodes.length; i++) { |
| 190 var node = nodes[i]; | 204 var node = nodes[i]; |
| 191 var isLast = (i == nodes.length - 1); | 205 var isLast = (i == nodes.length - 1); |
| 192 chrome.tts.speak(node.name || '', { | 206 |
| 193 lang: 'en-US', | 207 var options = { |
| 208 rate: this.rate_, |
| 194 'enqueue': true, | 209 'enqueue': true, |
| 195 onEvent: (function(node, isLast, event) { | 210 onEvent: (function(node, isLast, event) { |
| 196 if (event.type == 'start') { | 211 if (event.type == 'start') { |
| 197 chrome.accessibilityPrivate.setFocusRing([node.location]); | 212 chrome.accessibilityPrivate.setFocusRing([node.location]); |
| 198 } else if (event.type == 'interrupted' || | 213 } else if (event.type == 'interrupted' || |
| 199 event.type == 'cancelled') { | 214 event.type == 'cancelled') { |
| 200 chrome.accessibilityPrivate.setFocusRing([]); | 215 chrome.accessibilityPrivate.setFocusRing([]); |
| 201 } else if (event.type == 'end') { | 216 } else if (event.type == 'end') { |
| 202 if (isLast) { | 217 if (isLast) { |
| 203 chrome.accessibilityPrivate.setFocusRing([]); | 218 chrome.accessibilityPrivate.setFocusRing([]); |
| 204 } | 219 } |
| 205 } | 220 } |
| 206 }).bind(this, node, isLast) | 221 }).bind(this, node, isLast) |
| 222 }; |
| 223 |
| 224 // Pick the voice name from prefs first, or the one that matches |
| 225 // the locale next, but don't pick a voice that isn't currently |
| 226 // loaded. If no voices are found, leave the voiceName option |
| 227 // unset to let the browser try to route the speech request |
| 228 // anyway if possible. |
| 229 console.log('Pref: ' + this.voiceNameFromPrefs_); |
| 230 console.log('Locale: ' + this.voiceNameFromLocale_); |
| 231 var valid = ''; |
| 232 this.validVoiceNames_.forEach(function(voiceName) { |
| 233 if (valid) |
| 234 valid += ','; |
| 235 valid += voiceName; |
| 207 }); | 236 }); |
| 237 console.log('Valid: ' + valid); |
| 238 if (this.voiceNameFromPrefs_ && |
| 239 this.validVoiceNames_.has(this.voiceNameFromPrefs_)) { |
| 240 options['voiceName'] = this.voiceNameFromPrefs_; |
| 241 } else if (this.voiceNameFromLocale_ && |
| 242 this.validVoiceNames_.has(this.voiceNameFromLocale_)) { |
| 243 options['voiceName'] = this.voiceNameFromLocale_; |
| 244 } |
| 245 |
| 246 chrome.tts.speak(node.name || '', options); |
| 208 } | 247 } |
| 248 }, |
| 249 |
| 250 /** |
| 251 * Loads preferences from chrome.storage, sets default values if |
| 252 * necessary, and registers a listener to update prefs when they |
| 253 * change. |
| 254 */ |
| 255 initPreferences_: function() { |
| 256 var updatePrefs = (function() { |
| 257 chrome.storage.sync.get(['voice', 'rate'], (function(prefs) { |
| 258 if (prefs['voice']) { |
| 259 this.voiceNameFromPrefs_ = prefs['voice']; |
| 260 } |
| 261 if (prefs['rate']) { |
| 262 this.rate_ = parseFloat(prefs['rate']); |
| 263 } else { |
| 264 chrome.storage.sync.set({'rate': this.rate_}); |
| 265 } |
| 266 }).bind(this)); |
| 267 }).bind(this); |
| 268 |
| 269 updatePrefs(); |
| 270 chrome.storage.onChanged.addListener(updatePrefs); |
| 271 |
| 272 this.updateDefaultVoice_(); |
| 273 window.speechSynthesis.onvoiceschanged = (function() { |
| 274 this.updateDefaultVoice_(); |
| 275 }).bind(this); |
| 276 }, |
| 277 |
| 278 /** |
| 279 * Get the list of TTS voices, and set the default voice if not already set. |
| 280 */ |
| 281 updateDefaultVoice_: function() { |
| 282 console.log('updateDefaultVoice_ ' + this.down_); |
| 283 var uiLocale = chrome.i18n.getMessage('@@ui_locale'); |
| 284 uiLocale = uiLocale.replace('_', '-').toLowerCase(); |
| 285 |
| 286 chrome.tts.getVoices((function(voices) { |
| 287 console.log('updateDefaultVoice_ voices: ' + voices.length); |
| 288 this.validVoiceNames_ = new Set(); |
| 289 |
| 290 if (voices.length == 0) |
| 291 return; |
| 292 |
| 293 voices.forEach((function(voice) { |
| 294 this.validVoiceNames_.add(voice.voiceName); |
| 295 }).bind(this)); |
| 296 |
| 297 voices.sort(function(a, b) { |
| 298 function score(voice) { |
| 299 var lang = voice.lang.toLowerCase(); |
| 300 var s = 0; |
| 301 if (lang == uiLocale) |
| 302 s += 2; |
| 303 if (lang.substr(0, 2) == uiLocale.substr(0, 2)) |
| 304 s += 1; |
| 305 return s; |
| 306 } |
| 307 return score(b) - score(a); |
| 308 }); |
| 309 |
| 310 this.voiceNameFromLocale_ = voices[0].voiceName; |
| 311 |
| 312 chrome.storage.sync.get(['voice'], (function(prefs) { |
| 313 if (!prefs['voice']) { |
| 314 chrome.storage.sync.set({'voice': voices[0].voiceName}); |
| 315 } |
| 316 }).bind(this)); |
| 317 }).bind(this)); |
| 209 } | 318 } |
| 210 }; | 319 }; |
| 211 | |
| 212 new SelectToSpeak(); | |
| OLD | NEW |