mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-26 06:58:37 +00:00
Fixed ANRs in voice message recording/playback + Allow voice recording Bluetooth playback + allow voice message recording using headset/headphones/hearing aids/bluetooth device
This commit is contained in:
parent
de32e634a0
commit
ccd7bcea62
2 changed files with 62 additions and 21 deletions
|
|
@ -424,30 +424,36 @@ class ChatMessageContentData(
|
||||||
|
|
||||||
private fun initVoiceRecordPlayer() {
|
private fun initVoiceRecordPlayer() {
|
||||||
Log.i("[Voice Recording] Creating player for voice record")
|
Log.i("[Voice Recording] Creating player for voice record")
|
||||||
// In case no headphones/headset is connected, use speaker sound card to play recordings, otherwise use earpiece
|
// In case no headphones/headset/hearing aid/bluetooth is connected, use speaker sound card to play recordings, otherwise use earpiece
|
||||||
// If none are available, default one will be used
|
// If none are available, default one will be used
|
||||||
var headphonesCard: String? = null
|
var headphonesCard: String? = null
|
||||||
|
var bluetoothCard: String? = null
|
||||||
var speakerCard: String? = null
|
var speakerCard: String? = null
|
||||||
var earpieceCard: String? = null
|
var earpieceCard: String? = null
|
||||||
for (device in coreContext.core.audioDevices) {
|
for (device in coreContext.core.audioDevices) {
|
||||||
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
|
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
|
||||||
when (device.type) {
|
when (device.type) {
|
||||||
|
AudioDevice.Type.Headphones, AudioDevice.Type.Headset, AudioDevice.Type.HearingAid -> {
|
||||||
|
headphonesCard = device.id
|
||||||
|
}
|
||||||
|
AudioDevice.Type.Bluetooth -> {
|
||||||
|
bluetoothCard = device.id
|
||||||
|
}
|
||||||
AudioDevice.Type.Speaker -> {
|
AudioDevice.Type.Speaker -> {
|
||||||
speakerCard = device.id
|
speakerCard = device.id
|
||||||
}
|
}
|
||||||
AudioDevice.Type.Earpiece -> {
|
AudioDevice.Type.Earpiece -> {
|
||||||
earpieceCard = device.id
|
earpieceCard = device.id
|
||||||
}
|
}
|
||||||
AudioDevice.Type.Headphones, AudioDevice.Type.Headset -> {
|
|
||||||
headphonesCard = device.id
|
|
||||||
}
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i("[Voice Recording] Found headset/headphones sound card [$headphonesCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
Log.i("[Voice Recording] Found headset/headphones/hearingAid sound card [$headphonesCard], bluetooth sound card [$bluetoothCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
||||||
|
val playbackSoundCard = headphonesCard ?: bluetoothCard ?: speakerCard ?: earpieceCard
|
||||||
|
Log.i("[Voice Recording] Using device $playbackSoundCard to make the voice message playback")
|
||||||
|
|
||||||
val localPlayer = coreContext.core.createLocalPlayer(headphonesCard ?: speakerCard ?: earpieceCard, null, null)
|
val localPlayer = coreContext.core.createLocalPlayer(playbackSoundCard, null, null)
|
||||||
if (localPlayer != null) {
|
if (localPlayer != null) {
|
||||||
voiceRecordingPlayer = localPlayer
|
voiceRecordingPlayer = localPlayer
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,32 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
} else {
|
} else {
|
||||||
recorderParams.fileFormat = RecorderFileFormat.Wav
|
recorderParams.fileFormat = RecorderFileFormat.Wav
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In case no headphones/headset/hearing aid/bluetooth is connected, use microphone
|
||||||
|
// If none are available, default one will be used
|
||||||
|
var bluetoothAudioDevice: AudioDevice? = null
|
||||||
|
var headsetAudioDevice: AudioDevice? = null
|
||||||
|
var builtinMicrophone: AudioDevice? = null
|
||||||
|
for (device in coreContext.core.audioDevices) {
|
||||||
|
if (device.hasCapability(AudioDevice.Capabilities.CapabilityRecord)) {
|
||||||
|
when (device.type) {
|
||||||
|
AudioDevice.Type.Bluetooth -> {
|
||||||
|
bluetoothAudioDevice = device
|
||||||
|
}
|
||||||
|
AudioDevice.Type.Headset, AudioDevice.Type.HearingAid, AudioDevice.Type.Headphones -> {
|
||||||
|
headsetAudioDevice = device
|
||||||
|
}
|
||||||
|
AudioDevice.Type.Microphone -> {
|
||||||
|
builtinMicrophone = device
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i("[Chat Message Sending] Found headset/headphones/hearingAid [${headsetAudioDevice?.id}], bluetooth [${bluetoothAudioDevice?.id}] and builtin microphone [${builtinMicrophone?.id}]")
|
||||||
|
recorderParams.audioDevice = headsetAudioDevice ?: bluetoothAudioDevice ?: builtinMicrophone
|
||||||
|
Log.i("[Chat Message Sending] Using device ${recorderParams.audioDevice?.id} to make the voice message recording")
|
||||||
|
|
||||||
recorder = coreContext.core.createRecorder(recorderParams)
|
recorder = coreContext.core.createRecorder(recorderParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,14 +313,14 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tickerFlowRecording() = flow {
|
private fun tickerFlowRecording() = flow {
|
||||||
while (recorder.state == RecorderState.Running) {
|
while (isVoiceRecording.value == true) {
|
||||||
emit(Unit)
|
emit(Unit)
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tickerFlowPlaying() = flow {
|
private fun tickerFlowPlaying() = flow {
|
||||||
while (voiceRecordingPlayer.state == Player.State.Playing) {
|
while (isPlayingVoiceRecording.value == true) {
|
||||||
emit(Unit)
|
emit(Unit)
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
|
|
@ -353,14 +379,15 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
isVoiceRecording.value = true
|
isVoiceRecording.value = true
|
||||||
sendMessageEnabled.value = true
|
sendMessageEnabled.value = true
|
||||||
|
|
||||||
|
val maxVoiceRecordDuration = corePreferences.voiceRecordingMaxDuration
|
||||||
tickerFlowRecording().onEach {
|
tickerFlowRecording().onEach {
|
||||||
val duration = recorder.duration
|
withContext(Dispatchers.Main) {
|
||||||
voiceRecordingDuration.postValue(recorder.duration % voiceRecordingProgressBarMax)
|
val duration = recorder.duration
|
||||||
formattedDuration.postValue(SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration)) // duration is in ms
|
voiceRecordingDuration.value = recorder.duration % voiceRecordingProgressBarMax
|
||||||
|
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration) // duration is in ms
|
||||||
|
|
||||||
if (duration >= corePreferences.voiceRecordingMaxDuration) {
|
if (duration >= maxVoiceRecordDuration) {
|
||||||
withContext(Dispatchers.Main) {
|
Log.w("[Chat Message Sending] Max duration for voice recording exceeded (${maxVoiceRecordDuration}ms), stopping.")
|
||||||
Log.w("[Chat Message Sending] Max duration for voice recording exceeded (${corePreferences.voiceRecordingMaxDuration}ms), stopping.")
|
|
||||||
stopVoiceRecording()
|
stopVoiceRecording()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -435,7 +462,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
isPlayingVoiceRecording.value = true
|
isPlayingVoiceRecording.value = true
|
||||||
|
|
||||||
tickerFlowPlaying().onEach {
|
tickerFlowPlaying().onEach {
|
||||||
voiceRecordPlayingPosition.postValue(voiceRecordingPlayer.currentPosition)
|
withContext(Dispatchers.Main) {
|
||||||
|
voiceRecordPlayingPosition.value = voiceRecordingPlayer.currentPosition
|
||||||
|
}
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -456,30 +485,36 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
|
|
||||||
private fun initVoiceRecordPlayer() {
|
private fun initVoiceRecordPlayer() {
|
||||||
Log.i("[Chat Message Sending] Creating player for voice record")
|
Log.i("[Chat Message Sending] Creating player for voice record")
|
||||||
// In case no headphones/headset is connected, use speaker sound card to play recordings, otherwise use earpiece
|
// In case no headphones/headset/hearing aid/bluetooth is connected, use speaker sound card to play recordings, otherwise use earpiece
|
||||||
// If none are available, default one will be used
|
// If none are available, default one will be used
|
||||||
var headphonesCard: String? = null
|
var headphonesCard: String? = null
|
||||||
|
var bluetoothCard: String? = null
|
||||||
var speakerCard: String? = null
|
var speakerCard: String? = null
|
||||||
var earpieceCard: String? = null
|
var earpieceCard: String? = null
|
||||||
for (device in coreContext.core.audioDevices) {
|
for (device in coreContext.core.audioDevices) {
|
||||||
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
|
if (device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
|
||||||
when (device.type) {
|
when (device.type) {
|
||||||
|
AudioDevice.Type.Headphones, AudioDevice.Type.Headset, AudioDevice.Type.HearingAid -> {
|
||||||
|
headphonesCard = device.id
|
||||||
|
}
|
||||||
|
AudioDevice.Type.Bluetooth -> {
|
||||||
|
bluetoothCard = device.id
|
||||||
|
}
|
||||||
AudioDevice.Type.Speaker -> {
|
AudioDevice.Type.Speaker -> {
|
||||||
speakerCard = device.id
|
speakerCard = device.id
|
||||||
}
|
}
|
||||||
AudioDevice.Type.Earpiece -> {
|
AudioDevice.Type.Earpiece -> {
|
||||||
earpieceCard = device.id
|
earpieceCard = device.id
|
||||||
}
|
}
|
||||||
AudioDevice.Type.Headphones, AudioDevice.Type.Headset -> {
|
|
||||||
headphonesCard = device.id
|
|
||||||
}
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i("[Chat Message Sending] Found headset/headphones sound card [$headphonesCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
Log.i("[Chat Message Sending] Found headset/headphones/hearingAid sound card [$headphonesCard], bluetooth sound card [$bluetoothCard], speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
||||||
|
val playbackSoundCard = headphonesCard ?: bluetoothCard ?: speakerCard ?: earpieceCard
|
||||||
|
Log.i("[Chat Message Sending] Using device $playbackSoundCard to make the voice message playback")
|
||||||
|
|
||||||
val localPlayer = coreContext.core.createLocalPlayer(headphonesCard ?: speakerCard ?: earpieceCard, null, null)
|
val localPlayer = coreContext.core.createLocalPlayer(playbackSoundCard, null, null)
|
||||||
if (localPlayer != null) {
|
if (localPlayer != null) {
|
||||||
voiceRecordingPlayer = localPlayer
|
voiceRecordingPlayer = localPlayer
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue