Fixed audio focus requests for voice recording/playing + call record playback and switched to MKV/Opus encoding instead of WAV because it's lighter
This commit is contained in:
parent
c699ddc1e9
commit
2e36f1aa86
5 changed files with 90 additions and 34 deletions
|
@ -30,7 +30,6 @@ import androidx.media.AudioFocusRequestCompat
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.ticker
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -41,7 +40,6 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.ImageUtils
|
||||
import java.lang.Exception
|
||||
|
||||
class ChatMessageContentData(
|
||||
private val chatMessage: ChatMessage,
|
||||
|
@ -74,7 +72,7 @@ class ChatMessageContentData(
|
|||
val formattedDuration = MutableLiveData<String>()
|
||||
val voiceRecordPlayingPosition = MutableLiveData<Int>()
|
||||
val isVoiceRecordPlaying = MutableLiveData<Boolean>()
|
||||
var voiceRecordPlayingAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
|
||||
val isAlone: Boolean
|
||||
get() {
|
||||
|
@ -210,7 +208,7 @@ class ChatMessageContentData(
|
|||
val isVoiceRecord = content.isVoiceRecording
|
||||
filePath.value = path
|
||||
isImage.value = FileUtils.isExtensionImage(path)
|
||||
isVideo.value = FileUtils.isExtensionVideo(path)
|
||||
isVideo.value = FileUtils.isExtensionVideo(path) && !isVoiceRecord
|
||||
isAudio.value = FileUtils.isExtensionAudio(path) && !isVoiceRecord
|
||||
isPdf.value = FileUtils.isExtensionPdf(path)
|
||||
isVoiceRecording.value = isVoiceRecord
|
||||
|
@ -259,8 +257,12 @@ class ChatMessageContentData(
|
|||
initVoiceRecordPlayer()
|
||||
}
|
||||
|
||||
if (voiceRecordPlayingAudioFocusRequest == null) {
|
||||
voiceRecordPlayingAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecording(
|
||||
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
|
||||
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (voiceRecordAudioFocusRequest == null) {
|
||||
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||
coreContext.context
|
||||
)
|
||||
}
|
||||
|
@ -277,10 +279,10 @@ class ChatMessageContentData(
|
|||
voiceRecordingPlayer.pause()
|
||||
}
|
||||
|
||||
val request = voiceRecordPlayingAudioFocusRequest
|
||||
val request = voiceRecordAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecording(coreContext.context, request)
|
||||
voiceRecordPlayingAudioFocusRequest = null
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
voiceRecordAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isVoiceRecordPlaying.value = false
|
||||
|
@ -310,10 +312,6 @@ class ChatMessageContentData(
|
|||
}
|
||||
Log.i("[Voice Recording] Found speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
||||
|
||||
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
|
||||
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
val localPlayer = coreContext.core.createLocalPlayer(speakerCard ?: earpieceCard, null, null)
|
||||
if (localPlayer != null) {
|
||||
voiceRecordingPlayer = localPlayer
|
||||
|
|
|
@ -90,7 +90,7 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
|
||||
val voiceRecordPlayingPosition = MutableLiveData<Int>()
|
||||
|
||||
var voiceRecordPlayingAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
|
||||
private lateinit var voiceRecordingPlayer: Player
|
||||
private val playerListener = PlayerListener {
|
||||
|
@ -108,7 +108,11 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
isReadOnly.value = chatRoom.hasBeenLeft()
|
||||
|
||||
val recorderParams = coreContext.core.createRecorderParams()
|
||||
if (corePreferences.voiceMessagesFormatMkv) {
|
||||
recorderParams.fileFormat = RecorderFileFormat.Mkv
|
||||
} else {
|
||||
recorderParams.fileFormat = RecorderFileFormat.Wav
|
||||
}
|
||||
recorder = coreContext.core.createRecorder(recorderParams)
|
||||
}
|
||||
|
||||
|
@ -279,6 +283,12 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
return
|
||||
}
|
||||
|
||||
if (voiceRecordAudioFocusRequest == null) {
|
||||
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||
coreContext.context
|
||||
)
|
||||
}
|
||||
|
||||
when (recorder.state) {
|
||||
RecorderState.Running -> Log.w("[Chat Message Sending] Recorder is already recording")
|
||||
RecorderState.Paused -> {
|
||||
|
@ -286,7 +296,11 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
recorder.start()
|
||||
}
|
||||
RecorderState.Closed -> {
|
||||
val tempFileName = "voice-recording-${System.currentTimeMillis()}.wav"
|
||||
val extension = when (recorder.params.fileFormat) {
|
||||
RecorderFileFormat.Mkv -> "mkv"
|
||||
else -> "wav"
|
||||
}
|
||||
val tempFileName = "voice-recording-${System.currentTimeMillis()}.$extension"
|
||||
val file = FileUtils.getFileStoragePath(tempFileName)
|
||||
Log.w("[Chat Message Sending] Recorder is closed, starting recording in ${file.absoluteFile}")
|
||||
recorder.open(file.absolutePath)
|
||||
|
@ -329,6 +343,12 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
}
|
||||
}
|
||||
|
||||
val request = voiceRecordAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
voiceRecordAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isPendingVoiceRecord.value = false
|
||||
isVoiceRecording.value = false
|
||||
sendMessageEnabled.value = textToSend.value?.isNotEmpty() == true || attachments.value?.isNotEmpty() == true
|
||||
|
@ -342,6 +362,12 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
voiceRecordingDuration.value = recorder.duration
|
||||
}
|
||||
|
||||
val request = voiceRecordAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
voiceRecordAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isVoiceRecording.value = false
|
||||
if (corePreferences.sendVoiceRecordingRightAway) {
|
||||
Log.i("[Chat Message Sending] Sending voice recording right away")
|
||||
|
@ -355,8 +381,12 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
initVoiceRecordPlayer()
|
||||
}
|
||||
|
||||
if (voiceRecordPlayingAudioFocusRequest == null) {
|
||||
voiceRecordPlayingAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecording(
|
||||
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
|
||||
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (voiceRecordAudioFocusRequest == null) {
|
||||
voiceRecordAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||
coreContext.context
|
||||
)
|
||||
}
|
||||
|
@ -375,10 +405,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
voiceRecordingPlayer.pause()
|
||||
}
|
||||
|
||||
val request = voiceRecordPlayingAudioFocusRequest
|
||||
val request = voiceRecordAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecording(coreContext.context, request)
|
||||
voiceRecordPlayingAudioFocusRequest = null
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
voiceRecordAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isPlayingVoiceRecording.value = false
|
||||
|
@ -401,10 +431,6 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
}
|
||||
Log.i("[Chat Message Sending] Found speaker sound card [$speakerCard] and earpiece sound card [$earpieceCard]")
|
||||
|
||||
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
|
||||
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
val localPlayer = coreContext.core.createLocalPlayer(speakerCard ?: earpieceCard, null, null)
|
||||
if (localPlayer != null) {
|
||||
voiceRecordingPlayer = localPlayer
|
||||
|
@ -431,10 +457,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
|||
voiceRecordingPlayer.close()
|
||||
}
|
||||
|
||||
val request = voiceRecordPlayingAudioFocusRequest
|
||||
val request = voiceRecordAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecording(coreContext.context, request)
|
||||
voiceRecordPlayingAudioFocusRequest = null
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
voiceRecordAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isPlayingVoiceRecording.value = false
|
||||
|
|
|
@ -21,9 +21,12 @@ package org.linphone.activities.main.recordings.viewmodels
|
|||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.media.AudioFocusRequestCompat
|
||||
import kotlin.collections.ArrayList
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.activities.main.recordings.data.RecordingData
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.FileUtils
|
||||
|
||||
class RecordingsViewModel : ViewModel() {
|
||||
|
@ -31,12 +34,26 @@ class RecordingsViewModel : ViewModel() {
|
|||
|
||||
val isVideoVisible = MutableLiveData<Boolean>()
|
||||
|
||||
private var recordingPlayingAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
|
||||
private val recordingListener = object : RecordingData.RecordingListener {
|
||||
override fun onPlayingStarted(videoAvailable: Boolean) {
|
||||
if (recordingPlayingAudioFocusRequest == null) {
|
||||
recordingPlayingAudioFocusRequest = AppUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||
coreContext.context
|
||||
)
|
||||
}
|
||||
|
||||
isVideoVisible.value = videoAvailable
|
||||
}
|
||||
|
||||
override fun onPlayingEnded() {
|
||||
val request = recordingPlayingAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
recordingPlayingAudioFocusRequest = null
|
||||
}
|
||||
|
||||
isVideoVisible.value = false
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +65,13 @@ class RecordingsViewModel : ViewModel() {
|
|||
|
||||
override fun onCleared() {
|
||||
recordingsList.value.orEmpty().forEach(RecordingData::destroy)
|
||||
|
||||
val request = recordingPlayingAudioFocusRequest
|
||||
if (request != null) {
|
||||
AppUtils.releaseAudioFocusForVoiceRecordingOrPlayback(coreContext.context, request)
|
||||
recordingPlayingAudioFocusRequest = null
|
||||
}
|
||||
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
|
|
@ -412,6 +412,11 @@ class CorePreferences constructor(private val context: Context) {
|
|||
val preventInterfaceFromShowingUp: Boolean
|
||||
get() = config.getBool("app", "keep_app_invisible", false)
|
||||
|
||||
// By default we will record voice messages using MKV format and Opus audio encoding
|
||||
// If disabled, WAV format will be used instead. Warning: files will be heavier!
|
||||
val voiceMessagesFormatMkv: Boolean
|
||||
get() = config.getBool("app", "record_voice_messages_in_mkv_format", true)
|
||||
|
||||
/* Default values related */
|
||||
|
||||
val echoCancellerCalibration: Int
|
||||
|
|
|
@ -156,7 +156,7 @@ class AppUtils {
|
|||
return currentVolume <= maxVolume * 0.5
|
||||
}
|
||||
|
||||
fun acquireAudioFocusForVoiceRecording(context: Context): AudioFocusRequestCompat {
|
||||
fun acquireAudioFocusForVoiceRecordingOrPlayback(context: Context): AudioFocusRequestCompat {
|
||||
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val audioAttrs = AudioAttributesCompat.Builder()
|
||||
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
|
||||
|
@ -170,22 +170,25 @@ class AppUtils {
|
|||
.build()
|
||||
when (AudioManagerCompat.requestAudioFocus(audioManager, request)) {
|
||||
AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
|
||||
Log.i("[Audio Focus] Voice recording audio focus request granted")
|
||||
Log.i("[Audio Focus] Voice recording/playback audio focus request granted")
|
||||
}
|
||||
AudioManager.AUDIOFOCUS_REQUEST_FAILED -> {
|
||||
Log.w("[Audio Focus] Voice recording audio focus request failed")
|
||||
Log.w("[Audio Focus] Voice recording/playback audio focus request failed")
|
||||
}
|
||||
AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
|
||||
Log.w("[Audio Focus] Voice recording audio focus request delayed")
|
||||
Log.w("[Audio Focus] Voice recording/playback audio focus request delayed")
|
||||
}
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
fun releaseAudioFocusForVoiceRecording(context: Context, request: AudioFocusRequestCompat) {
|
||||
fun releaseAudioFocusForVoiceRecordingOrPlayback(
|
||||
context: Context,
|
||||
request: AudioFocusRequestCompat
|
||||
) {
|
||||
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request)
|
||||
Log.i("[Audio Focus] Voice recording audio focus request abandoned")
|
||||
Log.i("[Audio Focus] Voice recording/playback audio focus request abandoned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue