Skip to content

Instantly share code, notes, and snippets.

@halilozercan
Created January 16, 2020 13:02
Show Gist options
  • Save halilozercan/94915f623d8dfb352b166af1fd88a102 to your computer and use it in GitHub Desktop.
Save halilozercan/94915f623d8dfb352b166af1fd88a102 to your computer and use it in GitHub Desktop.
fun updateAudioDeviceState() {
// Check if any Bluetooth headset is connected. The internal BT state will
// change accordingly.
if (bluetoothManager.state == State.HEADSET_AVAILABLE || bluetoothManager.state == State.HEADSET_UNAVAILABLE || bluetoothManager.state == State.SCO_DISCONNECTING) {
bluetoothManager.updateDevice()
}
// Update the set of available audio devices.
val newAudioDevices: MutableSet<AudioDevice> =
HashSet()
if (bluetoothManager.state == State.SCO_CONNECTED || bluetoothManager.state == State.SCO_CONNECTING || bluetoothManager.state == State.HEADSET_AVAILABLE) {
newAudioDevices.add(AudioDevice.BLUETOOTH)
}
if (hasWiredHeadset) { // If a wired headset is connected, then it is the only possible option.
newAudioDevices.add(AudioDevice.WIRED_HEADSET)
} else {
// No wired headset, hence the audio-device list can contain speaker // phone (on a tablet), or speaker phone and earpiece (on mobile phone).
newAudioDevices.add(AudioDevice.SPEAKER_PHONE)
if (hasEarpiece()) {
newAudioDevices.add(AudioDevice.EARPIECE)
}
}
// Store state which is set to true if the device list has changed.
var audioDeviceSetUpdated = audioDevices != newAudioDevices
// Update the existing audio device set.
audioDevices = newAudioDevices
// Correct user selected audio devices if needed.
if (bluetoothManager.state == State.HEADSET_UNAVAILABLE
&& userSelectedAudioDevice == AudioDevice.BLUETOOTH
) { // If BT is not available, it can't be the user selection.
userSelectedAudioDevice = AudioDevice.NONE
}
// If user selected speaker phone, but then plugged wired headset then make
// wired headset as user selected device.
if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) {
userSelectedAudioDevice = AudioDevice.WIRED_HEADSET
}
// If user selected wired headset, but then unplugged wired headset then make
// speaker phone as user selected device.
if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) {
userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE
}
// Need to start Bluetooth if it is available and user either selected it explicitly or
// user did not select any output device.
val needBluetoothAudioStart =
(bluetoothManager.state == State.HEADSET_AVAILABLE
&& (userSelectedAudioDevice == AudioDevice.NONE
|| userSelectedAudioDevice == AudioDevice.BLUETOOTH))
// Need to stop Bluetooth audio if user selected different device and
// Bluetooth SCO connection is established or in the process.
val needBluetoothAudioStop =
((bluetoothManager.state == State.SCO_CONNECTED
|| bluetoothManager.state == State.SCO_CONNECTING)
&& (userSelectedAudioDevice != AudioDevice.NONE
&& userSelectedAudioDevice != AudioDevice.BLUETOOTH))
// Start or stop Bluetooth SCO connection given states set earlier.
if (needBluetoothAudioStop) {
bluetoothManager.stopScoAudio()
bluetoothManager.updateDevice()
}
if (needBluetoothAudioStart && !needBluetoothAudioStop) { // Attempt to start Bluetooth SCO audio (takes a few second to start).
if (!bluetoothManager.startScoAudio()) { // Remove BLUETOOTH from list of available devices since SCO failed.
audioDevices.remove(AudioDevice.BLUETOOTH)
audioDeviceSetUpdated = true
}
}
// Update selected audio device.
val newAudioDevice = when {
// If a Bluetooth is connected, then it should be used as output audio
// device. Note that it is not sufficient that a headset is available;
// an active SCO channel must also be up and running.
bluetoothManager.state == State.SCO_CONNECTED -> {
AudioDevice.BLUETOOTH
}
// If a wired headset is connected, but Bluetooth is not, then wired headset is used as
// audio device.
hasWiredHeadset -> {
AudioDevice.WIRED_HEADSET
}
// No wired headset and no Bluetooth, hence the audio-device list can contain speaker
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
// |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE
// depending on the user's selection.
else -> {
defaultAudioDevice
}
}
// Switch to new device but only if there has been any changes.
if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) { // Do the required device switch.
setAudioDeviceInternal(newAudioDevice)
onAudioDeviceChanged?.invoke(selectedAudioDevice, audioDevices)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment