VoiceDialerActivity.java revision e6ca0690abd35dd9c7645fc2a24167f3e5ecc1ee
1538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project/* 2ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * Copyright (C) 2010 The Android Open Source Project 3538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * 4538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * you may not use this file except in compliance with the License. 6538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * You may obtain a copy of the License at 7538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * 8538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * 10538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * See the License for the specific language governing permissions and 14538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * limitations under the License. 15538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project */ 16538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 17538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectpackage com.android.voicedialer; 18538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 19538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.app.Activity; 20538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.app.AlertDialog; 2133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganeshimport android.bluetooth.BluetoothAdapter; 227eb03167f94204716f858a07a12d7ef4e915c81bJaikumar Ganeshimport android.bluetooth.BluetoothDevice; 23ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.bluetooth.BluetoothHeadset; 2433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganeshimport android.bluetooth.BluetoothProfile; 25ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.content.BroadcastReceiver; 26ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.content.Context; 27538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.content.DialogInterface; 28ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.content.Intent; 29ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.content.IntentFilter; 30538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.media.AudioManager; 31ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.media.ToneGenerator; 32538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.os.Bundle; 33538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.os.Handler; 349b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdonimport android.os.PowerManager; 359b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdonimport android.os.PowerManager.WakeLock; 36538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.os.SystemProperties; 37538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.os.Vibrator; 38ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.speech.tts.TextToSpeech; 39538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.util.Config; 40538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.util.Log; 41538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.view.View; 42ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport android.view.WindowManager; 43538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport android.widget.TextView; 4433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 45538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectimport java.io.File; 46e6ca0690abd35dd9c7645fc2a24167f3e5ecc1eeJaikumar Ganeshimport java.io.IOException; 471b715dc663bd7155d996576774e487d31bf331f7Martin Hibdonimport java.io.InputStream; 48ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdonimport java.util.HashMap; 49e6ca0690abd35dd9c7645fc2a24167f3e5ecc1eeJaikumar Ganeshimport java.util.List; 50538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 51538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project/** 527a55150b308ed03d41091e4c3ac693d6eb0ab145mah * TODO: get rid of the anonymous classes 537a55150b308ed03d41091e4c3ac693d6eb0ab145mah * 54538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project * This class is the user interface of the VoiceDialer application. 55ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * It begins in the INITIALIZING state. 56ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 57ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * INITIALIZING : 58ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out on events from TTS and the BluetoothHeadset 59ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * once TTS initialized and SCO channel set up: 60ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * prompt the user "speak now" 61ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the SPEAKING_GREETING state 62ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 63ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_GREETING: 64ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from TTS or the fallback runnable 65ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * once the greeting utterance completes: 66ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin listening for the command using the {@link CommandRecognizerEngine} 67ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the WAITING_FOR_COMMAND state 68ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 69ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * WAITING_FOR_COMMAND : 70ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from the recognizer 71ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * on RecognitionFailure or RecognitionError: 72ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin speaking "try again." 73ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to state SPEAKING_TRY_AGAIN 74ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * on RecognitionSuccess: 75ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * single result: 76ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin speaking the sentence describing the intent 77ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the SPEAKING_CHOSEN_ACTION 78ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * multiple results: 79ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin speaking each of the choices in order 80ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the SPEAKING_CHOICES state 81ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 82ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_TRY_AGAIN: 83ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from TTS or the fallback runnable 84ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * once the try again utterance completes: 85ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin listening for the command using the {@link CommandRecognizerEngine} 86ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the LISTENING_FOR_COMMAND state 87ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 88ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_CHOSEN_ACTION: 89ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from TTS or the fallback runnable 90ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * once the utterance completes: 91ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * dispatch the intent that was chosen 92ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the EXITING state 93ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * finish the activity 94ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 95ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_CHOICES: 96ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from TTS or the fallback runnable 97ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * once the utterance completes: 98ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin listening for the user's choice using the 99ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * {@link PhoneTypeChoiceRecognizerEngine} 100ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the WAITING_FOR_CHOICE state. 101ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 102ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * WAITING_FOR_CHOICE: 103ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from the recognizer 104ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * on RecognitionFailure or RecognitionError: 105ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * begin speaking the "invalid choice" message, along with the list 106ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * of choices 107ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * * transition to the SPEAKING_CHOICES state 108ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * on RecognitionSuccess: 109ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * if the result is "try again", prompt the user to say a command, begin 110ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * listening for the command, and transition back to the WAITING_FOR_COMMAND 111ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * state. 112ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * if the result is "exit", then being speaking the "goodbye" message and 113ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * transition to the SPEAKING_GOODBYE state. 114ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * if the result is a valid choice, begin speaking the action chosen,initiate 115ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * the command the user has choose and exit. 116ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * if not a valid choice, speak the "invalid choice" message, begin 117ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * speaking the choices in order again, transition to the 118ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_CHOICES 119ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 120ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * SPEAKING_GOODBYE: 121ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * This transitions out only on events from TTS or the fallback runnable 122ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * after a time out, finish the activity. 123ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon * 124538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project */ 125ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 126538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Projectpublic class VoiceDialerActivity extends Activity { 127538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 128538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private static final String TAG = "VoiceDialerActivity"; 129538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 130538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private static final String MICROPHONE_EXTRA = "microphone"; 131538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private static final String CONTACTS_EXTRA = "contacts"; 132632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 133ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final String SPEAK_NOW_UTTERANCE = "speak_now"; 134ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final String TRY_AGAIN_UTTERANCE = "try_again"; 135ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final String CHOSEN_ACTION_UTTERANCE = "chose_action"; 136ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final String GOODBYE_UTTERANCE = "goodbye"; 137ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final String CHOICES_UTTERANCE = "choices"; 138ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 139ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int FIRST_UTTERANCE_DELAY = 300; 140ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int MAX_TTS_DELAY = 6000; 141ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int EXIT_DELAY = 2000; 142ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 143ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int BLUETOOTH_SAMPLE_RATE = 8000; 144ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int REGULAR_SAMPLE_RATE = 11025; 145ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 146ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int INITIALIZING = 0; 147ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int SPEAKING_GREETING = 1; 148ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int WAITING_FOR_COMMAND = 2; 149ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int SPEAKING_TRY_AGAIN = 3; 150ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int SPEAKING_CHOICES = 4; 151ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int WAITING_FOR_CHOICE = 5; 152ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int WAITING_FOR_DIALOG_CHOICE = 6; 153ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int SPEAKING_CHOSEN_ACTION = 7; 154ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int SPEAKING_GOODBYE = 8; 155ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final int EXITING = 9; 156ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 157ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final CommandRecognizerEngine mCommandEngine = 1585f25619fecb3fda9b34f90eb1e333a89ec431583Martin Hibdon new CommandRecognizerEngine(); 159ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static final PhoneTypeChoiceRecognizerEngine mPhoneTypeChoiceEngine = 160ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon new PhoneTypeChoiceRecognizerEngine(); 16164c64e7c25e6efe31fa53369ec49db442b68ff9amah private CommandRecognizerClient mCommandClient; 162ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private ChoiceRecognizerClient mChoiceClient; 163ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private ToneGenerator mToneGenerator; 164538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private Handler mHandler; 165538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private Thread mRecognizerThread = null; 166538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project private AudioManager mAudioManager; 167ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private BluetoothHeadset mBluetoothHeadset; 16833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh private BluetoothDevice mBluetoothDevice; 16933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh private BluetoothAdapter mAdapter; 170ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private TextToSpeech mTts; 171ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private HashMap<String, String> mTtsParams; 172ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private VoiceDialerBroadcastReceiver mReceiver; 173ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private boolean mWaitingForTts; 174ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private boolean mWaitingForScoConnection; 175ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private Intent[] mAvailableChoices; 176ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private Intent mChosenAction; 177ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private int mBluetoothVoiceVolume; 178ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private int mState; 1794a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon private AlertDialog mAlertDialog; 180ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private Runnable mFallbackRunnable; 181ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private boolean mUsingBluetooth = false; 182ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private int mSampleRate; 1839b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon private WakeLock mWakeLock; 184538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 185538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project @Override 186538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project protected void onCreate(Bundle icicle) { 187ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon if (Config.LOGD) Log.d(TAG, "onCreate"); 188538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project super.onCreate(icicle); 189538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project mHandler = new Handler(); 190538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE); 191ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon mToneGenerator = new ToneGenerator(AudioManager.STREAM_RING, 192ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon ToneGenerator.MAX_VOLUME); 193ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon } 194ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon 195ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon protected void onStart() { 196ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "onStart " + getIntent()); 197ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon super.onStart(); 198ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1999b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon acquireWakeLock(this); 2009b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon 201ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = INITIALIZING; 202ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChosenAction = null; 2032abf40fd5f901da1b6168aa778cbf7e6713a1e9bMartin Hibdon mAudioManager.requestAudioFocus( 2042abf40fd5f901da1b6168aa778cbf7e6713a1e9bMartin Hibdon null, AudioManager.STREAM_MUSIC, 2052abf40fd5f901da1b6168aa778cbf7e6713a1e9bMartin Hibdon AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 206be1584eb7e59c391a45aa21fb5e65ada1aedae9aDave Sparks 207ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // set this flag so this activity will stay in front of the keyguard 208ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 209ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon getWindow().addFlags(flags); 21064c64e7c25e6efe31fa53369ec49db442b68ff9amah 211538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project // open main window 212538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project setTheme(android.R.style.Theme_Dialog); 213538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project setTitle(R.string.title); 214538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project setContentView(R.layout.voice_dialing); 215538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project findViewById(R.id.microphone_view).setVisibility(View.INVISIBLE); 216538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project findViewById(R.id.retry_view).setVisibility(View.INVISIBLE); 217538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project findViewById(R.id.microphone_loading_view).setVisibility(View.VISIBLE); 218538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project if (RecognizerLogger.isEnabled(this)) { 219ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ((TextView) findViewById(R.id.substate)).setText(R.string.logging_enabled); 220538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 221632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 222ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Get handle to BluetoothHeadset object 223ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon IntentFilter audioStateFilter; 224ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon audioStateFilter = new IntentFilter(); 22533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh audioStateFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 226ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon audioStateFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 227ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mReceiver = new VoiceDialerBroadcastReceiver(); 228ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon registerReceiver(mReceiver, audioStateFilter); 229538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 230ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.setContactsFile(newFile(getArg(CONTACTS_EXTRA))); 231ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.setMinimizeResults(true); 232ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.setAllowOpenEntries(false); 233ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandClient = new CommandRecognizerClient(); 234ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChoiceClient = new ChoiceRecognizerClient(); 235538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 23633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mAdapter = BluetoothAdapter.getDefaultAdapter(); 23733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (BluetoothHeadset.isBluetoothVoiceDialingEnabled(this) && mAdapter != null) { 23833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (!mAdapter.getProfileProxy(this, mBluetoothHeadsetServiceListener, 23933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh BluetoothProfile.HEADSET)) { 24033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh Log.e(TAG, "Getting Headset Proxy failed"); 24133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 242632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 243ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 244fbc76d73e034753bf284d52f0df766369ae3cb5bMartin Hibdon mUsingBluetooth = false; 245fbc76d73e034753bf284d52f0df766369ae3cb5bMartin Hibdon if (Config.LOGD) Log.d(TAG, "bluetooth unavailable"); 246ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mSampleRate = REGULAR_SAMPLE_RATE; 247ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.setMinimizeResults(false); 248ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.setAllowOpenEntries(true); 249ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 250ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we're not using bluetooth apparently, just start listening. 251ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon listenForCommand(); 252ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 253fbc76d73e034753bf284d52f0df766369ae3cb5bMartin Hibdon 254538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 255632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 256ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon class ErrorRunnable implements Runnable { 257ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private int mErrorMsg; 258ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public ErrorRunnable(int errorMsg) { 259ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mErrorMsg = errorMsg; 260ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 2615f25619fecb3fda9b34f90eb1e333a89ec431583Martin Hibdon 262ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 263ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // put up an error and exit 264ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.removeCallbacks(mMicFlasher); 265ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ((TextView)findViewById(R.id.state)).setText(R.string.failure); 266ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ((TextView)findViewById(R.id.substate)).setText(mErrorMsg); 267ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ((TextView)findViewById(R.id.substate)).setText( 268ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon R.string.headset_connection_lost); 269ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.microphone_view).setVisibility(View.INVISIBLE); 270ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.retry_view).setVisibility(View.VISIBLE); 271ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 272ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 273ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (!mUsingBluetooth) { 274ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon playSound(ToneGenerator.TONE_PROP_NACK); 275538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 276ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 277538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 278632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 279ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon class OnTtsCompletionRunnable implements Runnable { 280ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private boolean mFallback; 281632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 282ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon OnTtsCompletionRunnable(boolean fallback) { 283ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallback = fallback; 284538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 285538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 286ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 287ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mFallback) { 288ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.e(TAG, "utterance completion not delivered, using fallback"); 289ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 290ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.d(TAG, "onTtsCompletionRunnable"); 291ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mState == SPEAKING_GREETING || mState == SPEAKING_TRY_AGAIN) { 292ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon listenForCommand(); 293ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else if (mState == SPEAKING_CHOICES) { 294ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon listenForChoice(); 295ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else if (mState == SPEAKING_GOODBYE) { 296ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = EXITING; 297ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 298ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else if (mState == SPEAKING_CHOSEN_ACTION) { 299ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = EXITING; 300ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon startActivityHelp(mChosenAction); 301ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 302ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 303538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 304538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 305632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 306ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon class GreetingRunnable implements Runnable { 307ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 308ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = SPEAKING_GREETING; 309ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, 310ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon SPEAK_NOW_UTTERANCE); 311ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(getString(R.string.speak_now_tts), 312ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_FLUSH, 313ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 314ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Normally, we will begin listening for the command after the 315ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // utterance completes. As a fallback in case the utterance 316ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // does not complete, post a delayed runnable to fire 317ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the intent. 318ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = new OnTtsCompletionRunnable(true); 319ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(mFallbackRunnable, MAX_TTS_DELAY); 320ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 321ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 3222abf40fd5f901da1b6168aa778cbf7e6713a1e9bMartin Hibdon 323ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon class TtsInitListener implements TextToSpeech.OnInitListener { 324ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onInit(int status) { 325ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR. 326ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "onInit for tts"); 327ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (status != TextToSpeech.SUCCESS) { 328ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Initialization failed. 329ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.e(TAG, "Could not initialize TextToSpeech."); 330ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new ErrorRunnable(R.string.recognition_error)); 331ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 332ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 333ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 334632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 335ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mTts == null) { 336ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.e(TAG, "null tts"); 337ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new ErrorRunnable(R.string.recognition_error)); 338ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 339ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 340538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 341632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 342ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener()); 343ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 344ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // The TTS engine has been successfully initialized. 345ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mWaitingForTts = false; 346ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 347ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // TTS over bluetooth is really loud, 348ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Limit volume to -18dB. Stream volume range represents approximately 50dB 349ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // (See AudioSystem.cpp linearToLog()) so the number of steps corresponding 350ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // to 18dB is 18 / (50 / maxSteps). 351ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mBluetoothVoiceVolume = mAudioManager.getStreamVolume( 352ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon AudioManager.STREAM_BLUETOOTH_SCO); 353ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_BLUETOOTH_SCO); 354ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int volume = maxVolume - ((18 / (50/maxVolume)) + 1); 355ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mBluetoothVoiceVolume > volume) { 356ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, 0); 357ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 358632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 359ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mWaitingForScoConnection) { 360ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the bluetooth connection is not up yet, still waiting. 361ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 362ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we now have SCO connection and TTS, so we can start. 363ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(new GreetingRunnable(), FIRST_UTTERANCE_DELAY); 364ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 365538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 366538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 367632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 368ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon class OnUtteranceCompletedListener 369ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon implements TextToSpeech.OnUtteranceCompletedListener { 370ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onUtteranceCompleted(String utteranceId) { 371fbc76d73e034753bf284d52f0df766369ae3cb5bMartin Hibdon if (Config.LOGD) Log.d(TAG, "onUtteranceCompleted " + utteranceId); 372ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // since the utterance has completed, we no longer need the fallback. 373ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.removeCallbacks(mFallbackRunnable); 374ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = null; 375ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new OnTtsCompletionRunnable(false)); 376ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 377538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 378538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 37933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh private void updateBluetoothParameters(boolean connected) { 38033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (connected) { 38133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (Config.LOGD) Log.d(TAG, "using bluetooth"); 38233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mUsingBluetooth = true; 38333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 38433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothHeadset.startVoiceRecognition(mBluetoothDevice); 38533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 38633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mSampleRate = BLUETOOTH_SAMPLE_RATE; 38733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mCommandEngine.setMinimizeResults(true); 38833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mCommandEngine.setAllowOpenEntries(false); 38933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 39033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // we can't start recognizing until we get connected to the BluetoothHeadset 39133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // and have a connected audio state. We will listen for these 39233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // states to change. 39333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mWaitingForScoConnection = true; 39433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 39533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // initialize the text to speech system 39633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mWaitingForTts = true; 39733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mTts = new TextToSpeech(VoiceDialerActivity.this, new TtsInitListener()); 39833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mTtsParams = new HashMap<String, String>(); 39933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_STREAM, 40033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh String.valueOf(AudioManager.STREAM_VOICE_CALL)); 40133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // we need to wait for the TTS system and the SCO connection 40233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // before we can start listening. 40333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } else { 40433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (Config.LOGD) Log.d(TAG, "not using bluetooth"); 40533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mUsingBluetooth = false; 40633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mSampleRate = REGULAR_SAMPLE_RATE; 40733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mCommandEngine.setMinimizeResults(false); 40833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mCommandEngine.setAllowOpenEntries(true); 409fbc76d73e034753bf284d52f0df766369ae3cb5bMartin Hibdon 41033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // we're not using bluetooth apparently, just start listening. 41133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh listenForCommand(); 41233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 41333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 414538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 41533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh private BluetoothProfile.ServiceListener mBluetoothHeadsetServiceListener = 41633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh new BluetoothProfile.ServiceListener() { 41733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh public void onServiceConnected(int profile, BluetoothProfile proxy) { 418ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "onServiceConnected"); 41933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothHeadset = (BluetoothHeadset) proxy; 42033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 421e6ca0690abd35dd9c7645fc2a24167f3e5ecc1eeJaikumar Ganesh List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 42233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 423e6ca0690abd35dd9c7645fc2a24167f3e5ecc1eeJaikumar Ganesh if (deviceList.size() > 0) { 424e6ca0690abd35dd9c7645fc2a24167f3e5ecc1eeJaikumar Ganesh mBluetoothDevice = deviceList.get(0); 42533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh int state = mBluetoothHeadset.getConnectionState(mBluetoothDevice); 42633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (Config.LOGD) Log.d(TAG, "headset status " + state); 42733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 42833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh // We are already connnected to a headset 42933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (state == BluetoothHeadset.STATE_CONNECTED) { 43033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh updateBluetoothParameters(true); 43133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh return; 43233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 43333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 43433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh updateBluetoothParameters(false); 43533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 43633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 43733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh public void onServiceDisconnected(int profile) { 43833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothHeadset = null; 439538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 440538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project }; 441538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 442ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private class VoiceDialerBroadcastReceiver extends BroadcastReceiver { 443ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon @Override 444ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onReceive(Context context, Intent intent) { 445ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String action = intent.getAction(); 44633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 44733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 44833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 44933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 45033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 45133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (Config.LOGD) Log.d(TAG, "HEADSET STATE -> " + state); 45233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh 45333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (state == BluetoothProfile.STATE_CONNECTED) { 45433175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothDevice = device; 45533175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh updateBluetoothParameters(true); 45633175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 45733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothDevice = null; 45833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh updateBluetoothParameters(false); 45933175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } 46033175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 46133175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 46233175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); 46333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED && 464ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mWaitingForScoConnection) { 465ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // SCO channel has just become available. 466ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mWaitingForScoConnection = false; 467ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mWaitingForTts) { 468ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // still waiting for the TTS to be set up. 469ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 470ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we now have SCO connection and TTS, so we can start. 471ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(new GreetingRunnable(), FIRST_UTTERANCE_DELAY); 472ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 47333175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh } else if (prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 474ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (!mWaitingForScoConnection) { 475ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // apparently our connection to the headset has dropped. 476ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we won't be able to continue voicedialing. 477ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "lost sco connection"); 4784a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 479ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new ErrorRunnable( 480ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon R.string.headset_connection_lost)); 4814a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 482ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 483ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 484ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 4854a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 486ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 487ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 4884a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 489ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void askToTryAgain() { 490ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // get work off UAPI thread 491ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new Runnable() { 492ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 493ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mAlertDialog != null) { 494ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAlertDialog.dismiss(); 495ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 4964a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 497ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.removeCallbacks(mMicFlasher); 498ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ((TextView)findViewById(R.id.state)).setText(R.string.please_try_again); 499ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.state).setVisibility(View.VISIBLE); 500ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.microphone_view).setVisibility(View.INVISIBLE); 501ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.retry_view).setVisibility(View.VISIBLE); 502ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 503ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 504ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = SPEAKING_TRY_AGAIN; 505ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, 506ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TRY_AGAIN_UTTERANCE); 507ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(getString(R.string.no_results_tts), 508ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_FLUSH, 509ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 510ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 511ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Normally, the we will start listening after the 512ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // utterance completes. As a fallback in case the utterance 513ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // does not complete, post a delayed runnable to fire 514ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the intent. 515ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = new OnTtsCompletionRunnable(true); 516ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(mFallbackRunnable, MAX_TTS_DELAY); 517ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 518ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon try { 519ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Thread.sleep(playSound(ToneGenerator.TONE_PROP_NACK)); 520ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } catch (InterruptedException e) { 521ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 522ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we are not using tts, so we just start listening again. 523ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon listenForCommand(); 524ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 5254a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 526ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }); 527ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 5284a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 529ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void performChoice() { 530ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 531ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String sentenceSpoken = spaceOutDigits( 532ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChosenAction.getStringExtra( 533ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SENTENCE_EXTRA)); 534ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 535ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = SPEAKING_CHOSEN_ACTION; 536ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, 537ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon CHOSEN_ACTION_UTTERANCE); 538ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(sentenceSpoken, 539ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_FLUSH, 540ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 541ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 542ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Normally, the intent will be dispatched after the 543ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // utterance completes. As a fallback in case the utterance 544ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // does not complete, post a delayed runnable to fire 545ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the intent. 546ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = new OnTtsCompletionRunnable(true); 547ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(mFallbackRunnable, MAX_TTS_DELAY); 548ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 549ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // just dispatch the intent 550ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon startActivityHelp(mChosenAction); 551ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 5524a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 553ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 5544a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 555ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void waitForChoice() { 556ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 557ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // We are running in bluetooth mode, and we have 558ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // multiple matches. Speak the choices and let 559ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the user choose. 560ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 561ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // We will not start listening until the utterance 562ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // of the choice list completes. 563ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon speakChoices(); 564ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 565ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Normally, listening will begin after the 566ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // utterance completes. As a fallback in case the utterance 567ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // does not complete, post a delayed runnable to begin 568ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // listening. 569ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = new OnTtsCompletionRunnable(true); 570ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(mFallbackRunnable, MAX_TTS_DELAY); 571ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 572ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // We are not running in bluetooth mode, so all 573ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we need to do is wait for the user to select 574ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // a choice from the alert dialog. We will wait 575ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // indefinitely for this. 576ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = WAITING_FOR_DIALOG_CHOICE; 577ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 5784a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 5794a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 58064c64e7c25e6efe31fa53369ec49db442b68ff9amah private class CommandRecognizerClient implements RecognizerClient { 581ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon static final int MIN_VOLUME_TO_SKIP = 2; 582ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon /** 58364c64e7c25e6efe31fa53369ec49db442b68ff9amah * Called by the {@link RecognizerEngine} when the microphone is started. 58464c64e7c25e6efe31fa53369ec49db442b68ff9amah */ 5851b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon public void onMicrophoneStart(InputStream mic) { 58664c64e7c25e6efe31fa53369ec49db442b68ff9amah if (Config.LOGD) Log.d(TAG, "onMicrophoneStart"); 587ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 588ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (!mUsingBluetooth) { 589ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon playSound(ToneGenerator.TONE_PROP_BEEP); 590ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 591ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int ringVolume = mAudioManager.getStreamVolume( 592ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon AudioManager.STREAM_RING); 593ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.d(TAG, "ringVolume " + ringVolume); 594ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 595ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (ringVolume >= MIN_VOLUME_TO_SKIP) { 596ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // now we're playing a sound, and corrupting the input sample. 597ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // So we need to pull that junk off of the input stream so that the 598ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // recognizer won't see it. 599ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon try { 600ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon skipBeep(mic); 601ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } catch (java.io.IOException e) { 602ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.e(TAG, "IOException " + e); 603ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 604ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 605ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "no tone"); 606d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 6071b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon } 608632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 60964c64e7c25e6efe31fa53369ec49db442b68ff9amah mHandler.post(new Runnable() { 61064c64e7c25e6efe31fa53369ec49db442b68ff9amah public void run() { 611ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.retry_view).setVisibility(View.INVISIBLE); 612ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.microphone_loading_view).setVisibility( 613ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon View.INVISIBLE); 61464c64e7c25e6efe31fa53369ec49db442b68ff9amah ((TextView)findViewById(R.id.state)).setText(R.string.listening); 61564c64e7c25e6efe31fa53369ec49db442b68ff9amah mHandler.post(mMicFlasher); 61664c64e7c25e6efe31fa53369ec49db442b68ff9amah } 61764c64e7c25e6efe31fa53369ec49db442b68ff9amah }); 61864c64e7c25e6efe31fa53369ec49db442b68ff9amah } 619538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 620d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon /** 621d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon * Beep detection 622d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon */ 623d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int START_WINDOW_MS = 500; // Beep detection window duration in ms 624d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int SINE_FREQ = 400; // base sine frequency on beep 625d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int NUM_PERIODS_BLOCK = 10; // number of sine periods in one energy averaging block 626d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int THRESHOLD = 8; // absolute pseudo energy threshold 627d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int START = 0; // beep detection start 628d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int RISING = 1; // beep rising edge start 629d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon private static final int TOP = 2; // beep constant energy detected 630d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 631d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon void skipBeep(InputStream is) throws IOException { 632ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int sampleCount = ((mSampleRate / SINE_FREQ) * NUM_PERIODS_BLOCK); 633d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int blockSize = 2 * sampleCount; // energy averaging block 634d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 635d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (is == null || blockSize == 0) { 636d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon return; 637d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 638d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 639d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon byte[] buf = new byte[blockSize]; 640ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int maxBytes = 2 * ((START_WINDOW_MS * mSampleRate) / 1000); 641d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon maxBytes = ((maxBytes-1) / blockSize + 1) * blockSize; 642d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 643d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int count = 0; 644d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int state = START; // detection state 645d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon long prevE = 0; // previous pseudo energy 646d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon long peak = 0; 647d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int threshold = THRESHOLD*sampleCount; // absolute energy threshold 648d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon Log.d(TAG, "blockSize " + blockSize); 649d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 650d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon while (count < maxBytes) { 651d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int cnt = 0; 652d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon while (cnt < blockSize) { 653d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon int n = is.read(buf, cnt, blockSize-cnt); 654d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (n < 0) { 655d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon throw new java.io.IOException(); 656d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 657d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon cnt += n; 658d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 659d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 660d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon // compute pseudo energy 661d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon cnt = blockSize; 662d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon long sumx = 0; 663d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon long sumxx = 0; 664d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon while (cnt >= 2) { 665d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon short smp = (short)((buf[cnt - 1] << 8) + (buf[cnt - 2] & 0xFF)); 666d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon sumx += smp; 667d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon sumxx += smp*smp; 668d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon cnt -= 2; 6691b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon } 670d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon long energy = (sumxx*sampleCount - sumx*sumx)/(sampleCount*sampleCount); 671d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon Log.d(TAG, "sumx " + sumx + " sumxx " + sumxx + " ee " + energy); 672d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon 673d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon switch (state) { 674d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon case START: 675d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (energy > threshold && energy > (prevE * 2) && prevE != 0) { 676d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon // rising edge if energy doubled and > abs threshold 677d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon state = RISING; 678ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "start RISING: " + count +" time: "+ (((1000*count)/2)/mSampleRate)); 679d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 680d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon break; 681d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon case RISING: 682d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (energy < threshold || energy < (prevE / 2)){ 683d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon // energy fell back below half of previous, back to start 684ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "back to START: " + count +" time: "+ (((1000*count)/2)/mSampleRate)); 685d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon peak = 0; 686d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon state = START; 687d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } else if (energy > (prevE / 2) && energy < (prevE * 2)) { 688d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon // Start of constant energy 689ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "start TOP: " + count +" time: "+ (((1000*count)/2)/mSampleRate)); 690d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (peak < energy) { 691d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon peak = energy; 692d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 693d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon state = TOP; 694d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 695d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon break; 696d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon case TOP: 697d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (energy < threshold || energy < (peak / 2)) { 698d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon // e went to less than half of the peak 699ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "end TOP: " + count +" time: "+ (((1000*count)/2)/mSampleRate)); 700d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon return; 701d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 702d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon break; 703d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon } 704d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon prevE = energy; 705d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon count += blockSize; 7061b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon } 707d23e5f670b10a582761421d8bc101d4a2d660ac0Martin Hibdon if (Config.LOGD) Log.d(TAG, "no beep detected, timed out"); 7081b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon } 7091b715dc663bd7155d996576774e487d31bf331f7Martin Hibdon 71064c64e7c25e6efe31fa53369ec49db442b68ff9amah /** 71164c64e7c25e6efe31fa53369ec49db442b68ff9amah * Called by the {@link RecognizerEngine} if the recognizer fails. 71264c64e7c25e6efe31fa53369ec49db442b68ff9amah */ 71364c64e7c25e6efe31fa53369ec49db442b68ff9amah public void onRecognitionFailure(final String msg) { 71464c64e7c25e6efe31fa53369ec49db442b68ff9amah if (Config.LOGD) Log.d(TAG, "onRecognitionFailure " + msg); 715ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we had zero results. Just try again. 716ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon askToTryAgain(); 71764c64e7c25e6efe31fa53369ec49db442b68ff9amah } 718538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 71964c64e7c25e6efe31fa53369ec49db442b68ff9amah /** 72064c64e7c25e6efe31fa53369ec49db442b68ff9amah * Called by the {@link RecognizerEngine} on an internal error. 72164c64e7c25e6efe31fa53369ec49db442b68ff9amah */ 72264c64e7c25e6efe31fa53369ec49db442b68ff9amah public void onRecognitionError(final String msg) { 72364c64e7c25e6efe31fa53369ec49db442b68ff9amah if (Config.LOGD) Log.d(TAG, "onRecognitionError " + msg); 724ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new ErrorRunnable(R.string.recognition_error)); 725ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 72664c64e7c25e6efe31fa53369ec49db442b68ff9amah } 727538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project 72864c64e7c25e6efe31fa53369ec49db442b68ff9amah /** 72964c64e7c25e6efe31fa53369ec49db442b68ff9amah * Called by the {@link RecognizerEngine} when is succeeds. If there is 73064c64e7c25e6efe31fa53369ec49db442b68ff9amah * only one item, then the Intent is dispatched immediately. 73164c64e7c25e6efe31fa53369ec49db442b68ff9amah * If there are more, then an AlertDialog is displayed and the user is 73264c64e7c25e6efe31fa53369ec49db442b68ff9amah * prompted to select. 73364c64e7c25e6efe31fa53369ec49db442b68ff9amah * @param intents a list of Intents corresponding to the sentences. 73464c64e7c25e6efe31fa53369ec49db442b68ff9amah */ 73564c64e7c25e6efe31fa53369ec49db442b68ff9amah public void onRecognitionSuccess(final Intent[] intents) { 736ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "CommandRecognizerClient onRecognitionSuccess " + 737ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon intents.length); 738ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mState != WAITING_FOR_COMMAND) { 739ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "not waiting for command, ignoring"); 740ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 741ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 74264c64e7c25e6efe31fa53369ec49db442b68ff9amah 743ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // store the intents in a member variable so that we can access it 744ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // later when the user chooses which action to perform. 745ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAvailableChoices = intents; 74664c64e7c25e6efe31fa53369ec49db442b68ff9amah 747ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new Runnable() { 74864c64e7c25e6efe31fa53369ec49db442b68ff9amah public void run() { 749ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (!mUsingBluetooth) { 750ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon playSound(ToneGenerator.TONE_PROP_ACK); 751ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 75264c64e7c25e6efe31fa53369ec49db442b68ff9amah mHandler.removeCallbacks(mMicFlasher); 75364c64e7c25e6efe31fa53369ec49db442b68ff9amah 754ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String[] sentences = new String[intents.length]; 755ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon for (int i = 0; i < intents.length; i++) { 756ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon sentences[i] = intents[i].getStringExtra( 757ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SENTENCE_EXTRA); 758ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 75964c64e7c25e6efe31fa53369ec49db442b68ff9amah 760ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (intents.length == 0) { 761ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon onRecognitionFailure("zero intents"); 762ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 763ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 764ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 765ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (intents.length > 0) { 766ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // see if we the response was "exit" or "cancel". 767ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String value = intents[0].getStringExtra( 768ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SEMANTIC_EXTRA); 769ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "value " + value); 770ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if ("X".equals(value)) { 771ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 772ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 773ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 774ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 775ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 776ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth && 777ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon (intents.length == 1 || 778ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon !Intent.ACTION_CALL_PRIVILEGED.equals( 779ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon intents[0].getAction()))) { 780ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // When we're running in bluetooth mode, we expect 781ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // that the user is not looking at the screen and cannot 782ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // interact with the device in any way besides voice 783ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // commands. In this case we need to minimize how many 784ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // interactions the user has to perform in order to call 785ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // someone. 786ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // So if there is only one match, instead of making the 787ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // user confirm, we just assume it's correct, speak 788ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the choice over TTS, and then dispatch it. 789ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // If there are multiple matches for some intent type 790ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // besides "call", it's too difficult for the user to 791ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // explain which one they meant, so we just take the highest 792ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // confidence match and dispatch that. 793ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 794ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Speak the sentence for the action we are about 795ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // to dispatch so that the user knows what is happening. 796ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChosenAction = intents[0]; 797ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon performChoice(); 798ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 799ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 800ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 801ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Either we are not running in bluetooth mode, 802ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // or we had multiple matches. Either way, we need 803ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the user to confirm the choice. 804ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Put up a dialog from which the user can select 805ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // his/her choice. 806ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon DialogInterface.OnCancelListener cancelListener = 807ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon new DialogInterface.OnCancelListener() { 808ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 809ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onCancel(DialogInterface dialog) { 810ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) { 811ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.d(TAG, "cancelListener.onCancel"); 812ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 813ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon dialog.dismiss(); 814ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 815ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 816ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 817ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 818ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon DialogInterface.OnClickListener clickListener = 819ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon new DialogInterface.OnClickListener() { 820ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 821ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onClick(DialogInterface dialog, int which) { 822ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) { 823ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.d(TAG, "clickListener.onClick " + which); 824ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 825ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon startActivityHelp(intents[which]); 826ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon dialog.dismiss(); 827ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 828ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 829ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 830ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 831ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon DialogInterface.OnClickListener negativeListener = 832ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon new DialogInterface.OnClickListener() { 833ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 834ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onClick(DialogInterface dialog, int which) { 835ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) { 836ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Log.d(TAG, "negativeListener.onClick " + 837ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon which); 838ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 839ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon dialog.dismiss(); 840ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 84164c64e7c25e6efe31fa53369ec49db442b68ff9amah } 842ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 843ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 844ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAlertDialog = 845ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon new AlertDialog.Builder(VoiceDialerActivity.this) 846ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon .setTitle(R.string.title) 847ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon .setItems(sentences, clickListener) 848ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon .setOnCancelListener(cancelListener) 849ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon .setNegativeButton(android.R.string.cancel, 850ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon negativeListener) 851ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon .show(); 852ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 853ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon waitForChoice(); 854538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 855538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 85664c64e7c25e6efe31fa53369ec49db442b68ff9amah }); 85764c64e7c25e6efe31fa53369ec49db442b68ff9amah } 858538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 859632bb9327078cbe2794ff24be8bb28e4f81309f1The Android Open Source Project 860ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private class ChoiceRecognizerClient implements RecognizerClient { 861ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onRecognitionSuccess(final Intent[] intents) { 862ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "ChoiceRecognizerClient onRecognitionSuccess"); 863ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mState != WAITING_FOR_CHOICE) { 864ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "not waiting for choice, ignoring"); 865ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return; 866ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 867ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 868ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mAlertDialog != null) { 869ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAlertDialog.dismiss(); 870ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 871ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 872ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // disregard all but the first intent. 873ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (intents.length > 0) { 874ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String value = intents[0].getStringExtra( 875ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SEMANTIC_EXTRA); 876ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "value " + value); 877ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if ("R".equals(value)) { 878ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 879ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new GreetingRunnable()); 880ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 881ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon listenForCommand(); 882ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 883ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else if ("X".equals(value)) { 884ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 885ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 886ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // it's a phone type response 887ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChosenAction = null; 888ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon for (int i = 0; i < mAvailableChoices.length; i++) { 889ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (value.equalsIgnoreCase( 890ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAvailableChoices[i].getStringExtra( 891ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon CommandRecognizerEngine.PHONE_TYPE_EXTRA))) { 892ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mChosenAction = mAvailableChoices[i]; 893ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 894ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 895ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 896ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mChosenAction != null) { 897ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon performChoice(); 898ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 899ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // invalid choice 900ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "invalid choice" + value); 901ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 902ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 903ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.remove(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID); 904ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(getString(R.string.invalid_choice_tts), 905ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_FLUSH, 906ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 907ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 908ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon waitForChoice(); 909ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 910ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 911ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 912ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 913ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 914ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onRecognitionFailure(String msg) { 915ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "ChoiceRecognizerClient onRecognitionFailure"); 916ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 917ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 918ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 919ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onRecognitionError(String err) { 920ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "ChoiceRecognizerClient onRecognitionError"); 921ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.post(new ErrorRunnable(R.string.recognition_error)); 922ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon exitActivity(); 923ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 924ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 925ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void onMicrophoneStart(InputStream mic) { 926ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "ChoiceRecognizerClient onMicrophoneStart"); 927ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 928ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 929ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 930ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void speakChoices() { 931ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "speakChoices"); 932ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = SPEAKING_CHOICES; 933ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 934ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String sentenceSpoken = spaceOutDigits( 935ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAvailableChoices[0].getStringExtra( 936ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SENTENCE_EXTRA)); 937ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 938ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // When we have multiple choices, they will be of the form 939ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // "call jack jones at home", "call jack jones on mobile". 940ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Speak the entire first sentence, then the last word from each 941ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // of the remaining sentences. This will come out to something 942ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // like "call jack jones at home mobile or work". 943ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon StringBuilder builder = new StringBuilder(); 944ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(sentenceSpoken); 945ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 946ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int count = mAvailableChoices.length; 947ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon for (int i=1; i < count; i++) { 948ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (i == count-1) { 949ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(" or "); 950ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 951ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(" "); 952ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 953ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String tmpSentence = mAvailableChoices[i].getStringExtra( 954ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon RecognizerEngine.SENTENCE_EXTRA); 955ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String[] words = tmpSentence.trim().split(" "); 956ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(words[words.length-1]); 957ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 958ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, 959ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon CHOICES_UTTERANCE); 960ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(builder.toString(), 961ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_ADD, 962ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 963ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 964ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 965ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 966ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static String spaceOutDigits(String sentenceDisplay) { 967ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // if we have a sentence of the form "dial 123 456 7890", 968ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // we need to insert a space between each digit, otherwise 969ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // the TTS engine will say "dial one hundred twenty three...." 970ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // When there already is a space, we also insert a comma, 971ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // so that it pauses between sections. For the displayable 972ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // sentence "dial 123 456 7890" it will speak 973ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // "dial 1 2 3, 4 5 6, 7 8 9 0" 974ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon char buffer[] = sentenceDisplay.toCharArray(); 975ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon StringBuilder builder = new StringBuilder(); 976ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon boolean buildingNumber = false; 977ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int l = sentenceDisplay.length(); 978ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon for (int index = 0; index < l; index++) { 979ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon char c = buffer[index]; 980ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Character.isDigit(c)) { 981ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (buildingNumber) { 982ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(" "); 983ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 984ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon buildingNumber = true; 985ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(c); 986ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else if (c == ' ') { 987ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (buildingNumber) { 988ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(","); 989ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 990ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(" "); 991ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 992ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 993ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon buildingNumber = false; 994ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon builder.append(c); 995ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 996ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 997ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return builder.toString(); 998ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 999ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 10004a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon private void startActivityHelp(Intent intent) { 1001ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon startActivity(intent); 1002ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1003ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1004ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void listenForCommand() { 1005ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "" 1006ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon + "Command(): MICROPHONE_EXTRA: "+getArg(MICROPHONE_EXTRA)+ 1007ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon ", CONTACTS_EXTRA: "+getArg(CONTACTS_EXTRA)); 1008ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1009ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = WAITING_FOR_COMMAND; 1010ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread = new Thread() { 1011ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 1012ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mCommandEngine.recognize(mCommandClient, 1013ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon VoiceDialerActivity.this, 1014ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon newFile(getArg(MICROPHONE_EXTRA)), 1015ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mSampleRate); 1016ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1017ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 1018ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread.start(); 1019ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1020ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1021ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void listenForChoice() { 1022ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "listenForChoice(): MICROPHONE_EXTRA: " + 1023ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon getArg(MICROPHONE_EXTRA)); 1024ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1025ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = WAITING_FOR_CHOICE; 1026ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread = new Thread() { 1027ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 1028ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mPhoneTypeChoiceEngine.recognize(mChoiceClient, 1029ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon VoiceDialerActivity.this, 1030ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon newFile(getArg(MICROPHONE_EXTRA)), mSampleRate); 1031ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1032ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 1033ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread.start(); 1034ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1035ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1036ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private void exitActivity() { 1037ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon synchronized(this) { 1038ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mState != EXITING) { 1039ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "exitActivity"); 1040ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = SPEAKING_GOODBYE; 1041ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mUsingBluetooth) { 1042ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, 1043ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon GOODBYE_UTTERANCE); 1044ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.speak(getString(R.string.goodbye_tts), 1045ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon TextToSpeech.QUEUE_FLUSH, 1046ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTtsParams); 1047ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // Normally, the activity will finish() after the 1048ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // utterance completes. As a fallback in case the utterance 1049ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // does not complete, post a delayed runnable finish the 1050ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // activity. 1051ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mFallbackRunnable = new OnTtsCompletionRunnable(true); 1052ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(mFallbackRunnable, MAX_TTS_DELAY); 1053ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } else { 1054ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(new Runnable() { 1055ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 1056ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 1057ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1058ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }, EXIT_DELAY); 1059ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1060ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 10614a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 1062ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1063ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1064ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private String getArg(String name) { 1065ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (name == null) return null; 1066ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon String arg = getIntent().getStringExtra(name); 1067ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (arg != null) return arg; 1068ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon arg = SystemProperties.get("app.voicedialer." + name); 1069ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return arg != null && arg.length() > 0 ? arg : null; 1070ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 10714a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon 1072ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private static File newFile(String name) { 1073ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return name != null ? new File(name) : null; 10744a330957ce5190ed86c57e0c65910359b866cba7Martin Hibdon } 1075ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1076ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private int playSound(int toneType) { 1077ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int msecDelay = 1; 1078ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1079ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // use the MediaPlayer to prompt the user 1080ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mToneGenerator != null) { 1081ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mToneGenerator.startTone(toneType); 1082ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon msecDelay = StrictMath.max(msecDelay, 300); 1083ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1084ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // use the Vibrator to prompt the user 1085ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mAudioManager != null && 1086ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { 1087ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon final int VIBRATOR_TIME = 150; 1088ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon final int VIBRATOR_GUARD_TIME = 150; 1089ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon Vibrator vibrator = new Vibrator(); 1090ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon vibrator.vibrate(VIBRATOR_TIME); 1091ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon msecDelay = StrictMath.max(msecDelay, 1092ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon VIBRATOR_TIME + VIBRATOR_GUARD_TIME); 1093ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1094ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1095ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1096ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon return msecDelay; 1097ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1098ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1099ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon protected void onStop() { 1100ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "onStop"); 1101ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1102ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon synchronized(this) { 1103ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mState = EXITING; 1104ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1105ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1106ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mAlertDialog != null) { 1107ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAlertDialog.dismiss(); 1108ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1109ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1110ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // set the volume back to the level it was before we started. 1111ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, 1112ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mBluetoothVoiceVolume, 0); 1113ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mAudioManager.abandonAudioFocus(null); 1114ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1115ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // shut down bluetooth, if it exists 1116ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mBluetoothHeadset != null) { 111733175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mBluetoothHeadset.stopVoiceRecognition(mBluetoothDevice); 111833175fc0fa9e2c51e9749e737a42077854a6e6abJaikumar Ganesh mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); 1119ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mBluetoothHeadset = null; 1120ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1121ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1122ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // shut down recognizer and wait for the thread to complete 1123ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mRecognizerThread != null) { 1124ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread.interrupt(); 1125ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon try { 1126ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread.join(); 1127ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } catch (InterruptedException e) { 1128ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (Config.LOGD) Log.d(TAG, "onStop mRecognizerThread.join exception " + e); 1129ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1130ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mRecognizerThread = null; 1131ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1132ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1133ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // clean up UI 1134ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.removeCallbacks(mMicFlasher); 1135ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.removeMessages(0); 1136ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1137ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon if (mTts != null) { 1138ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.stop(); 1139ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts.shutdown(); 1140ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mTts = null; 1141ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1142ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon unregisterReceiver(mReceiver); 1143ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1144ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon super.onStop(); 1145ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 11469b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon releaseWakeLock(); 11479b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon 1148ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // It makes no sense to have this activity maintain state when in 1149ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon // background. When it stops, it should just be destroyed. 1150ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon finish(); 1151ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1152ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 11539b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon private void acquireWakeLock(Context context) { 11549b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon if (mWakeLock == null) { 11559b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 11569b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VoiceDialer"); 11579b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon mWakeLock.acquire(); 11589b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon } 11599b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon } 11609b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon 11619b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon private void releaseWakeLock() { 11629b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon if (mWakeLock != null) { 11639b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon mWakeLock.release(); 11649b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon mWakeLock = null; 11659b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon } 11669b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon } 11679b8b34f954c8213207b05896a341a2d2e2799693Martin Hibdon 1168ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon private Runnable mMicFlasher = new Runnable() { 1169ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon int visible = View.VISIBLE; 1170ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1171ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon public void run() { 1172ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.microphone_view).setVisibility(visible); 1173ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon findViewById(R.id.state).setVisibility(visible); 1174ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon visible = visible == View.VISIBLE ? View.INVISIBLE : View.VISIBLE; 1175ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon mHandler.postDelayed(this, 750); 1176ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon } 1177ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon }; 1178ed873c21cd8ad85df735ec841b147f5ac0f740a9Martin Hibdon 1179538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project @Override 1180538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project protected void onDestroy() { 1181ef5d3e8cd31873c7af4902986ae61b408d0343bbMartin Hibdon if (Config.LOGD) Log.d(TAG, "onDestroy"); 1182538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project super.onDestroy(); 1183538879a6f24ff7305e66d719e751b5d612029bf3The Android Open Source Project } 118451d490c50f83eef5c547e8b8f5004e5f22232c71Jaikumar Ganesh} 1185