Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to switch audio output programatically in android 10?

My use case is I have an app in which a user can press a button and it would toggle between external speaker and the wired headset/bluetooth device. If the user is connected to a wired headset or a bluetooth ear piece device, I want to switch the audio output from that wired headset or bluetooth to the external speaker, and then when they click again, to renable the wired headset/bluetooth audio output.

Does anyone know this can be done in Android 10? I tried the following the example in this post but it doesnt consistently work for bluetooth cases. My code is as followed:

if (shouldEnableExternalSpeaker) {
     audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
     audioManager.isSpeakerphoneOn = true
     if (isBlueToothConnected) {
         audioManager.stopBluetoothScoOn()
     }
} else {
     if (isBlueToothConnected) {
         audioManager.startBluetoothSco()
     }

     audioManager.mode = AudioManager.NORMAL
     audioManager.isSpeakerphoneOn = false
}

I also have the necessary permission to the user's audio:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

like image 477
SnG Avatar asked Feb 04 '23 15:02

SnG


2 Answers

I think you should split your handling into 3 cases, play sound via:

  1. external device via Bluetooth
  2. wired headset/device
  3. phone speaker

The resulting code could then loke like this.

if(shouldEnableExternalSpeaker) {
    if(isBlueToothConnected) {
        // 1. case - bluetooth device
        mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        mAudioManager.startBluetoothSco();
        mAudioManager.setBluetoothScoOn(true);
    } else {
        // 2. case - wired device
        mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        mAudioManager.stopBluetoothSco();
        mAudioManager.setBluetoothScoOn(false);
        mAudioManager.setSpeakerphoneOn(false);
    }
} else {
   // 3. case - phone speaker
   mAudioManager.setMode(AudioManager.MODE_NORMAL);
   mAudioManager.stopBluetoothSco();
   mAudioManager.setBluetoothScoOn(false);
   mAudioManager.setSpeakerphoneOn(true);
}

Even though I haven't used the AudioManager recently, this has worked for me in the past.

like image 123
Lukas Makor Avatar answered Feb 06 '23 14:02

Lukas Makor


Lines:

audioManager.isSpeakerphoneOn = true
audioManager.isSpeakerphoneOn = false

will not work. You have to use setMethods:

Case 1 - bluetooth

mAudioManager.startBluetoothSco(); // This method can be used by applications wanting to send and received audio to/from a bluetooth SCO headset while the phone is not in call. 
mAudioManager.setBluetoothScoOn(true); // set true to use bluetooth SCO for communications; false to not use bluetooth SCO for communications

Case 2 - wired

mAudioManager.stopBluetoothSco(); // stop wanting to send and receive
mAudioManager.setBluetoothScoOn(false); // turn off bluetooth
mAudioManager.setSpeakerphoneOn(false); //set true to turn on speakerphone; false to turn it off

Case 3 - internal speaker

mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
mAudioManager.setSpeakerphoneOn(true); // turn on speaker phone

You can keep things automatically happening as follows:

public class BluetoothReceiver extends BroadcastReceiver {
    private AudioManager localAudioManager;
    private static final int STATE_DISCONNECTED  = 0x00000000;
    private static final String EXTRA_STATE = "android.bluetooth.headset.extra.STATE";
    private static final String TAG = "BluetoothReceiver";
    private static final String ACTION_BT_HEADSET_STATE_CHANGED  = "android.bluetooth.headset.action.STATE_CHANGED";
    private static final String ACTION_BT_HEADSET_FORCE_ON = "android.bluetooth.headset.action.FORCE_ON";
    private static final String ACTION_BT_HEADSET_FORCE_OFF = "android.bluetooth.headset.action.FORCE_OFF";

    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.i(TAG,"onReceive - BluetoothBroadcast");
        localAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        final String action = intent.getAction();
        if (action.equals(ACTION_BT_HEADSET_STATE_CHANGED)) {
            final int extraData = intent.getIntExtra(EXTRA_STATE, STATE_DISCONNECTED);
            if (extraData == STATE_DISCONNECTED) {
                //no headset -> going other modes
                localAudioManager.setBluetoothScoOn(false);
                localAudioManager.stopBluetoothSco();
                localAudioManager.setMode(AudioManager.MODE_NORMAL);
                Log.i(TAG, "Bluetooth Headset Off " + localAudioManager.getMode());
                Log.i(TAG, "A2DP: " + localAudioManager.isBluetoothA2dpOn() + ". SCO: " + localAudioManager.isBluetoothScoAvailableOffCall());
            } else {            
                localAudioManager.setMode(0);
                localAudioManager.setBluetoothScoOn(true);
                localAudioManager.startBluetoothSco();
                localAudioManager.setMode(AudioManager.MODE_IN_CALL);
                Log.i(TAG, "Bluetooth Headset On " + localAudioManager.getMode());
                Log.i(TAG, "A2DP: " + localAudioManager.isBluetoothA2dpOn() + ". SCO: " + localAudioManager.isBluetoothScoAvailableOffCall());
            }
        }   

        if (action.equals(ACTION_BT_HEADSET_FORCE_ON)) {
            localAudioManager.setMode(0);
            localAudioManager.setBluetoothScoOn(true);
            localAudioManager.startBluetoothSco();
            localAudioManager.setMode(AudioManager.MODE_IN_CALL);
            Log.i(TAG, "Bluetooth Headset On " + localAudioManager.getMode());
            Log.i(TAG, "A2DP: " + localAudioManager.isBluetoothA2dpOn() + ". SCO: " + localAudioManager.isBluetoothScoAvailableOffCall());
        }

        if (action.equals(ACTION_BT_HEADSET_FORCE_OFF)) {
            localAudioManager.setBluetoothScoOn(false);
            localAudioManager.stopBluetoothSco();
            localAudioManager.setMode(AudioManager.MODE_NORMAL);
            Log.i(TAG, "Bluetooth Headset Off " + localAudioManager.getMode());
            Log.i(TAG, "A2DP: " + localAudioManager.isBluetoothA2dpOn() + ". SCO: " + localAudioManager.isBluetoothScoAvailableOffCall());
        }
    }
}
like image 37
Nilanka Manoj Avatar answered Feb 06 '23 16:02

Nilanka Manoj