我的倒计时应用程序使用文本转语音来提供音频倒计时。
当应用程序检测到蓝牙扬声器时,音频会发送到该扬声器。
问题三星 Watch 4 和 TicWatch Pro 3 等可穿戴设备被报告为耳机!
例如我原来的 onCreate() 包括:
// Check if a speaker is connected
if (bluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED)
sendSpeechToSpeaker()
else
sendSpeechToPhone()
问题1 是否有对上述问题的简单修复,仅检测连接的耳机?
我的解决方法包括单独检查每个连接的蓝牙设备并忽略那些不是耳机的设备
问题2 有人可以建议一个比我的解决方法更简单的方法吗?
解决方法
在初始化期间,检查每个已连接的蓝牙设备,如果它们实际上是耳机,则重新路由音频
btServiceListener 检查每个已连接的设备
val btServiceListener: ServiceListener = object : ServiceListener {
// used to scan all CONNECTED Bluetooth devices looking for external speakers ...
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.HEADSET) {
val connectionStates = intArrayOf(BluetoothProfile.STATE_CONNECTED)
// get all connected headsets
val connectedHeadsetList = proxy.getDevicesMatchingConnectionStates(connectionStates)
for (connectedHeadset in connectedHeadsetList) {
// check each headset and check if it is ACTUALLY a headset
val majorMask = BluetoothClass.Device.Major.UNCATEGORIZED // actually want to use BITMASK but some Fwit declared it private !
val isHeadset = (connectedHeadset.bluetoothClass?.deviceClass?.and(majorMask) == BluetoothClass.Device.Major.AUDIO_VIDEO)
if (isHeadset)
sendSpeechToSpeaker()
}
}
bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) // here we are finished with the proxy so clear
}
override fun onServiceDisconnected(profile: Int) {}
}
上面的监听器是在onCreate()中调用的
// search the list of connected bluetooth headsets
bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
bluetoothAdapter.getProfileProxy(this, btServiceListener, BluetoothProfile.HEADSET);
监听器使用大调掩码进行工作 耳机的代码是 (0x0400) 三星 Galaxy Watch 可穿戴 (0x0700) TicWatch Pro 3 GPS 未分类(0x1F00)
为了简单起见,我没有显示 BTListener 中所需的相同代码,以确保手表断开连接时不会将音频路由到扬声器!
当我重新访问这段代码时,我发现了一个更简单的解决方案。在传递给文本转语音引擎的包中选择 AudioManager.STREAM_MUSIC。
初始化
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
ttsBundle = Bundle()
ttsBundle.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_MUSIC)
textToSpeech = TextToSpeech(this) { status ->
if (status == TextToSpeech.SUCCESS) {
Log.d(TAG, "TTS initialisation successful")
} else {
Log.d(TAG, "TTS initialisation failed")
}
使用
textToSpeech.speak("Text sent to device loudspeaker or Bluetooth connected speaker", TextToSpeech.QUEUE_FLUSH, ttsBundle, null)
PS我留下了第一个答案,因为我仍在其他应用程序中使用该代码
可以通过直接检查音频属性而不是解析每个连接的蓝牙设备来避免上述问题
private fun routeTTSAudioOutput() {
// on start and when changes are detected ensure audio is being routed correctly
val bluetoothA2DPConnected = audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)
if (bluetoothA2DPConnected) {
if (!bluetoothSCOTurnedOn) audioManager.startBluetoothSco()
audioManager.mode = AudioManager.MODE_IN_CALL
} else {
if (bluetoothSCOTurnedOn) audioManager.stopBluetoothSco()
audioManager.mode = AudioManager.MODE_NORMAL
}
audioManager.isSpeakerphoneOn = !bluetoothA2DPConnected
bluetoothSCOTurnedOn = bluetoothA2DPConnected
bundleTTS = Bundle()
bundleTTS.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, audioManager.mode)
this.runOnUiThread { bluetoothConnectedImageView.visibility = if (bluetoothA2DPConnected) ImageView.VISIBLE else ImageView.INVISIBLE }
}
音频回调使用上述函数将文本路由为语音
private val audioDeviceCallback : AudioDeviceCallback = object : AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
super.onAudioDevicesAdded(addedDevices)
routeTTSAudioOutput()
}
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
super.onAudioDevicesRemoved(removedDevices)
routeTTSAudioOutput()
}
}