1

I have a little app where I want to be able to send TTS audio to speaker or bluetooth headset if connected. It is working fine in the speaker phone mode, but no luck with BT. Any idea how this could be addressed? Code snippet is below:'

private void speak(String s){

        HashMap<String, String> hash = new HashMap<String, String>(); 

        hash.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_SYSTEM));



        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

        audioManager.setMode(AudioManager.MODE_NORMAL);
        audioManager.startBluetoothSco();
        audioManager.setBluetoothScoOn(true);

        tts.speak(s, TextToSpeech.QUEUE_ADD, hash);



     }

'

user2109066
  • 77
  • 1
  • 8

1 Answers1

3

Use my answer at Using the Android RecognizerIntent with a bluetooth headset

In your activity that you declare private void speak(String s) add the a class member

BluetoothHeadsetUtils mBluetoothHelper;
TelephonyManager mTelManager;
MyPhoneStateListener mPhoneStateListener = new MyPhoneStateListener();

On this same activity onCreate() add

@Override
public void onCreate()
{
     mBluetoothHelper = new BluetoothHeadsetUtils(this);
     mTelManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
     mTelManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}  

And onResume() and onPause of the activity add

@Override
onResume()
{ 
    mBluetoothHelper.start();
}

@Override
onPause()
{
    mBluetoothHelper.stop();
}

You can define your speak(String s) as

public void speak(String s, String utteranceId) // utteranceId can be an empty string
{
    HashMap<String, String> myHashRender = new HashMap<String, String>();
    myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
    if (mBluetoothHelper.isOnHeadsetSco())
    {
        myHashRender.put(TextToSpeech.Engine.KEY_PARAM_STREAM, 
                String.valueOf(AudioManager.STREAM_VOICE_CALL));
    }
    tts.speak(s, TextToSpeech.QUEUE_ADD, myHashRender);
}

Instead of broadcast receiver use phone state listener

protected class MyPhoneStateListener extends PhoneStateListener
{

    @Override
    public void onCallStateChanged(int state, String incomingNumber)
    {

                switch (state)
                {
                    case TelephonyManager.CALL_STATE_RINGING:

                            mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
                            speak("whatever you want here", "ringing"); //$NON-NLS-1$

                            Log.d(TAG, "ringing cancel recognizer");  //$NON-NLS-1$

                        break;

                    case TelephonyManager.CALL_STATE_OFFHOOK:


                            Log.d(TAG, "offhook cancel recognizer");  //$NON-NLS-1$

                        break;

                    case TelephonyManager.CALL_STATE_IDLE:


                        new Thread(new Runnable()
                        {   
                            @Override
                            public void run()
                            {
                                try
                                {
                                    Thread.sleep(2000);
                                }
                                catch (InterruptedException e)
                                {

                                }
                                mBluetoothHelper.start();
                            }
                        }).start();
                        Log.d(TAG, "call state idle");  //$NON-NLS-1$

            }


    }
}

In your activity or service add implement OnUtteranceCompletedListener and add the code below

@Override
public void onUtteranceCompleted(String utteranceId)
{
    if (utteranceId.equals("ringing"))
    {
        mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
        mBluetoothHelper.stop();
    }
}
Community
  • 1
  • 1
