Fixed several audio focus/routing/volume control issues
This commit is contained in:
@ -50,7 +50,7 @@ import kotlin.math.roundToInt
|
||||
class BaresipService: Service() {
|
||||
|
||||
internal lateinit var intent: Intent
|
||||
internal lateinit var am: AudioManager
|
||||
private lateinit var am: AudioManager
|
||||
private lateinit var nt: Ringtone
|
||||
private lateinit var nm: NotificationManager
|
||||
private lateinit var snb: NotificationCompat.Builder
|
||||
@ -94,7 +94,7 @@ class BaresipService: Service() {
|
||||
filesPath = filesDir.absolutePath
|
||||
pName = packageName
|
||||
|
||||
am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
|
||||
val ntUri = RingtoneManager.getActualDefaultRingtoneUri(applicationContext,
|
||||
RingtoneManager.TYPE_NOTIFICATION)
|
||||
@ -253,12 +253,12 @@ class BaresipService: Service() {
|
||||
BluetoothHeadset.STATE_CONNECTED -> {
|
||||
Log.d(TAG, "Bluetooth headset is connected")
|
||||
if (audioFocusRequest != null)
|
||||
startBluetoothSco(am, 1000L, 3)
|
||||
startBluetoothSco(applicationContext, 1000L, 3)
|
||||
}
|
||||
BluetoothHeadset.STATE_DISCONNECTED -> {
|
||||
Log.d(TAG, "Bluetooth headset is disconnected")
|
||||
if (audioFocusRequest != null)
|
||||
stopBluetoothSco(am)
|
||||
stopBluetoothSco(applicationContext)
|
||||
}
|
||||
|
||||
}
|
||||
@ -287,6 +287,7 @@ class BaresipService: Service() {
|
||||
}
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> {
|
||||
Log.d(TAG, "Bluetooth headset SCO is disconnected")
|
||||
resetCallVolume()
|
||||
}
|
||||
AudioManager.SCO_AUDIO_STATE_ERROR -> {
|
||||
Log.d(TAG, "Bluetooth headset SCO state ERROR")
|
||||
@ -465,7 +466,6 @@ class BaresipService: Service() {
|
||||
val vidMode = intent.getIntExtra("video", Api.VIDMODE_OFF)
|
||||
stopRinging()
|
||||
stopMediaPlayer()
|
||||
//requestAudioFocus(applicationContext, am, AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
setCallVolume()
|
||||
proximitySensing(true)
|
||||
Api.ua_answer(uap, callp, vidMode)
|
||||
@ -677,8 +677,15 @@ class BaresipService: Service() {
|
||||
}
|
||||
"call incoming" -> {
|
||||
val peerUri = ev[1]
|
||||
var missed = false
|
||||
if (!Utils.checkPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO))) {
|
||||
toast(getString(R.string.no_calls))
|
||||
missed = true
|
||||
} else if (!requestAudioFocus(applicationContext)) {
|
||||
toast(getString(R.string.audio_focus_denied))
|
||||
missed = true
|
||||
}
|
||||
if (missed) {
|
||||
if (ua.account.callHistory) {
|
||||
CallHistory.add(CallHistory(aor, peerUri, "in"))
|
||||
CallHistory.save()
|
||||
@ -889,8 +896,9 @@ class BaresipService: Service() {
|
||||
call.remove()
|
||||
if (!Call.inCall()) {
|
||||
resetCallVolume()
|
||||
abandonAudioFocus(am)
|
||||
Utils.clearCommunicationDevice(am)
|
||||
if (!abandonAudioFocus(applicationContext))
|
||||
Log.e(TAG, "Failed to abandon audio focus")
|
||||
am.mode = MODE_NORMAL
|
||||
proximitySensing(false)
|
||||
}
|
||||
val missed = call.startTime == null && call.dir == "in" && !call.rejected
|
||||
@ -1190,7 +1198,6 @@ class BaresipService: Service() {
|
||||
}
|
||||
|
||||
private fun startRinging() {
|
||||
requestAudioFocus(applicationContext, am, AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
if (rt == null) {
|
||||
val rtUri = RingtoneManager.getActualDefaultRingtoneUri(
|
||||
applicationContext,
|
||||
@ -1305,7 +1312,7 @@ class BaresipService: Service() {
|
||||
}
|
||||
|
||||
private fun setCallVolume() {
|
||||
if (callVolume != 0 && origVolume.isEmpty()) {
|
||||
if (callVolume != 0)
|
||||
for (streamType in listOf(AudioManager.STREAM_MUSIC, AudioManager.STREAM_VOICE_CALL)) {
|
||||
origVolume[streamType] = am.getStreamVolume(streamType)
|
||||
val maxVolume = am.getStreamMaxVolume(streamType)
|
||||
@ -1313,15 +1320,14 @@ class BaresipService: Service() {
|
||||
Log.d(TAG, "Orig/new/max $streamType volume is " +
|
||||
"${origVolume[streamType]}/${am.getStreamVolume(streamType)}/$maxVolume")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetCallVolume() {
|
||||
for ((streamType, streamVolume) in origVolume) {
|
||||
am.setStreamVolume(streamType, streamVolume, 0)
|
||||
Log.d(TAG, "Reset $streamType volume to ${am.getStreamVolume(streamType)}")
|
||||
}
|
||||
origVolume.clear()
|
||||
if (callVolume != 0)
|
||||
for ((streamType, streamVolume) in origVolume) {
|
||||
am.setStreamVolume(streamType, streamVolume, 0)
|
||||
Log.d(TAG, "Reset $streamType volume to ${am.getStreamVolume(streamType)}")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
@ -1499,7 +1505,7 @@ class BaresipService: Service() {
|
||||
this.unregisterReceiver(bluetoothReceiver)
|
||||
this.unregisterReceiver(hotSpotReceiver)
|
||||
stopRinging()
|
||||
abandonAudioFocus(am)
|
||||
abandonAudioFocus(applicationContext)
|
||||
stopMediaPlayer()
|
||||
uas.clear()
|
||||
callHistory.clear()
|
||||
@ -1562,53 +1568,44 @@ class BaresipService: Service() {
|
||||
private var audioFocusRequest: AudioFocusRequestCompat? = null
|
||||
private var btAdapter: BluetoothAdapter? = null
|
||||
|
||||
fun requestAudioFocus(ctx: Context, am: AudioManager, type: Int): Boolean {
|
||||
Log.d(TAG, "Requesting audio focus of type $type")
|
||||
fun requestAudioFocus(ctx: Context): Boolean {
|
||||
Log.d(TAG, "Requesting audio focus")
|
||||
if (audioFocusRequest != null) {
|
||||
if (audioFocusRequest!!.audioAttributesCompat.contentType == type) {
|
||||
Log.d(TAG, "Already focused")
|
||||
am.mode = MODE_IN_COMMUNICATION
|
||||
return true
|
||||
} else {
|
||||
abandonAudioFocus(am)
|
||||
}
|
||||
Log.d(TAG, "Already focused")
|
||||
return true
|
||||
}
|
||||
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val attributes = AudioAttributesCompat.Builder()
|
||||
.setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(type)
|
||||
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributesCompat.CONTENT_TYPE_SPEECH)
|
||||
.build()
|
||||
audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
|
||||
audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
|
||||
.setAudioAttributes(attributes)
|
||||
.setOnAudioFocusChangeListener { }
|
||||
.build()
|
||||
if (AudioManagerCompat.requestAudioFocus(am, audioFocusRequest!!) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
Log.d(TAG, "Audio focus granted")
|
||||
isAudioFocused = true
|
||||
if (isBluetoothHeadsetConnected(ctx) /* && !am.isBluetoothScoOn */) {
|
||||
Log.d(TAG, "Starting Bluetooth SCO")
|
||||
startBluetoothSco(am, 10L, 1)
|
||||
}
|
||||
am.mode = MODE_IN_COMMUNICATION
|
||||
//if (type == AudioAttributes.CONTENT_TYPE_SPEECH || isBluetoothHeadsetConnected(ctx))
|
||||
//am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
Log.d(TAG, "requestAudioFocus granted")
|
||||
if (isBluetoothHeadsetConnected(ctx))
|
||||
startBluetoothSco(ctx, 250L, 3)
|
||||
} else {
|
||||
Log.i(TAG, "Audio focus denied")
|
||||
Log.w(TAG, "requestAudioFocus denied")
|
||||
audioFocusRequest = null
|
||||
isAudioFocused = false
|
||||
}
|
||||
return isAudioFocused
|
||||
return audioFocusRequest != null
|
||||
}
|
||||
|
||||
private fun abandonAudioFocus(am: AudioManager) {
|
||||
fun abandonAudioFocus(ctx: Context): Boolean {
|
||||
if (audioFocusRequest != null) {
|
||||
Log.d(TAG, "Abandoning audio focus")
|
||||
AudioManagerCompat.abandonAudioFocusRequest(am, audioFocusRequest!!)
|
||||
audioFocusRequest = null
|
||||
isAudioFocused = false
|
||||
stopBluetoothSco(am)
|
||||
Utils.clearCommunicationDevice(am)
|
||||
am.mode = MODE_NORMAL
|
||||
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
if (AudioManagerCompat.abandonAudioFocusRequest(am, audioFocusRequest!!) ==
|
||||
AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
audioFocusRequest = null
|
||||
if (isBluetoothHeadsetConnected(ctx))
|
||||
stopBluetoothSco(ctx)
|
||||
}
|
||||
}
|
||||
return audioFocusRequest == null
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@ -1631,22 +1628,33 @@ class BaresipService: Service() {
|
||||
false
|
||||
}
|
||||
|
||||
private fun startBluetoothSco(am: AudioManager, delay: Long, count: Int) {
|
||||
private fun startBluetoothSco(ctx: Context, delay: Long, count: Int) {
|
||||
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
if (isBluetoothScoOn(am)) {
|
||||
Log.d(TAG, "Bluetooth SCO is already on")
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "Starting Bluetooth SCO at count $count")
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
am.isBluetoothScoOn = true
|
||||
if (VERSION.SDK_INT < 31) {
|
||||
am.startBluetoothSco()
|
||||
} else {
|
||||
Utils.setCommunicationDevice(am, AudioDeviceInfo.TYPE_BLUETOOTH_SCO)
|
||||
}
|
||||
if (!isBluetoothScoOn(am) && count > 1)
|
||||
startBluetoothSco(am, delay, count - 1)
|
||||
startBluetoothSco(ctx, delay, count - 1)
|
||||
else
|
||||
am.isBluetoothScoOn = true
|
||||
}, delay)
|
||||
}
|
||||
|
||||
private fun stopBluetoothSco(am: AudioManager) {
|
||||
private fun stopBluetoothSco(ctx: Context) {
|
||||
Log.d(TAG, "Stopping Bluetooth SCO")
|
||||
val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
if (!isBluetoothScoOn(am)) {
|
||||
Log.d(TAG, "Bluetooth SCO is already off")
|
||||
return
|
||||
}
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
if (VERSION.SDK_INT < 31)
|
||||
am.stopBluetoothSco()
|
||||
|
||||
@ -10,7 +10,6 @@ import android.content.*
|
||||
import android.content.Intent.ACTION_CALL
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
@ -169,7 +168,7 @@ class MainActivity : AppCompatActivity() {
|
||||
am = getSystemService(AUDIO_SERVICE) as AudioManager
|
||||
kgm = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
|
||||
serviceEventObserver = Observer<Event<Long>> {
|
||||
serviceEventObserver = Observer {
|
||||
val event = it.getContentIfNotHandled()
|
||||
Log.d(TAG, "Observed event $event")
|
||||
if (event != null && BaresipService.serviceEvents.isNotEmpty()) {
|
||||
@ -352,7 +351,7 @@ class MainActivity : AppCompatActivity() {
|
||||
callButton.setOnClickListener {
|
||||
if (aorSpinner.selectedItemPosition >= 0) {
|
||||
if (Utils.checkPermissions(this, arrayOf(RECORD_AUDIO)))
|
||||
makeCall()
|
||||
makeCall()
|
||||
else
|
||||
Toast.makeText(applicationContext, R.string.no_calls, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@ -364,6 +363,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (callRunnable != null) {
|
||||
callHandler.removeCallbacks(callRunnable!!)
|
||||
callRunnable = null
|
||||
BaresipService.abandonAudioFocus(applicationContext)
|
||||
am.mode = AudioManager.MODE_NORMAL
|
||||
showCall(ua)
|
||||
return@setOnClickListener
|
||||
@ -372,6 +372,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (audioModeChangedListener != null) {
|
||||
am.removeOnModeChangedListener(audioModeChangedListener!!)
|
||||
audioModeChangedListener = null
|
||||
BaresipService.abandonAudioFocus(applicationContext)
|
||||
am.mode = AudioManager.MODE_NORMAL
|
||||
showCall(ua)
|
||||
return@setOnClickListener
|
||||
@ -972,12 +973,12 @@ class MainActivity : AppCompatActivity() {
|
||||
AudioManager.STREAM_MUSIC
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||
Log.d(TAG, "Adjusting volume $keyCode of stream $stream")
|
||||
am.adjustStreamVolume(stream,
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
|
||||
AudioManager.ADJUST_LOWER else
|
||||
AudioManager.ADJUST_RAISE,
|
||||
AudioManager.FLAG_SHOW_UI)
|
||||
Log.d(TAG, "Adjusted volume $keyCode of stream $stream to ${am.getStreamVolume(stream)}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1803,6 +1804,8 @@ class MainActivity : AppCompatActivity() {
|
||||
onHoldCall.newCall = null
|
||||
call.remove()
|
||||
call.destroy()
|
||||
if (!BaresipService.abandonAudioFocus(applicationContext))
|
||||
Log.e(TAG, "Failed to abandon audio focus")
|
||||
showCall(ua)
|
||||
false
|
||||
}
|
||||
@ -1863,7 +1866,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val uri = if (Utils.isTelUri(uriText)) {
|
||||
if (ua.account.telProvider == "") {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
String.format(getString(R.string.no_telephony_provider), aor))
|
||||
String.format(getString(R.string.no_telephony_provider), aor))
|
||||
return
|
||||
}
|
||||
Utils.telToSip(uriText, ua.account)
|
||||
@ -1872,7 +1875,10 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
if (!Utils.checkUri(uri)) {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
|
||||
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
|
||||
} else if (!BaresipService.requestAudioFocus(applicationContext)) {
|
||||
Toast.makeText(applicationContext, R.string.audio_focus_denied,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
callUri.isFocusable = false
|
||||
uaAdapter.notifyDataSetChanged()
|
||||
@ -1881,24 +1887,16 @@ class MainActivity : AppCompatActivity() {
|
||||
hangupButton.visibility = View.VISIBLE
|
||||
hangupButton.isEnabled = true
|
||||
if (Build.VERSION.SDK_INT < 31) {
|
||||
if (!BaresipService.requestAudioFocus(this, am, AudioAttributes.CONTENT_TYPE_SPEECH)) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.audio_focus_denied,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
callRunnable = Runnable {
|
||||
callRunnable = null
|
||||
if (!call(ua, uri)) {
|
||||
callButton.visibility = View.VISIBLE
|
||||
callButton.isEnabled = true
|
||||
hangupButton.visibility = View.INVISIBLE
|
||||
hangupButton.isEnabled = false
|
||||
}
|
||||
callRunnable = Runnable {
|
||||
callRunnable = null
|
||||
if (!call(ua, uri)) {
|
||||
callButton.visibility = View.VISIBLE
|
||||
callButton.isEnabled = true
|
||||
hangupButton.visibility = View.INVISIBLE
|
||||
hangupButton.isEnabled = false
|
||||
}
|
||||
callHandler.postDelayed(callRunnable!!, 1000)
|
||||
}
|
||||
callHandler.postDelayed(callRunnable!!, 1000)
|
||||
} else {
|
||||
audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
|
||||
if (mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
@ -1919,13 +1917,18 @@ class MainActivity : AppCompatActivity() {
|
||||
"device ${am.communicationDevice!!.type}")
|
||||
}
|
||||
}
|
||||
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
|
||||
if (!BaresipService.requestAudioFocus(this, am, AudioAttributes.CONTENT_TYPE_SPEECH))
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.audio_focus_denied,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
if (am.mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
if (!call(ua, uri)) {
|
||||
callButton.visibility = View.VISIBLE
|
||||
callButton.isEnabled = true
|
||||
hangupButton.visibility = View.INVISIBLE
|
||||
hangupButton.isEnabled = false
|
||||
}
|
||||
} else {
|
||||
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
|
||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -986,7 +986,7 @@ object Utils {
|
||||
Log.d(TAG, "New com dev/mode ${am.communicationDevice!!.type}/${am.mode}")
|
||||
}
|
||||
|
||||
fun clearCommunicationDevice(am: AudioManager) {
|
||||
private fun clearCommunicationDevice(am: AudioManager) {
|
||||
if (Build.VERSION.SDK_INT > 33) {
|
||||
am.clearCommunicationDevice()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user