VoiceRecognitionThread.java
Go to the documentation of this file.
00001 package com.riverlab.robotmanager.voice_recognition;
00002 
00003 import java.util.ArrayList;
00004 import java.util.HashMap;
00005 
00006 import android.bluetooth.BluetoothAdapter;
00007 import android.bluetooth.BluetoothDevice;
00008 import android.content.Context;
00009 import android.content.Intent;
00010 import android.os.Handler;
00011 import android.os.HandlerThread;
00012 import android.os.Message;
00013 import android.os.Parcelable;
00014 
00015 import com.google.glass.input.VoiceInputHelper;
00016 import com.google.glass.input.VoiceListener;
00017 import com.google.glass.logging.FormattingLogger;
00018 import com.google.glass.logging.FormattingLoggers;
00019 import com.google.glass.logging.Log;
00020 import com.google.glass.voice.VoiceCommand;
00021 import com.google.glass.voice.VoiceConfig;
00022 import com.riverlab.robotmanager.MainActivity;
00023 import com.riverlab.robotmanager.RobotManagerApplication;
00024 import com.riverlab.robotmanager.bluetooth.ConnectedThread;
00025 import com.riverlab.robotmanager.robot.Robot;
00026 
00027 /*
00028  * The voice recognition thread inherits the HandlerThread class.
00029  * This thread is used to continuously listen to voice input
00030  * It also manages the current vocabulary the voice listener
00031  * is listening for.  
00032  * 
00033  * Voice recognition is done by the VoiceListenerImpl inner class.
00034  * When a command is recognized the inner class selects the appropriate
00035  * action and performs it.
00036  */
00037 
00038 public class VoiceRecognitionThread extends HandlerThread
00039 {
00040         //Globals
00041         private RobotManagerApplication mApplication;
00042         private BluetoothAdapter mBluetoothAdapter;
00043         private Context mContext;
00044 
00045         //Global flags
00046         private boolean isShutdown = false;
00047         private boolean isListening = true;
00048         private boolean isConnected = false;
00049         private boolean usingSystemCommands = false;
00050         
00051         //Continuous listening
00052         private VoiceInputHelper mVoiceInputHelper;
00053         private VoiceConfig mVoiceConfig;
00054         private ArrayList<String> mSystemCommands;
00055         private ArrayList<String> mConnectCommands;
00056         private ArrayList<String> mVoiceConfigList;
00057         private HashMap<String, Runnable> mActionMap;
00058 
00059         //Thread globals
00060         private VoiceHelperThread mHelper = null;
00061         private Handler mHelperHandler = null;
00062 
00063         //Handler message type constants
00064         public static final int ENABLE_SYSTEM_CMD_MESSAGE = 0;
00065         public static final int ADD_VOCAB_MESSAGE = 1;
00066         public static final int REMOVE_VOCAB_MESSAGE = 2;
00067         public static final int CHANGE_VOCAB_MESSAGE = 3;
00068         public static final int CHANGE_VOCAB_ACTION_MESSAGE = 4;
00069         public static final int RESET_MESSAGE = 5;
00070         public static final int LISTENING_MESSAGE = 6;
00071         public static final int CONTEXT_MESSAGE = 7;
00072         public static final int CONNECTION_MESSAGE = 8;
00073         public static final int UPDATE_MESSAGE = 9;
00074         public static final int SHUTDOWN_MESSAGE = 10;
00075 
00076         //Handlers
00077         private Handler mainHandler;
00078         private Handler connectedHandler;
00079         private Handler mHandler = null;
00080 
00081         /*
00082          * Constructor
00083          */
00084         public VoiceRecognitionThread(RobotManagerApplication app, Context context)
00085         {
00086                 super("Voice Recognition Thread");
00087                 this.mApplication  = app;
00088                 this.mContext = context;
00089         }
00090 
00091         /*
00092          * This function sets up separate ArrayLists of Strings which
00093          * define standard voice commands for the user to use.
00094          * These lists are:
00095          *              - Connect commands: Used to connect to Bluetooth devices
00096          *              - System commands: Used to navigate the app
00097          * In addition, it sets up and starts voice recognition
00098          */
00099         private void setup()
00100         {
00101                 //Standard lists of voice commands
00102                 mSystemCommands = new ArrayList<String>();
00103                 mConnectCommands = new ArrayList<String>();
00104 
00105 
00106                 //Prefix all bluetooth device names with "connect to " to allow the
00107                 //user to connect through voice control and add them to the connect command list
00108                 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
00109                 for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices())
00110                 {
00111                         mConnectCommands.add("Connect to " + device.getName());
00112                 }
00113                 mConnectCommands.add("Close robot manager");
00114 
00115 
00116                 //Hard coded system commands
00117                 mSystemCommands.add("All robots");
00118                 mSystemCommands.add("Start listening");
00119                 mSystemCommands.add("Stop listening");
00120                 mSystemCommands.add("End connection");
00121                 mSystemCommands.add("Close robot manager");
00122                 mSystemCommands.add("View robots");
00123                 //mSystemCommands.add("Create group"); NOT IMPLEMENTED YET
00124                 mSystemCommands.add("View messages");
00125                 //mSystemCommands.add("View map"); NOT IMPLEMENTED YET
00126                 //mSystemCommands.add("Show commands"); NOT IMPLEMENTED YET
00127 
00128 
00129                 //Set up command listener
00130                 mVoiceConfigList = new ArrayList<String>(mConnectCommands);
00131                 mVoiceConfig = new VoiceConfig("MyVoiceConfig", mConnectCommands.toArray(new String[mConnectCommands.size()]));
00132                 mVoiceInputHelper = new VoiceInputHelper(mContext, new VoiceListenerImpl(mVoiceConfig),
00133                                 VoiceInputHelper.newUserActivityObserver(mContext));
00134 
00135                 mVoiceInputHelper.addVoiceServiceListener();
00136         }
00137 
00138         /*
00139          * This function starts the voice recognition thread.  
00140          * It also creates a handler which is associated with this thread's
00141          * looper.
00142          */
00143         @Override
00144         public void start()
00145         {
00146                 super.start();
00147 
00148                 mHandler = new Handler(getLooper()){
00149                         @Override
00150                         public void handleMessage(Message msg) {
00151                                 switch (msg.what) {
00152                                 
00153                                 case ENABLE_SYSTEM_CMD_MESSAGE:
00154                                         Boolean enable = (Boolean)msg.obj;
00155                                         useSystemCommands(enable);
00156                                         break;
00157                                         
00158                                 case ADD_VOCAB_MESSAGE:
00159                                         String addition = (String)msg.obj;
00160                                         addToVocab(addition);
00161                                         break;
00162                                         
00163                                 case REMOVE_VOCAB_MESSAGE:
00164                                         String deletion = (String)msg.obj;
00165                                         removeFromVocab(deletion);
00166                                         break;
00167                                         
00168                                 case CHANGE_VOCAB_MESSAGE:
00169                                         ArrayList<String> newVocab = (ArrayList<String>)msg.obj;
00170                                         changeVocab(newVocab);
00171                                         break;
00172                                         
00173                                 case CHANGE_VOCAB_ACTION_MESSAGE:
00174                                         HashMap<String, Runnable> actionMap = (HashMap<String, Runnable>)msg.obj;
00175                                         changeVocabWithAction(actionMap);
00176                                         break;
00177                                         
00178                                 case RESET_MESSAGE:
00179                                         String resetMessage = (String)msg.obj;
00180                                         reset(resetMessage);
00181                                         break;
00182                                         
00183                                 case LISTENING_MESSAGE:
00184                                         boolean isListening = (Boolean) msg.obj;
00185                                         VoiceRecognitionThread.this.isListening = isListening;
00186                                         break;
00187                                         
00188                                 case CONTEXT_MESSAGE:
00189                                         Context newContext = (Context)msg.obj;
00190                                         setContext(newContext);
00191                                         break;
00192                                         
00193                                 case CONNECTION_MESSAGE:
00194                                         String status = (String)msg.obj;
00195                                         if (status.equals("connected"))
00196                                         {
00197                                                 useSystemCommands(true);
00198                                                 changeVocab(new ArrayList<String>());
00199                                         }
00200                                         else if (status.equals("disconnected"))
00201                                         {
00202                                                 useSystemCommands(false);
00203                                                 changeVocab(mConnectCommands);
00204                                         }
00205                                         break;
00206                                         
00207                                 case UPDATE_MESSAGE:
00208                                         update();
00209                                         break;
00210                                         
00211                                 case SHUTDOWN_MESSAGE:
00212                                         shutdown();
00213                                         break;
00214                                 }
00215 
00216                         }
00217                 };
00218 
00219                 setup();
00220         }
00221 
00222         /*
00223          * This function returns true if the thread has been started and the thread's 
00224          * handler is available
00225          */
00226         public synchronized boolean isReady()
00227         {
00228                 return mHandler != null;
00229         }
00230 
00231         /*
00232          * This function accepts the handlers of the main and connected threads
00233          * and saves them in global variables
00234          */
00235         public void setHandlers(Handler mainHandler, Handler connectedHandler)
00236         {
00237                 this.mainHandler = mainHandler;
00238                 this.connectedHandler = connectedHandler;
00239         }
00240 
00241         /*
00242          * Returns this thread's handler
00243          */
00244         public Handler getHandler()
00245         {
00246                 return mHandler;
00247         }
00248 
00249         /*
00250          * This thread sets the listening status of the 
00251          * voice listener
00252          */
00253         public void setListeningStatus(boolean isListening)
00254         {
00255                 this.isListening = isListening;
00256         }
00257 
00258         /*
00259          * This function returns a list of default commands that the robot
00260          * in focus can understand
00261          */
00262         public ArrayList<String> getDefaultRobotCommands()
00263         {
00264                 ArrayList<String> newVocab = new ArrayList<String>();
00265 
00266                 //Get the instance of the robot in focus
00267                 Robot robot = mApplication.getRobotInFocus();
00268                 
00269                 //If no robot is in focus, get the default commands of all robots
00270                 if (robot == null)
00271                 {
00272                         for (Robot rbt : mApplication.getRobots())
00273                         {
00274                                 newVocab.addAll(rbt.getNextPhrases(new ArrayList<String>()).newVocab);
00275                         }
00276                         return newVocab;
00277                 }
00278                 else
00279                 {
00280                         NewPhrasesMessage msg = robot.getNextPhrases(new ArrayList<String>());
00281                         return msg.newVocab;
00282                 }
00283         }
00284 
00285         /*
00286          * This function accepts a string and adds that string to
00287          * the vocabulary used by the voice listener
00288          */
00289         public void addToVocab(String newVoiceCommand)
00290         {
00291                 Log.d("VoiceThread", "Adding " + newVoiceCommand + " to vocab");
00292 
00293                 //Add the new command to the current list of commands
00294                 mVoiceConfigList.add(newVoiceCommand);
00295 
00296                 Log.d("VoiceThread", "Vocab: " + mVoiceConfigList.toString());
00297 
00298                 //Remove the current voice listener, replace it with a new one which
00299                 //uses the new vocabulary, and restart the listener
00300                 mVoiceInputHelper.removeVoiceServiceListener();
00301                 mVoiceConfig = new VoiceConfig("MyVoiceConfig", mVoiceConfigList.toArray(new String[mVoiceConfigList.size()]));
00302                 mVoiceInputHelper = new VoiceInputHelper(mContext, new VoiceListenerImpl(mVoiceConfig),
00303                                 VoiceInputHelper.newUserActivityObserver(mContext));
00304 
00305                 mVoiceInputHelper.addVoiceServiceListener();
00306         }
00307 
00308         /*
00309          * This function accepts a string and removes that string from
00310          * the vocabulary used by the voice listener
00311          */
00312         public void removeFromVocab(String vcToRemove)
00313         {
00314                 //Remove the provided command from the current list of commands
00315                 mVoiceConfigList.remove(vcToRemove);
00316 
00317                 //Remove the current voice listener, replace it with a new one which
00318                 //uses the new vocabulary, and restart the listener
00319                 mVoiceInputHelper.removeVoiceServiceListener();
00320                 mVoiceConfig = new VoiceConfig("MyVoiceConfig", mVoiceConfigList.toArray(new String[mVoiceConfigList.size()]));
00321                 mVoiceInputHelper = new VoiceInputHelper(mContext, new VoiceListenerImpl(mVoiceConfig),
00322                                 VoiceInputHelper.newUserActivityObserver(mContext));
00323 
00324                 mVoiceInputHelper.addVoiceServiceListener();
00325         }
00326 
00327         /*
00328          * This function accepts an ArrayList of Strings and sets it
00329          * as the current list of accepted commands
00330          */
00331         public void changeVocab(ArrayList<String> newVocab)
00332         {
00333                 Log.i("VoiceThread", "Changing vocab");
00334                 
00335                 //Only update vocab if connected or changing to connection commands
00336                 if (mApplication.getConnectionStatus() || newVocab.equals(mConnectCommands))
00337                 {
00338                         //Empty current list and repopulate using the provided list
00339                         mVoiceConfigList = new ArrayList<String>();
00340                         mVoiceConfigList.addAll(newVocab);
00341                         
00342                         if (usingSystemCommands)
00343                         {
00344                                 mVoiceConfigList.addAll(mSystemCommands);
00345                                 mVoiceConfigList.addAll(mApplication.getRobotNames());
00346                         }
00347                 }
00348                 else //If we are not connected, make sure the connect commands are available
00349                 {
00350                         mVoiceConfigList.addAll(mConnectCommands);
00351                 }
00352 
00353                 //Remove, replace, and restart the voice listener
00354                 mVoiceInputHelper.removeVoiceServiceListener();
00355                 mVoiceConfig = new VoiceConfig("MyVoiceConfig", mVoiceConfigList.toArray(new String[mVoiceConfigList.size()]));
00356                 mVoiceInputHelper = new VoiceInputHelper(mContext, new VoiceListenerImpl(mVoiceConfig),
00357                                 VoiceInputHelper.newUserActivityObserver(mContext));
00358 
00359                 mVoiceInputHelper.addVoiceServiceListener();
00360 
00361                 Log.d("VoiceRecognitionThread", "New listening list: " + mVoiceConfigList.toString());
00362 
00363         }
00364 
00365         /*
00366          * This function accepts a hash map with keys of type String and values of type Runnable
00367          * The key is what the voice listener will listen for and the Runnable value is what will
00368          * be executed upon detection of the key.
00369          */
00370         public void changeVocabWithAction(HashMap<String, Runnable> vocabWithAction)
00371         {
00372                 mActionMap = vocabWithAction;
00373 
00374                 //Extract voice commands from the map's keys
00375                 ArrayList<String> commands = new ArrayList<String>();
00376                 for (String command : mActionMap.keySet())
00377                 {
00378                         commands.add(command);
00379                 }
00380 
00381                 //Do not use system commands
00382                 useSystemCommands(false);
00383                 changeVocab(commands);
00384         }
00385 
00386         /*
00387          * This function sets the context global variable
00388          */
00389         public void setContext(Context newContext)
00390         {
00391                 mContext = newContext;
00392 
00393         }
00394 
00395         /*
00396          * This function sets the usingSystemCommands global variables
00397          */
00398         public void useSystemCommands(boolean use)
00399         {
00400                 usingSystemCommands = use;
00401         }
00402         
00403         /*
00404          * This function updates the vocabulary when a new robot is added.
00405          * If a command is already in progress, no update is done as the update
00406          * will occur once the command is finished.
00407          */
00408         private void update()
00409         {
00410                 if (mHelper == null)
00411                 {
00412                         changeVocab(getDefaultRobotCommands());
00413                 }
00414         }
00415 
00416         /*
00417          * This function is used after a full command has been detected.
00418          * It resets the helper to null and sends the detected command to the connected thread
00419          * to be sent to the google glass driver ros node
00420          */
00421         public void reset(String expression)
00422         {
00423                 Message msg = connectedHandler.obtainMessage(ConnectedThread.WRITE_MESSAGE, expression);
00424                 connectedHandler.sendMessage(msg);
00425 
00426                 mHelper = null;
00427                 usingSystemCommands = true;
00428                 changeVocab(getDefaultRobotCommands());
00429         }
00430 
00431         /*
00432          * This function cleans up the helper thread and the voice recognizer
00433          */
00434         public void shutdown()
00435         {
00436                 isShutdown = true;
00437                 mVoiceInputHelper.removeVoiceServiceListener();
00438                 mVoiceInputHelper = null;
00439                 if (mHelper != null)
00440                 {
00441                         mHelper.interrupt();
00442                         mHelper = null;
00443                 }
00444                 mApplication.setVoiceThreadHandler(null);
00445         }
00446 
00447 
00448         //This class is used to continuously listen for user input 
00449         public class VoiceListenerImpl implements VoiceListener {
00450                 protected final VoiceConfig voiceConfig;
00451 
00452                 public VoiceListenerImpl(VoiceConfig voiceConfig) {
00453                         this.voiceConfig = voiceConfig;
00454                 }
00455 
00456                 @Override
00457                 public void onVoiceServiceConnected() {
00458                         mVoiceInputHelper.setVoiceConfig(mVoiceConfig);
00459                 }
00460 
00461                 @Override
00462                 public void onVoiceServiceDisconnected() {      
00463 
00464                 }
00465 
00466                 //This method handles recognized voice commands
00467                 @Override
00468                 public VoiceConfig onVoiceCommand(VoiceCommand vc) {
00469                         String recognizedStr = vc.getLiteral();
00470 
00471                         Log.i("VoiceRecognitionThread", "Recognized text: "+recognizedStr);
00472 
00473                         if (isListening)
00474                         {                               
00475                                 //Check to see if it is a robot focus
00476                                 if (recognizedStr.equals("All robots"))
00477                                 {
00478                                         Log.d("VoiceRecognitionThread", "Setting focus on All");
00479                                         //focus on all robots
00480                                         mApplication.setRobotInFocus("All");
00481 
00482                                         Message msg = mainHandler.obtainMessage(MainActivity.FOCUS_MESSAGE, "All");
00483                                         mainHandler.sendMessageAtFrontOfQueue(msg);
00484 
00485                                         changeVocab(getDefaultRobotCommands());
00486                                 }
00487 
00488                                 else if (mApplication.getRobotNames().contains(recognizedStr))
00489                                 {
00490                                         Log.d("VoiceRecognitionThread", "Setting focus on: " + recognizedStr);
00491                                         //Set focus on robot whose name was recognized
00492                                         mApplication.setRobotInFocus(recognizedStr);
00493 
00494 
00495                                         Message msg = mainHandler.obtainMessage(MainActivity.FOCUS_MESSAGE, recognizedStr);
00496                                         mainHandler.sendMessageAtFrontOfQueue(msg);
00497 
00498                                         changeVocab(getDefaultRobotCommands());
00499                                 }
00500                                 else
00501                                 {
00502                                         //Print Command to screen
00503                                         Message msg = mainHandler.obtainMessage(MainActivity.COMMAND_MESSAGE, recognizedStr);
00504                                         mainHandler.sendMessageAtFrontOfQueue(msg);
00505 
00506                                         //Check to see if it is a system command.
00507                                         if (recognizedStr.startsWith("Connect to"))
00508                                         {
00509                                                 if (!isConnected) //If already connected do not connect
00510                                                 {
00511                                                         Message msgConnected = connectedHandler.obtainMessage(ConnectedThread.CONNECT_MESSAGE, recognizedStr.substring(11));
00512                                                         connectedHandler.sendMessageAtFrontOfQueue(msgConnected);
00513                                                 }
00514                                         }
00515                                         else if (recognizedStr.equals("End connection"))
00516                                         {
00517                                                 Message msgConnected = connectedHandler.obtainMessage();
00518                                                 msgConnected.what = ConnectedThread.DISCONNECT_MESSAGE;
00519                                                 connectedHandler.sendMessage(msgConnected);
00520                                         }
00521 
00522                                         else if (recognizedStr.equals("Close robot manager"))
00523                                         {
00524                                                 mApplication.onShutdown();
00525                                         }
00526 
00527                                         else if (recognizedStr.equals("Stop listening"))
00528                                         {
00529                                                 setListeningStatus(false);
00530                                         }
00531 
00532                                         else if (recognizedStr.equals("View robots"))
00533                                         {
00534                                                 Log.i("VoiceRecognitionThread", "Requesting launch of RobotListActivity");
00535                                                 Message msgView = mainHandler.obtainMessage();
00536                                                 msgView.what = MainActivity.ROBOT_LIST_MESSAGE;
00537                                                 mainHandler.sendMessage(msgView);
00538                                         }
00539 
00540                                         else if (recognizedStr.equals("Create group"))
00541                                         {
00542                                                 //NOT YET IMPLEMENTED
00543                                         }
00544 
00545                                         else if (recognizedStr.equals("View messages"))
00546                                         {
00547                                                 Log.i("VoiceRecognitionThread", "Requesting launch of MessageListActivity");
00548                                                 Message msgView = mainHandler.obtainMessage();
00549                                                 msgView.what = MainActivity.MESSAGE_LIST_MESSAGE;
00550                                                 mainHandler.sendMessage(msgView);
00551                                         }
00552 
00553                                         else if (recognizedStr.equals("View map"))
00554                                         {
00555                                                 //NOT YET IMPLEMENTED
00556                                         }
00557                                         
00558                                         else if (recognizedStr.equals("Cancel"))
00559                                         {
00560                                                 mHelper.interrupt();
00561                                                 mHelper = null;
00562                                                 changeVocab(getDefaultRobotCommands());
00563                                                 Message mainMsg = mainHandler.obtainMessage(MainActivity.COMMAND_MESSAGE, "Cancel");
00564                                                 mainHandler.sendMessage(mainMsg);
00565                                         } 
00566 
00567                                         else if (mActionMap != null && mActionMap.containsKey(recognizedStr))
00568                                         {
00569                                                 mActionMap.get(recognizedStr).run();
00570                                         }
00571 
00572                                         //If the recognized string is not a system command, it must be a robot command
00573                                         else if (!recognizedStr.equals("Start listening"))
00574                                         {
00575                                                 if (mHelper ==  null)
00576                                                 {
00577                                                         Robot focus = mApplication.getRobotInFocus();
00578                                                         if (focus == null)
00579                                                         {
00580                                                                 mHelper = new VoiceHelperThread(mApplication, recognizedStr, 
00581                                                                                 "All", mHandler);
00582                                                         }
00583                                                         else
00584                                                         {
00585                                                                 mHelper = new VoiceHelperThread(mApplication, recognizedStr, 
00586                                                                                 focus.getName(), mHandler);
00587                                                         }
00588 
00589                                                         mHelper.start();
00590                                                         while(!mHelper.isReady());
00591                                                         mHelperHandler = mHelper.getHandler();
00592                                                         //addToVocab("Cancel");
00593 
00594                                                         usingSystemCommands = false;
00595                                                 }
00596                                                 else
00597                                                 {
00598                                                         Message newPhraseMsg = mHelperHandler.obtainMessage(VoiceHelperThread.RECEIVE_PHRASE_MESSAGE, recognizedStr);
00599                                                         mHelperHandler.sendMessageAtFrontOfQueue(newPhraseMsg);
00600                                                 }
00601                                         }
00602                                 }
00603                         }
00604                         else if (recognizedStr.equals("Start listening"))
00605                         {
00606                                 Message msgShow = mainHandler.obtainMessage(MainActivity.COMMAND_MESSAGE, recognizedStr);
00607                                 mainHandler.sendMessageAtFrontOfQueue(msgShow);
00608                                 setListeningStatus(true);
00609                         }
00610 
00611                         return null;
00612                 }
00613 
00614                 @Override
00615                 public FormattingLogger getLogger() {
00616                         return FormattingLoggers.getContextLogger();
00617                 }
00618 
00619                 @Override
00620                 public boolean isRunning() {
00621                         return true;
00622                 }
00623 
00624                 @Override
00625                 public boolean onResampledAudioData(byte[] arg0, int arg1, int arg2) {
00626                         return false;
00627                 }
00628 
00629                 //This may be considered in order to differentiate commands from normal
00630                 //speech
00631                 @Override
00632                 public boolean onVoiceAmplitudeChanged(double arg0) {
00633                         return false;
00634                 }
00635 
00636                 @Override
00637                 public void onVoiceConfigChanged(VoiceConfig arg0, boolean arg1) 
00638                 {
00639                 }
00640         }
00641 }


google_glass_driver
Author(s): Nicholas Otero
autogenerated on Fri Aug 28 2015 10:51:45