Hoan Nguyen
  • 18,033
  • 3
  • 50
  • 54
  • If you use my solution at http://stackoverflow.com/questions/14991158/using-the-android-recognizerintent-with-a-bluetooth-headset/14993590#14993590. Make sure you get the uptodate answer, which I just update a few minutes ago. Override onScoAudioConnected() and put your tts.speak inside the if (mBluetoothHelper.isOnHeadsetSco()) where mBluetoothHelper = new BluetoothHelper(this); – Hoan Nguyen Mar 10 '13 at 20:19
  • same problem after trying suggested changes. – user2109066 Mar 11 '13 at 05:07
  • The audio is not established right away when calling startBluetoothSco. Thus when you call tts.speak right after it will fail. – Hoan Nguyen Mar 11 '13 at 05:11
  • the link is a bit buggy..: `BluetoothHeadsetUtils mBluetoothHeadsetUtils; @Override public void onCreate() { mBluetoothHelper = new BluetoothHelper(this); }` also ` public boolean isOnHeadsetSco() { return mIsOnHeadsetSco; } ` mIsOnHeadsetSco is undefined. Do you have simpler examples by any chance? – user2109066 Mar 11 '13 at 05:18
  • Give me a few minutes to update the answer there. I am afraid it can not be simpler. – Hoan Nguyen Mar 11 '13 at 05:25
  • not much luck either. Also, i may hear the end of the message on the BT only after i answer the call, but i need this message to be played before i answer the call. – user2109066 Mar 11 '13 at 06:30
  • do you make an out call or receiving call? – Hoan Nguyen Mar 11 '13 at 06:34
  • where do you put your speak call? should we move to chat? – Hoan Nguyen Mar 11 '13 at 06:54
  • i do not have enough status for chat, also it is getting late hear, maybe tomorrow? – user2109066 Mar 11 '13 at 07:00
  • Ok, I wrote the bluetooth helper class to use with tts and speech recognition for out going call only. but I got some idea when you have time, just make a comment here and I will open a chat room. – Hoan Nguyen Mar 11 '13 at 07:03
  • i've checked the latest update and do not see how i can use it – user2109066 Mar 12 '13 at 03:51
  • Do you get audio through the headset now? – Hoan Nguyen Mar 12 '13 at 03:59
  • where do i add tts.speak ? – user2109066 Mar 12 '13 at 04:03
  • First, I want to see if you can get audio through headset, so forget about the phone call for now, just override onScoAudioConnected() and in there put speak("voice through headset") with the speak method define as above and see if you heard it through your headset. – Hoan Nguyen Mar 12 '13 at 04:23
  • looks like it is working, but somehow not in the way i was expecting< let me play a bit – user2109066 Mar 12 '13 at 04:34
  • Ok, where do put your speak call when there is an incoming call? Do you implement a PhoneStateListener? – Hoan Nguyen Mar 12 '13 at 04:35
  • okay, here is what i see. If i send SMS, my app is able to play what i want using the described above method, however, for the incoming call Bluetooth terminal is making its own audio announcement. – user2109066 Mar 12 '13 at 04:42
  • Also, i am not sure how to invoke your class, this is what i am doing in my code: – user2109066 Mar 12 '13 at 04:42
  • ` mBluetoothHeadsetUtils = new BluetoothHeadsetUtils(this);` and in the BluetoothHeadsetUtils i have following code `public void onScoAudioConnected() { Log.d(TAG, "onScoAudioConnected"); //$NON-NLS-1$ // Load preferences SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); // Call a service, since this could take a few seconds// Call a service, since this could take a few seconds Intent mIntent = new Intent(mContext, MyTTSCallerIDIntentService.class); – user2109066 Mar 12 '13 at 04:42
  • mIntent.putExtra("SMS flag", "true"); mIntent.putExtra("incoming_number", "voice through headset"); mIntent.putExtra("SMSSenderName", "1234"); mIntent.putExtra("SMSMessage", "voice through headset"); mContext.startService(mIntent); }` – user2109066 Mar 12 '13 at 04:43
  • If you want to override onScoAudioConnected() then you have to create an inner class: private class MyBluetoothHelper extends BluetoothHeadsetUtils and inside this class override the onScoAudioConnected method. Of course you have to declare the member mBluetoothHelper as MyBluetoothHelper mbluetoothHelper. – Hoan Nguyen Mar 12 '13 at 04:48
  • Anyway, you may not need to extend BluetoothHeadsetUtils at all, just use the speak method above anywhere and you will be fine. Now I want to know how do you know when a phone call comes in. Do you implement a phone state listener? – Hoan Nguyen Mar 12 '13 at 04:51
  • not sure i understand everything you said, but this is how i get phone call: – user2109066 Mar 12 '13 at 05:00
  • and then i have broadcast receiver: `@Override public void onReceive(Context context, Intent intent) {` – user2109066 Mar 12 '13 at 05:01
  • So you will get state ringing, state offhook and state idle? – Hoan Nguyen Mar 12 '13 at 05:03
  • On state ringing you have to put mAudioManager.setStreamMute(AudioManager.STREAM_RING, true); speak(whatever you want to speak here, utteranceId); You have to change the speak method above a bit and also have to implement onUtteranceCompleted. I will change the speak code above. – Hoan Nguyen Mar 12 '13 at 05:10
  • In onUtteranceCompleted if the utteranceId is equal to the utteranceId, say "Phone Ringing", you pass in on the speak method in the state ringing receiver, then you call mBluetoothHelper.stop() there as well as mAudioManager.setStreamMute(AudioManager.STREAM_RING, false); . – Hoan Nguyen Mar 12 '13 at 05:19
  • Then on phone state idle you have to call mBluetoothHelper.start() – Hoan Nguyen Mar 12 '13 at 05:19
  • looks pretty much the same – user2109066 Mar 12 '13 at 05:20
  • i am bit confused here, if you can provide a little bit more detailed code snippet, i will appreciate – user2109066 Mar 12 '13 at 05:22
  • any luck here with example? – user2109066 Mar 12 '13 at 06:16
  • there should be something else besides mBluetoothHelper = new BluetoothHeadsetUtils(this);, how my tts.speak gets anything from mBluetoothHelper, shouldn't it be something like mBluetoothHelper.saysomething();??? – user2109066 Mar 12 '13 at 06:22
  • No the BluetoothHeadsetUtils is to establish audio connection with the headset. Once the audio is established, isOnHeadsetSco() returns true. So you only need to call isOnHeadsetSco() to make sure you have establish audio connection, which we did in the speak method. – Hoan Nguyen Mar 12 '13 at 06:26
  • I miss a closing parenthesis on the if statement in the speak method, just fixed it a moment ago. – Hoan Nguyen Mar 12 '13 at 06:30
  • 03-11 23:30:29.814: E/AndroidRuntime(4549): java.lang.NullPointerException 03-11 23:30:29.814: E/AndroidRuntime(4549): at com.slavuta_vs.myttscallerid.MyTTSCallerIDIntentService$1.speak(MyTTSCallerIDIntentService.java:357) – user2109066 Mar 12 '13 at 06:31
  • and the actual code is if (mBluetoothHeadsetUtils.isOnHeadsetSco()){ tts.speak(s, TextToSpeech.QUEUE_ADD, hash); } – user2109066 Mar 12 '13 at 06:32
  • Just use the the exact method speak(String text, String utteranceId) above and passing in an empty string utteranceId for other call beside the one in the ringing state. – Hoan Nguyen Mar 12 '13 at 06:39
  • I tested in my app when the phone is ringing and it work fine. I got the default calling window on, but no ringing in the beginning and heard "phone is ringing" which is my speak parameter. – Hoan Nguyen Mar 12 '13 at 06:42
  • actually i do not want to use it this way, i do not want to have application running, as my app is to announce incoming calls and SMSs, thats why i am using broadcast receiver. – user2109066 Mar 12 '13 at 06:45
  • If you do not implement onUtteranceCompleted then how do you mute and unmute the ringing. If you do not mute the ringing, you have the ringing sound and the tts voice at the same time. Just try to speak on ringing and mBluetoothHelper.start() on idle in your way to see if it work. You have to use the speak method I provided otherwise you will hear no sound. – Hoan Nguyen Mar 12 '13 at 06:52
  • I am OK with mixing ringing and TTS announcement. My app does not replace, just complement the regular service. When user is driving (for example) and receiving the call, i want to give him an opportunity to hear the name of the person who is calling, but it is OK to have ring tone as well.. – user2109066 Mar 12 '13 at 07:05
  • The ringing may drown out the tts voice. You should implement your app as a service. – Hoan Nguyen Mar 12 '13 at 07:07
  • when the telephone is ringing I can not hear TTS in bluetooth headset otherwise I can hear it. Why? I am also using AudioManager.STREAM_VOICE_CALL. Problem is only when phone is ringing. I am testing on android 4 – senzacionale Jul 16 '13 at 08:49
  • I have the same problem, the ringing drown out the tts. The stream for the ringing is hidden and thus one cannot mute it. Even if one can find out the integer represents the stream, Google or the phone company can easily changes it again. – Hoan Nguyen Jul 16 '13 at 17:50
  • so that means no solution :( – senzacionale Jul 18 '13 at 06:19
  • So far as I know, if I have time I will dig into the source code to see if there is any work around. – Hoan Nguyen Jul 18 '13 at 06:27
  • that would be awesome. I can help you if you want. – senzacionale Jul 18 '13 at 07:22
  • @Hoan Nguyen what about A2DP. Is it possible to use TTS in bleutooth with A2DP and not Sco? Will then work when phone is ringing? – senzacionale Jul 18 '13 at 18:56
  • There is a BluetoothA2dp class for API >= 11, you could try to see if it work – Hoan Nguyen Jul 18 '13 at 19:20
  • @Hoan Nguyen anything new about TTS when phone is ringing. I still didn't find working solution :( – senzacionale Sep 12 '13 at 16:57
  • I am busy working on something else. When I have time I will look at this problem and if finding a solution I will let you know. – Hoan Nguyen Sep 12 '13 at 17:59