Simplified audio focus requesting by using androidx media

Do not play "busy" and "error" sounds when call is closed
Always use audio usage type USAGE_VOICE_COMMUNICATION
This commit is contained in:
Juha Heinanen
2022-02-06 16:33:50 +02:00
parent d57b2d2ae6
commit ef4a73af70
2 changed files with 37 additions and 81 deletions
+1
View File
@@ -64,4 +64,5 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.activity:activity-ktx:1.4.0" implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.fragment:fragment-ktx:1.4.1" implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation 'androidx.media:media:1.4.3'
} }
@@ -2,7 +2,6 @@ package com.tutpro.baresip
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.* import android.app.*
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothManager
@@ -26,6 +25,9 @@ import androidx.annotation.Keep
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.media.AudioAttributesCompat
import androidx.media.AudioFocusRequestCompat
import androidx.media.AudioManagerCompat
import java.io.File import java.io.File
import java.net.InetAddress import java.net.InetAddress
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@@ -59,8 +61,7 @@ class BaresipService: Service() {
private lateinit var hotSpotReceiver: BroadcastReceiver private lateinit var hotSpotReceiver: BroadcastReceiver
private var rtTimer: Timer? = null private var rtTimer: Timer? = null
private var audioFocusRequest: AudioFocusRequest? = null private var audioFocusRequest: AudioFocusRequestCompat? = null
private var audioFocusUsage = -1
private var origVolume = mutableMapOf<Int, Int>() private var origVolume = mutableMapOf<Int, Int>()
private var btAdapter: BluetoothAdapter? = null private var btAdapter: BluetoothAdapter? = null
private var linkAddresses = mutableMapOf<String, String>() private var linkAddresses = mutableMapOf<String, String>()
@@ -229,14 +230,13 @@ class BaresipService: Service() {
when (state) { when (state) {
BluetoothHeadset.STATE_CONNECTED -> { BluetoothHeadset.STATE_CONNECTED -> {
Log.d(TAG, "Bluetooth headset is connected") Log.d(TAG, "Bluetooth headset is connected")
if (isAudioFocused()) { if (audioFocusRequest != null)
// Without delay, SCO_AUDIO_STATE_CONNECTING -> // Without delay, SCO_AUDIO_STATE_CONNECTING ->
// SCO_AUDIO_STATE_DISCONNECTED // SCO_AUDIO_STATE_DISCONNECTED
Timer("Sco", false).schedule(1000) { Timer("Sco", false).schedule(1000) {
Log.d(TAG, "Starting Bluetooth SCO") Log.d(TAG, "Starting Bluetooth SCO")
am.startBluetoothSco() am.startBluetoothSco()
} }
}
} }
BluetoothHeadset.STATE_DISCONNECTED -> { BluetoothHeadset.STATE_DISCONNECTED -> {
Log.d(TAG, "Bluetooth headset is disconnected") Log.d(TAG, "Bluetooth headset is disconnected")
@@ -277,7 +277,6 @@ class BaresipService: Service() {
} }
AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> { AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> {
Log.d(TAG, "Bluetooth headset SCO is disconnected") Log.d(TAG, "Bluetooth headset SCO is disconnected")
abandonAudioFocus()
} }
AudioManager.SCO_AUDIO_STATE_ERROR -> { AudioManager.SCO_AUDIO_STATE_ERROR -> {
Log.d(TAG, "Bluetooth headset SCO state ERROR") Log.d(TAG, "Bluetooth headset SCO state ERROR")
@@ -407,8 +406,7 @@ class BaresipService: Service() {
stopMediaPlayer() stopMediaPlayer()
if (am.mode != AudioManager.MODE_IN_COMMUNICATION) if (am.mode != AudioManager.MODE_IN_COMMUNICATION)
am.mode = AudioManager.MODE_IN_COMMUNICATION am.mode = AudioManager.MODE_IN_COMMUNICATION
requestAudioFocus(AudioManager.STREAM_VOICE_CALL, requestAudioFocus(AudioAttributes.CONTENT_TYPE_SPEECH)
AudioAttributes.CONTENT_TYPE_SPEECH)
setCallVolume() setCallVolume()
proximitySensing(true) proximitySensing(true)
Api.ua_answer(uap, callp, vidMode) Api.ua_answer(uap, callp, vidMode)
@@ -588,8 +586,7 @@ class BaresipService: Service() {
stopMediaPlayer() stopMediaPlayer()
if (am.mode != AudioManager.MODE_IN_COMMUNICATION) if (am.mode != AudioManager.MODE_IN_COMMUNICATION)
am.mode = AudioManager.MODE_IN_COMMUNICATION am.mode = AudioManager.MODE_IN_COMMUNICATION
requestAudioFocus(AudioManager.STREAM_VOICE_CALL, requestAudioFocus(AudioAttributes.CONTENT_TYPE_SPEECH)
AudioAttributes.CONTENT_TYPE_SPEECH)
setCallVolume() setCallVolume()
proximitySensing(true) proximitySensing(true)
return return
@@ -796,12 +793,12 @@ class BaresipService: Service() {
Log.d(TAG, "AoR $aor call $callp is closed") Log.d(TAG, "AoR $aor call $callp is closed")
stopRinging() stopRinging()
stopMediaPlayer() stopMediaPlayer()
when (ev[2]) { /* when (ev[2]) {
"busy" -> "busy" ->
playMedia(R.raw.busy, 1) playMedia(R.raw.busy, 1)
"error" -> "error" ->
playMedia(R.raw.error, 1) playMedia(R.raw.error, 1)
} } */
call!!.remove() call!!.remove()
if (Call.calls().size == 0) { if (Call.calls().size == 0) {
resetCallVolume() resetCallVolume()
@@ -1101,51 +1098,6 @@ class BaresipService: Service() {
return spannable return spannable
} }
private fun requestAudioFocus(usage: Int, type: Int) {
if (audioFocusUsage != -1) {
if (audioFocusUsage == usage)
return
else
abandonAudioFocus()
}
if ((VERSION.SDK_INT >= 26) && (audioFocusRequest == null)) {
@TargetApi(26)
audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.run {
setAudioAttributes(AudioAttributes.Builder().run {
setUsage(usage)
setContentType(type)
build()
})
build()
}
@TargetApi(26)
if (am.requestAudioFocus(audioFocusRequest!!) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audio focus granted for usage $usage")
audioFocusUsage = usage
if (isBluetoothHeadsetConnected() && !am.isBluetoothScoOn) {
Log.d(TAG, "Starting Bluetooth SCO")
am.startBluetoothSco()
}
} else {
Log.d(TAG, "Audio focus denied")
audioFocusRequest = null
audioFocusUsage = -1
}
} else {
@Suppress("DEPRECATION")
if (am.requestAudioFocus(null, usage, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) ==
AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audio focus granted for usage $usage")
audioFocusUsage = usage
if (isBluetoothHeadsetConnected() && !am.isBluetoothScoOn)
am.startBluetoothSco()
} else {
Log.d(TAG, "Audio focus denied")
audioFocusUsage = -1
}
}
}
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
private fun isBluetoothHeadsetConnected(): Boolean { private fun isBluetoothHeadsetConnected(): Boolean {
@@ -1157,37 +1109,39 @@ class BaresipService: Service() {
true true
} }
private fun isAudioFocused(): Boolean { private fun requestAudioFocus(type: Int) {
return audioFocusUsage != -1 if (audioFocusRequest != null && audioFocusRequest!!.audioAttributesCompat.contentType == type)
return
val attributes = AudioAttributesCompat.Builder()
.setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
.setContentType(type)
.build()
audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
.setAudioAttributes(attributes)
.setOnAudioFocusChangeListener { }
.build()
if (AudioManagerCompat.requestAudioFocus(am, audioFocusRequest!!) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audio focus granted")
if (isBluetoothHeadsetConnected() && !am.isBluetoothScoOn) {
Log.d(TAG, "Starting Bluetooth SCO")
am.startBluetoothSco()
}
} else {
Log.d(TAG, "Audio focus denied")
audioFocusRequest = null
}
} }
private fun abandonAudioFocus() { private fun abandonAudioFocus() {
if (VERSION.SDK_INT >= 26) { if (audioFocusRequest != null) {
if (audioFocusRequest != null) { AudioManagerCompat.abandonAudioFocusRequest(am, audioFocusRequest!!)
if (am.abandonAudioFocusRequest(audioFocusRequest!!) == audioFocusRequest = null
AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.d(TAG, "Audio focus abandoned")
Log.d(TAG, "Audio focus abandoned")
audioFocusRequest = null
audioFocusUsage = -1
} else {
Log.d(TAG, "Failed to abandon audio focus")
}
}
} else {
if (audioFocusUsage != -1) {
@Suppress("DEPRECATION")
if (am.abandonAudioFocus(null) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audio focus abandoned")
audioFocusUsage = -1
} else {
Log.d(TAG, "Failed to abandon audio focus")
}
}
} }
} }
private fun startRinging() { private fun startRinging() {
requestAudioFocus(AudioManager.STREAM_RING, AudioAttributes.CONTENT_TYPE_MUSIC) requestAudioFocus(AudioAttributes.CONTENT_TYPE_MUSIC)
am.mode = AudioManager.MODE_RINGTONE am.mode = AudioManager.MODE_RINGTONE
if (VERSION.SDK_INT >= 28) { if (VERSION.SDK_INT >= 28) {
rt.isLooping = true rt.isLooping = true
@@ -1223,6 +1177,7 @@ class BaresipService: Service() {
} }
} }
@Suppress("UNUSED")
private fun playMedia(raw: Int, count: Int) { private fun playMedia(raw: Int, count: Int) {
if (mediaPlayer == null ) { if (mediaPlayer == null ) {
mediaPlayer = MediaPlayer.create(this, raw) mediaPlayer = MediaPlayer.create(this, raw)