I make a player that should play only through headphones (wired or Bluetooth), but not through the speaker.
If you turn off the headphones during playback, the player automatically pauses.
When (Bluetooth) headphones are connected back and I get the ACTION_AUDIO_STATE_CHANGED event, I resume playback. But for a few seconds, the sound goes through the builtin speaker and only then goes to the headphones. I think to fix this by setting the device to output. I can get the device id from AudioManager. But how to pass it to OpenSL ES?
Or maybe there is a way to completely prevent playback through the builtin speaker?
Or another way to solve this problem?
P.S. Santa, where is you when you are so needed? Help me, please!
Maybe you could listen to this other type of events with a BroadcastReceiver:
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED -> {
state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)
when (state) {
BluetoothA2dp.STATE_CONNECTED -> {
// Do something
}
BluetoothA2dp.STATE_DISCONNECTED -> {
// Do something else
}
}
}
Related
I have my phone connected to a Bluetooth speaker and the headphones plugged in. Now I'd like to play audio through the Bluetooth speaker.
When I set the audio stream to AudioManager.STREAM_MUSIC it just plays over the headphones.
It doesn't matter if it plays on the headphones as well but I need it to play on the Bluetooth speaker.
How is this possible? The app SoundAbout manages to do that so there must be a way.
EDIT: When I plug in the headphones and only afterwards connect to the Bluetooth speakers all audio plays through the Bluetooth speakers which I want. But I can't expect the user to find that out and before having to show them a complicated message I'd rather find out a way to make the sound always play through BT speakers when connected to some.
Thanks
(Note this is not the same question as this: How to Play audio through speaker even when headset is plugged in?
I want it to play on Bluetooth speakers, not on the integrated speaker of the phone.)
Solution
Suppose you already tested STREAM_RING on your new instance of media player and not directly setting stream type, and it didn't work out, You need a correct profile for your bluetooth device.
Take a look at this article
Read the "Implementing HAL" section, there is alot of source for different profile that you may be able to use.
There is also an easy solution which is to change your device profile to HEADSET in your getServiceConnected() method, it will turn into a Stay connected device but the output will become mono! As I recall, Which is a shame for speakers, A2DP also may not be supported in some hardwares and still interrupted by wire headsets.
I suggest to create a new profile and use it, a little bit tricky working with HAL but will worth it,
Sorry that I can not provide a source code for you at the moment.
If you have your routing logic within your application then based on that you can decide on which output the audio to be played.
I have a test app written for the exact purpose.
My Github Link
You can also route audio as you want based on a requirement.You can refer this github link for routing
Bluetooth connection may work with below state is true .
After receive BluetoothA2dp.STATE_CONNECTED, you can play music as normal.
Java Code Examples for android.bluetooth.BluetoothA2dp.STATE_CONNECTED
public BluetoothHandsfree(Context context, CallManager cm) {
mCM = cm;
mContext = context;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
boolean bluetoothCapable = (adapter != null);
mHeadset = null; // nothing connected yet
mA2dp = new BluetoothA2dp(mContext);
mA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
mA2dpDevice = null;
mA2dpSuspended = false;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":StartCall");
mStartCallWakeLock.setReferenceCounted(false);
mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":VoiceRecognition");
mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
mLocalBrsf = BRSF_AG_THREE_WAY_CALLING |
BRSF_AG_EC_NR |
BRSF_AG_REJECT_CALL |
BRSF_AG_ENHANCED_CALL_STATUS;
if (sVoiceCommandIntent == null) {
sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (mContext.getPackageManager().resolveActivity(sVoiceCommandIntent, 0) != null &&
BluetoothHeadset.isBluetoothVoiceDialingEnabled(mContext)) {
mLocalBrsf |= BRSF_AG_VOICE_RECOG;
}
mBluetoothPhoneState = new BluetoothPhoneState();
mUserWantsAudio = true;
mPhonebook = new BluetoothAtPhonebook(mContext, this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
cdmaSetSecondCallState(false);
if (bluetoothCapable) {
resetAtState();
}
}
please find below links : with sample codes it may help you.
Java Code Examples for android.bluetooth.BluetoothHeadset
Programmatically connect to paired Bluetooth speaker and play audio
You need to instantiate a new object of class MediaPlayer and use the following method on it
amediaplayer.setAudioStreamType(AudioManager.STREAM_RING)
Do not forget to check authorization to use bluetooth, you are unable to send anything to speaker via bluetooth without user privilege as you know.
Audiomanager overrides and routes audio to the latest connected device(either wired headset or bluetooth headset). In android, we do not have any option to override this setting unless it is a system app and route the audio wherever we wish to route. However, you can use reflection apis and override this setting. Audiomanager suspends bluetooth connection route(if already connected)if wired headset is connected and vice versa. You can look at the code here.
Hence using reflection apis you can toggle bluetooth audio route by invoking this method.
In my Android app, the user gets to specifically select the audio output between internal speakers and earphone. Here is my routine to turn the speaker phone on:
static void useSpeaker(Context ctx) {
AudioManager am = (AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_NORMAL);
am.setSpeakerphoneOn(true);
}
Once the audio is set to speakers, even if you plug in the earphone, the output still goes through the speaker. I have verified on a number of different tablets and phones that this logic works.
However, on one device, once the earphone is plugged in, the audio output automatically switches from speakers to earphone.
I am wondering if there is something else that I need to take care of in the code. Or, is it just this device ignoring my directive? Regards.
Im trying to build an app that can enable or disable the headphone jack on my Android 4.4 device . Once disabled nothing should be able to take control or activate it, even calling. I can root the device for this.
Any help would be appreciated.
As written here: How to mute audio in headset but let it play on speaker programmatically?
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_CALL);
am.setSpeakerphoneOn(false);
And then play the sound through the AudioManager.STREAM_SYSTEM stream.
When the sound's finished playing be sure to return the audio manager to its previous state or it'll stay on loudspeaker.
After I use the Android AudioManager to set SCO ON and speakerphone ON, then I return it back to where it started, the media stream will then stay on earpiece instead of the phones speaker. Here is where I make the changes to route a stream through the SCO speakerphone (which all works great)
if (am2.isBluetoothScoAvailableOffCall()) {
am2.startBluetoothSco();
}
if(!am2.isSpeakerphoneOn()){
speakerPhoneWasOn = false;
am2.setSpeakerphoneOn(true);
}
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_VOICE_CALL));
am2.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
Then I try to change it all back after my message is read (I am using TTS)
if(SMSstream == 1){
if (am2.isBluetoothScoAvailableOffCall()) {
am2.stopBluetoothSco();
}
if(!speakerPhoneWasOn){
am2.setSpeakerphoneOn(false);
}
}
else{
}
am2.setMode(OLD_AUDIO_MODE);
am2.abandonAudioFocus(null);
But it stays with the phone earpiece instead of the speaker until I reboot. I saw a few post that had the opposite problem but none with this issue. I have a Droid 3 with Android 2.3.4.
You can see the whole project and source code here: http://code.google.com/p/a2dpvolume/
OK, I finally fixed this problem. Its not pretty. There is some sort of bug in the AudioManager I believe. Even after abandoning focus it would leave the device streams in a mess. Unless the last TTS was read over the music stream just before abandoning focus, it would route streams wrong, mute streams, etc. So, I just have it read a single period if a SMS had been read over any stream except the music stream before abandoning focus. I also cleaned up the order I called things in, and fixed some of the phone mode, etc after reading a TTS. Strangely it seems to work well.
My Android tutorial states that I can explicitly tell the TTS engine which stream to use:
For music playback:
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_MUSIC));
And for phone calls:
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_VOICE_CALL));
My understanding is that audio routing to a Bluetooth headset works such that STREAM_MUSIC goes to A2DP (aka "media audio" in Android Bluetooth settings) and STREAM_VOICE_CALL goes to HSP (aka "phone audio" in Android Bluetooth settings).
But regardless whether I use STREAM_MUSIC or STREAM_VOICE_CALL in my little application, the audio always goes for some reason to A2DP.
What am I am doing wrong? Is there a way to route TTS output to headset's HSP profile?
I got this working for the most part, on most devices. Here is the part that starts the TTS on the voice call stream using Bluetooth SCO instead of A2DP.
if (mTtsReady) {
myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "A2DP_Vol");
OLD_AUDIO_MODE = am2.getMode();
if(SMSstream == 1){
if (am2.isBluetoothScoAvailableOffCall()) {
am2.startBluetoothSco();
}
if(!am2.isSpeakerphoneOn()){
speakerPhoneWasOn = false;
am2.setSpeakerphoneOn(true);
}
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_VOICE_CALL));
am2.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
else{
am2.requestAudioFocus(null, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_MUSIC));
}
new CountDownTimer(SMS_DELAY, SMS_DELAY/2) {
#Override
public void onFinish() {
try {
mTts.speak(str, TextToSpeech.QUEUE_ADD,
myHash);
} catch (Exception e) {
Toast.makeText(application,
R.string.TTSNotReady,
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
#Override
public void onTick(long arg0) {
}
}.start();
}
Now I just have a problem getting the stream to revert back when done. It all works fine to read TTS. It will pause any music, play the TTS, and then resume music fine. However, when I exit the app later the stream for media now plays through the phone earpiece until I reboot. I posted that question here: Audio stream stays on earpiece after using AudioManager
You can see my whole project here: http://code.google.com/p/a2dpvolume/
If your headset is compatible with the a2dp profile, then using AudioManager.STREAM_MUSIC and hence playing audio through the stream should get the job done.
I would also add, that if you are currently in a call, and you play audio through the voice stream, then any headset (a2dp or otherwise) can hear the audio. Unfortunately, you need to be in a call.
Unfortunately I have found that setting the mode to MODE_IN_CALL does nothing.
To sum it up:
If all that you are trying to do is play music (when not in a call), then use the AudioManager.STREAM_MUSIC and if the headset is A2DP compatible, then it will hear the music.
Also, take a look at AudioManager.isBluetoothA2dpOn(), to make sure that the system thinks that your headeset is plugged in.
Yes it is possible to play the TTS (text-to-speech) output through a non A2DP headset like jroal says by using SCO.
Use AudioManager startBluetoothSco to enable SCO. Then listen for AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED broadcast with the EXTRA_SCO_AUDIO_STATE set to SCO_AUDIO_STATE_CONNECTED.
If you now do TextToSpeech to the STREAM_VOICE_CALL stream, it will end up in the headset (even the cheap non A2DP devices).