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