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:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user