More speakerphone and call related enhancements for API 31+

This commit is contained in:
Juha Heinanen
2023-01-24 05:37:56 +02:00
parent afd26f7df9
commit 5195a24d08
4 changed files with 103 additions and 43 deletions
@@ -637,8 +637,6 @@ class BaresipService: Service() {
if (call!!.status == "transferring") if (call!!.status == "transferring")
break break
stopMediaPlayer() stopMediaPlayer()
if (am.mode != AudioManager.MODE_IN_COMMUNICATION)
am.mode = AudioManager.MODE_IN_COMMUNICATION
requestAudioFocus(AudioAttributes.CONTENT_TYPE_SPEECH) requestAudioFocus(AudioAttributes.CONTENT_TYPE_SPEECH)
setCallVolume() setCallVolume()
proximitySensing(true) proximitySensing(true)
@@ -879,7 +877,6 @@ class BaresipService: Service() {
call.remove() call.remove()
if (Call.calls().size == 0) { if (Call.calls().size == 0) {
resetCallVolume() resetCallVolume()
Utils.setSpeakerPhone(am, false)
am.mode = AudioManager.MODE_NORMAL am.mode = AudioManager.MODE_NORMAL
am.stopBluetoothSco() am.stopBluetoothSco()
abandonAudioFocus() abandonAudioFocus()
@@ -112,5 +112,11 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return null return null
} }
fun connectedCall(): Call? {
for (c in BaresipService.calls.reversed())
if (c.status == "connected") return c
return null
}
} }
} }
@@ -10,6 +10,7 @@ import android.content.*
import android.content.Intent.ACTION_CALL import android.content.Intent.ACTION_CALL
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration.ORIENTATION_PORTRAIT import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.media.AudioDeviceInfo
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
@@ -85,11 +86,13 @@ class MainActivity : AppCompatActivity() {
private lateinit var restoreRequest: ActivityResultLauncher<Intent> private lateinit var restoreRequest: ActivityResultLauncher<Intent>
private lateinit var contactsRequest: ActivityResultLauncher<Intent> private lateinit var contactsRequest: ActivityResultLauncher<Intent>
private lateinit var callsRequest: ActivityResultLauncher<Intent> private lateinit var callsRequest: ActivityResultLauncher<Intent>
private lateinit var comDevChangedListener: AudioManager.OnCommunicationDeviceChangedListener
private var callHandler: Handler = Handler(Looper.getMainLooper()) private var callHandler: Handler = Handler(Looper.getMainLooper())
private var callRunnable: Runnable? = null private var callRunnable: Runnable? = null
private var downloadsInputUri: Uri? = null private var downloadsInputUri: Uri? = null
private var downloadsOutputUri: Uri? = null private var downloadsOutputUri: Uri? = null
private var audioModeChangedListener: AudioManager.OnModeChangedListener? = null
private lateinit var baresipService: Intent private lateinit var baresipService: Intent
@@ -188,6 +191,21 @@ class MainActivity : AppCompatActivity() {
addAction(Intent.ACTION_SCREEN_ON) addAction(Intent.ACTION_SCREEN_ON)
}) })
if (Build.VERSION.SDK_INT >= 31) {
comDevChangedListener = AudioManager.OnCommunicationDeviceChangedListener { device ->
if (device != null) {
Log.d(TAG, "Com device changed to type ${device.type} in mode ${am.mode}")
if (speakerIcon != null) {
if (device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
speakerIcon!!.setIcon(R.drawable.speaker_on)
else
speakerIcon!!.setIcon(R.drawable.speaker_off)
}
}
}
am.addOnCommunicationDeviceChangedListener(mainExecutor, comDevChangedListener)
}
uaAdapter = UaSpinnerAdapter(applicationContext, BaresipService.uas) uaAdapter = UaSpinnerAdapter(applicationContext, BaresipService.uas)
aorSpinner.adapter = uaAdapter aorSpinner.adapter = uaAdapter
aorSpinner.setSelection(-1) aorSpinner.setSelection(-1)
@@ -340,6 +358,7 @@ class MainActivity : AppCompatActivity() {
hangupButton.setOnClickListener { hangupButton.setOnClickListener {
val ua = BaresipService.uas[aorSpinner.selectedItemPosition] val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
if (Build.VERSION.SDK_INT < 31) {
if (callRunnable != null && callHandler.hasCallbacks(callRunnable!!)) { if (callRunnable != null && callHandler.hasCallbacks(callRunnable!!)) {
callHandler.removeCallbacks(callRunnable!!) callHandler.removeCallbacks(callRunnable!!)
callRunnable = null callRunnable = null
@@ -347,6 +366,15 @@ class MainActivity : AppCompatActivity() {
showCall(ua) showCall(ua)
return@setOnClickListener return@setOnClickListener
} }
} else {
if (audioModeChangedListener != null) {
am.removeOnModeChangedListener(audioModeChangedListener!!)
audioModeChangedListener = null
am.mode = AudioManager.MODE_NORMAL
showCall(ua)
return@setOnClickListener
}
}
val aor = ua.account.aor val aor = ua.account.aor
val uaCalls = ua.calls() val uaCalls = ua.calls()
if (uaCalls.size > 0) { if (uaCalls.size > 0) {
@@ -744,6 +772,8 @@ class MainActivity : AppCompatActivity() {
super.onDestroy() super.onDestroy()
Log.d(TAG, "Main onDestroy") Log.d(TAG, "Main onDestroy")
this.unregisterReceiver(screenEventReceiver) this.unregisterReceiver(screenEventReceiver)
if (Build.VERSION.SDK_INT >= 31)
am.removeOnCommunicationDeviceChangedListener(comDevChangedListener)
BaresipService.serviceEvent.removeObserver(serviceEventObserver) BaresipService.serviceEvent.removeObserver(serviceEventObserver)
BaresipService.serviceEvents.clear() BaresipService.serviceEvents.clear()
BaresipService.activities.clear() BaresipService.activities.clear()
@@ -1234,13 +1264,7 @@ class MainActivity : AppCompatActivity() {
} }
R.id.speakerIcon -> { R.id.speakerIcon -> {
Utils.toggleSpeakerPhone(am) Utils.toggleSpeakerPhone(mainExecutor, am)
Handler(Looper.getMainLooper()).postDelayed({
if (Utils.isSpeakerPhoneOn(am))
item.setIcon(R.drawable.speaker_on)
else
item.setIcon(R.drawable.speaker_off)
}, 750)
} }
R.id.config -> { R.id.config -> {
@@ -1783,12 +1807,13 @@ class MainActivity : AppCompatActivity() {
) )
} else { } else {
callUri.isFocusable = false callUri.isFocusable = false
am.mode = AudioManager.MODE_IN_COMMUNICATION
uaAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
callButton.isEnabled = false callButton.isEnabled = false
hangupButton.visibility = View.VISIBLE hangupButton.visibility = View.VISIBLE
hangupButton.isEnabled = true hangupButton.isEnabled = true
if (Build.VERSION.SDK_INT < 31) {
am.mode = AudioManager.MODE_IN_COMMUNICATION
callRunnable = Runnable { callRunnable = Runnable {
if (!call(ua, uri)) { if (!call(ua, uri)) {
callButton.visibility = View.VISIBLE callButton.visibility = View.VISIBLE
@@ -1798,6 +1823,30 @@ class MainActivity : AppCompatActivity() {
} }
} }
callHandler.postDelayed(callRunnable!!, 1000) callHandler.postDelayed(callRunnable!!, 1000)
} else {
audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
if (mode == AudioManager.MODE_IN_COMMUNICATION) {
Log.d(TAG, "Audio mode changed to MODE_IN_COMMUNICATION using " +
"device ${am.communicationDevice!!.type}")
if (audioModeChangedListener != null) {
am.removeOnModeChangedListener(audioModeChangedListener!!)
audioModeChangedListener = null
}
if (!call(ua, uri)) {
callButton.visibility = View.VISIBLE
callButton.isEnabled = true
hangupButton.visibility = View.INVISIBLE
hangupButton.isEnabled = false
}
} else {
Log.d(TAG, "Audio mode changed to MODE_NORMAL using " +
"device ${am.communicationDevice!!.type}")
}
}
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
am.mode = AudioManager.MODE_IN_COMMUNICATION
}
} }
} else { } else {
val latestPeerUri = NewCallHistory.aorLatestPeerUri(aor) val latestPeerUri = NewCallHistory.aorLatestPeerUri(aor)
+27 -19
View File
@@ -41,6 +41,7 @@ import java.net.SocketException
import java.security.SecureRandom import java.security.SecureRandom
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
import java.util.concurrent.Executor
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
@@ -897,9 +898,10 @@ object Utils {
am.isSpeakerphoneOn am.isSpeakerphoneOn
} }
fun setSpeakerPhone(am: AudioManager, enabled: Boolean) { private fun setSpeakerPhone(executor: Executor, am: AudioManager, enable: Boolean) {
// Currently at API levels 31+, speakerphone cannot be turned on during call if (enable == isSpeakerPhoneOn(am))
if (enabled) { return
if (enable) {
if (Build.VERSION.SDK_INT >= 31) { if (Build.VERSION.SDK_INT >= 31) {
Log.d(TAG, "Setting current device from ${am.communicationDevice!!.type} to " + Log.d(TAG, "Setting current device from ${am.communicationDevice!!.type} to " +
"${AudioDeviceInfo.TYPE_BUILTIN_SPEAKER} in mode ${am.mode}") "${AudioDeviceInfo.TYPE_BUILTIN_SPEAKER} in mode ${am.mode}")
@@ -910,12 +912,25 @@ object Utils {
break break
} }
if (speakerDevice != null) { if (speakerDevice != null) {
am.mode = AudioManager.MODE_NORMAL if (am.mode == AudioManager.MODE_NORMAL) {
Handler(Looper.getMainLooper()).postDelayed({
if (!am.setCommunicationDevice(speakerDevice)) if (!am.setCommunicationDevice(speakerDevice))
Log.e(TAG, "Could not turn on speaker device") Log.e(TAG, "Could not turn on speaker device")
Log.d(TAG, "Type of current device is ${am.communicationDevice!!.type}") Log.d(TAG, "Type of current device is ${am.communicationDevice!!.type}")
}, 500) } else {
// Currently at API levels 31+, speakerphone needs to be turned on in normal mode
val normalListener = object : AudioManager.OnModeChangedListener {
override fun onModeChanged(mode: Int) {
if (mode == AudioManager.MODE_NORMAL) {
am.removeOnModeChangedListener(this)
if (!am.setCommunicationDevice(speakerDevice))
Log.e(TAG, "Could not turn on speaker device")
Log.d(TAG, "Type of current device is ${am.communicationDevice!!.type}")
}
}
}
am.addOnModeChangedListener(executor, normalListener)
am.mode = AudioManager.MODE_NORMAL
}
} }
} else { } else {
am.isSpeakerphoneOn = true am.isSpeakerphoneOn = true
@@ -925,17 +940,10 @@ object Utils {
if (Build.VERSION.SDK_INT >= 31) { if (Build.VERSION.SDK_INT >= 31) {
Log.d(TAG, "Setting current device from type ${am.communicationDevice!!.type} to " + Log.d(TAG, "Setting current device from type ${am.communicationDevice!!.type} to " +
"${AudioDeviceInfo.TYPE_BUILTIN_EARPIECE} in mode ${am.mode}") "${AudioDeviceInfo.TYPE_BUILTIN_EARPIECE} in mode ${am.mode}")
var earDevice: AudioDeviceInfo? = null am.clearCommunicationDevice()
for (device in am.availableCommunicationDevices) // Restore communication mode
if (device.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { if (Call.connectedCall() != null && am.mode == AudioManager.MODE_NORMAL)
earDevice = device
break
}
if (earDevice != null) {
if (!am.setCommunicationDevice(earDevice))
Log.e(TAG, "Could not turn on earpiece device")
am.mode = AudioManager.MODE_IN_COMMUNICATION am.mode = AudioManager.MODE_IN_COMMUNICATION
}
Log.d(TAG, "Type of current device is ${am.communicationDevice!!.type}") Log.d(TAG, "Type of current device is ${am.communicationDevice!!.type}")
} else { } else {
am.isSpeakerphoneOn = false am.isSpeakerphoneOn = false
@@ -944,11 +952,11 @@ object Utils {
} }
} }
fun toggleSpeakerPhone(am: AudioManager) { fun toggleSpeakerPhone(executor: Executor, am: AudioManager) {
if (Build.VERSION.SDK_INT >= 31) if (Build.VERSION.SDK_INT >= 31)
setSpeakerPhone(am, setSpeakerPhone(executor, am,
am.communicationDevice!!.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) am.communicationDevice!!.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE)
else else
setSpeakerPhone(am, !am.isSpeakerphoneOn) setSpeakerPhone(executor, am, !am.isSpeakerphoneOn)
} }
} }