Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / app_list / speech_manager.js
index c37d1a1..74f4668 100644 (file)
@@ -6,9 +6,9 @@
  * @fileoverview The class to Manage both offline / online speech recognition.
  */
 
-<include src="plugin_manager.js"/>
-<include src="audio_manager.js"/>
-<include src="speech_recognition_manager.js"/>
+<include src="plugin_manager.js">
+<include src="audio_manager.js">
+<include src="speech_recognition_manager.js">
 
 cr.define('speech', function() {
   'use strict';
@@ -23,18 +23,41 @@ cr.define('speech', function() {
     HOTWORD_RECOGNIZING: 'HOTWORD_RECOGNIZING',
     RECOGNIZING: 'RECOGNIZING',
     IN_SPEECH: 'IN_SPEECH',
-    STOPPING: 'STOPPING'
+    STOPPING: 'STOPPING',
+    NETWORK_ERROR: 'NETWORK_ERROR'
   };
 
   /**
+   * The time to show the network error message in seconds.
+   *
+   * @const {number}
+   */
+  var SPEECH_ERROR_TIMEOUT = 3;
+
+  /**
+   * Checks the prefix for the hotword module based on the language. This is
+   * fragile if the file structure has changed.
+   */
+  function getHotwordPrefix() {
+    var prefix = navigator.language.toLowerCase();
+    if (prefix == 'en-gb')
+      return prefix;
+    var hyphen = prefix.indexOf('-');
+    if (hyphen >= 0)
+      prefix = prefix.substr(0, hyphen);
+    if (prefix == 'en')
+      prefix = '';
+    return prefix;
+  }
+
+  /**
    * @constructor
    */
   function SpeechManager() {
     this.audioManager_ = new speech.AudioManager();
     this.audioManager_.addEventListener('audio', this.onAudioLevel_.bind(this));
-    this.shown_ = false;
     this.speechRecognitionManager_ = new speech.SpeechRecognitionManager(this);
-    this.setState_(SpeechState.READY);
+    this.errorTimeoutId_ = null;
   }
 
   /**
@@ -77,15 +100,19 @@ cr.define('speech', function() {
     this.pluginManager_ = pluginManager;
     this.audioManager_.addEventListener(
         'audio', pluginManager.sendAudioData.bind(pluginManager));
-    if (this.shown_) {
-      this.pluginManager_.startRecognizer();
-      this.audioManager_.start();
-      this.setState_(SpeechState.HOTWORD_RECOGNIZING);
-    } else {
-      this.pluginManager_.stopRecognizer();
-      this.setState_(SpeechState.READY);
-    }
-    chrome.send('setHotwordRecognizerState', [true]);
+    this.pluginManager_.startRecognizer();
+    this.audioManager_.start();
+    this.setState_(SpeechState.HOTWORD_RECOGNIZING);
+  };
+
+  /**
+   * Called when an error happens for loading the hotword recognizer.
+   *
+   * @private
+   */
+  SpeechManager.prototype.onHotwordRecognizerLoadError_ = function() {
+    this.setHotwordEnabled(false);
+    this.setState_(SpeechState.READY);
   };
 
   /**
@@ -123,6 +150,12 @@ cr.define('speech', function() {
    * Called when the speech recognition has ended.
    */
   SpeechManager.prototype.onSpeechRecognitionEnded = function() {
+    // Do not handle the speech recognition ends if it ends due to an error
+    // because an error message should be shown for a while.
+    // See onSpeechRecognitionError.
+    if (this.state == SpeechState.NETWORK_ERROR)
+      return;
+
     // Restarts the hotword recognition.
     if (this.state != SpeechState.STOPPING && this.pluginManager_) {
       this.pluginManager_.startRecognizer();
@@ -151,13 +184,31 @@ cr.define('speech', function() {
   };
 
   /**
+   * Called when the speech manager should recover from the error state.
+   *
+   * @private
+   */
+  SpeechManager.prototype.onSpeechRecognitionErrorTimeout_ = function() {
+    this.errorTimeoutId_ = null;
+    this.setState_(SpeechState.READY);
+    this.onSpeechRecognitionEnded();
+  };
+
+  /**
    * Called when an error happened during the speech recognition.
    *
    * @param {SpeechRecognitionError} e The error object.
    */
   SpeechManager.prototype.onSpeechRecognitionError = function(e) {
-    if (this.state != SpeechState.STOPPING)
-      this.setState_(SpeechState.READY);
+    if (e.error == 'network') {
+      this.setState_(SpeechState.NETWORK_ERROR);
+      this.errorTimeoutId_ = window.setTimeout(
+          this.onSpeechRecognitionErrorTimeout_.bind(this),
+          SPEECH_ERROR_TIMEOUT * 1000);
+    } else {
+      if (this.state != SpeechState.STOPPING)
+        this.setState_(SpeechState.READY);
+    }
   };
 
   /**
@@ -170,69 +221,58 @@ cr.define('speech', function() {
     if (enabled) {
       if (recognizer)
         return;
+      if (!this.naclArch)
+        return;
 
+      var prefix = getHotwordPrefix();
       var pluginManager = new speech.PluginManager(
+          prefix,
           this.onHotwordRecognizerReady_.bind(this),
-          this.onHotwordRecognized_.bind(this));
-      pluginManager.scheduleInitialize(
-          this.audioManager_.sampleRate, 'chrome://app-list/hotword.data');
+          this.onHotwordRecognized_.bind(this),
+          this.onHotwordRecognizerLoadError_.bind(this));
+      var modelUrl = 'chrome://app-list/_platform_specific/' + this.naclArch +
+          '_' + prefix + '/hotword.data';
+      pluginManager.scheduleInitialize(this.audioManager_.sampleRate, modelUrl);
     } else {
       if (!recognizer)
         return;
       document.body.removeChild(recognizer);
       this.pluginManager_ = null;
-      chrome.send('setHotwordRecognizerState', [false]);
-      if (this.state == SpeechState.HOTWORD_RECOGNIZING)
-        this.setState(SpeechState.READY);
+      if (this.state == SpeechState.HOTWORD_RECOGNIZING) {
+        this.audioManager_.stop();
+        this.setState_(SpeechState.READY);
+      }
     }
   };
 
   /**
-   * Starts the hotword recognizer.
-   */
-  SpeechManager.prototype.startHotwordRecognition = function() {
-    if (!this.pluginManager_)
-      return;
-
-    if (this.state == SpeechState.HOTWORD_RECOGNIZING)
-      return;
-
-    this.pluginManager_.startRecognizer();
-    this.audioManager_.start();
-    this.setState_(SpeechState.HOTWORD_RECOGNIZING);
-  };
-
-  /**
-   * Stops the hotword recognizer.
+   * Sets the NaCl architecture for the hotword module.
+   *
+   * @param {string} arch The architecture.
    */
-  SpeechManager.prototype.stopHotwordRecognition = function() {
-    if (!this.pluginManager_)
-      return;
-
-    if (this.state != SpeechState.HOTWORD_RECOGNIZING)
-      return;
-
-    this.pluginManager_.stopRecognizer();
-    this.audioManager_.stop();
-    this.setState_(SpeechState.READY);
+  SpeechManager.prototype.setNaclArch = function(arch) {
+    this.naclArch = arch;
   };
 
   /**
    * Called when the app-list bubble is shown.
+   *
+   * @param {boolean} hotwordEnabled Whether the hotword is enabled or not.
    */
-  SpeechManager.prototype.onShown = function() {
-    this.shown_ = true;
-    if (this.state == SpeechState.READY)
-      this.startHotwordRecognition();
+  SpeechManager.prototype.onShown = function(hotwordEnabled) {
+    this.setHotwordEnabled(hotwordEnabled);
+
+    // No one sets the state if the content is initialized on shown but hotword
+    // is not enabled. Sets the state in such case.
+    if (!this.state && !hotwordEnabled)
+      this.setState_(SpeechState.READY);
   };
 
   /**
    * Called when the app-list bubble is hidden.
    */
   SpeechManager.prototype.onHidden = function() {
-    this.shown_ = false;
-    if (this.pluginManager_)
-      this.pluginManager_.stopRecognizer();
+    this.setHotwordEnabled(false);
 
     // SpeechRecognition is asynchronous.
     this.audioManager_.stop();
@@ -249,7 +289,11 @@ cr.define('speech', function() {
    * Toggles the current state of speech recognition.
    */
   SpeechManager.prototype.toggleSpeechRecognition = function() {
-    if (this.state == SpeechState.RECOGNIZING ||
+    if (this.state == SpeechState.NETWORK_ERROR) {
+      if (this.errorTimeoutId_)
+        window.clearTimeout(this.errorTimeoutId_);
+      this.onSpeechRecognitionErrorTimeout_();
+    } else if (this.state == SpeechState.RECOGNIZING ||
         this.state == SpeechState.IN_SPEECH) {
       this.audioManager_.stop();
       this.speechRecognitionManager_.stop();