mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 20:44:38 +00:00
android: prevent WebView from changing audio device when toggling mic
This commit is contained in:
+31
@@ -7,9 +7,12 @@ import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.withBGApi
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
interface CallAudioDeviceManagerInterface {
|
||||
@@ -19,6 +22,7 @@ interface CallAudioDeviceManagerInterface {
|
||||
fun stop()
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyExternal: Boolean)
|
||||
fun selectSameDeviceOnWebViewChange()
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectDevice(id: Int)
|
||||
|
||||
@@ -35,12 +39,15 @@ interface CallAudioDeviceManagerInterface {
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
private var resetDeviceToDevice: AudioDeviceInfo? = null
|
||||
private var resetDeviceToDeviceJob: Job = Job()
|
||||
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
|
||||
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
|
||||
|
||||
private val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
resetDeviceToDevice = null
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val oldDevices = devices.value
|
||||
devices.value = am.availableCommunicationDevices
|
||||
@@ -53,13 +60,21 @@ class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
resetDeviceToDevice = null
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
devices.value = am.availableCommunicationDevices
|
||||
}
|
||||
}
|
||||
|
||||
private val listener: OnCommunicationDeviceChangedListener = OnCommunicationDeviceChangedListener { device ->
|
||||
val resetTo = resetDeviceToDevice
|
||||
if (resetTo != null && device != null && resetTo.id != device.id) {
|
||||
Log.w(TAG, "Resetting device that was set by WebView ${device.name?.localized()} to previously set device ${resetTo.name?.localized()}")
|
||||
selectDevice(resetTo.id)
|
||||
return@OnCommunicationDeviceChangedListener
|
||||
}
|
||||
devices.value = am.availableCommunicationDevices
|
||||
//Log.d(TAG, "Devices changed ${device?.details()} | ${devices.value.map { it.details() }}")
|
||||
currentDevice.value = device
|
||||
}
|
||||
|
||||
@@ -98,7 +113,18 @@ class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
// WebView modifies speaker when muting/unmuting microphone. It's not needed at all, returning back current device if it will be changed
|
||||
override fun selectSameDeviceOnWebViewChange() {
|
||||
resetDeviceToDevice = currentDevice.value
|
||||
resetDeviceToDeviceJob.cancel()
|
||||
resetDeviceToDeviceJob = withBGApi {
|
||||
delay(5000)
|
||||
resetDeviceToDevice = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
resetDeviceToDevice = null
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
if (device != null && am.communicationDevice?.id != id ) {
|
||||
am.setCommunicationDevice(device)
|
||||
@@ -153,6 +179,9 @@ class PreSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
// Works without it
|
||||
override fun selectSameDeviceOnWebViewChange() {}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
val isExternalDevice = device != null && device.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE && device.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
@@ -216,6 +245,8 @@ val AudioDeviceInfo.icon: ImageResource
|
||||
else -> MR.images.ic_brand_awareness_filled
|
||||
}
|
||||
|
||||
private fun AudioDeviceInfo.details(): String = "$productName id:$id name:${name?.localized()} type:$type sink:$isSink source:$isSource"
|
||||
|
||||
val AudioDeviceInfo.name: StringResource?
|
||||
get() = when (this.type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.strings.audio_device_earpiece
|
||||
|
||||
+5
-1
@@ -202,6 +202,7 @@ actual fun ActiveCallView() {
|
||||
is WCallCommand.Camera -> {
|
||||
updateActiveCall(call) { it.copy(localCamera = cmd.camera) }
|
||||
if (!call.localMediaSources.mic) {
|
||||
callAudioDeviceManager.selectSameDeviceOnWebViewChange()
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = false))
|
||||
}
|
||||
}
|
||||
@@ -261,7 +262,10 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceM
|
||||
devices = remember { callAudioDeviceManager.devices }.value,
|
||||
currentDevice = remember { callAudioDeviceManager.currentDevice },
|
||||
dismiss = { withBGApi { chatModel.callManager.endCall(call) } },
|
||||
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = !call.localMediaSources.mic)) },
|
||||
toggleAudio = {
|
||||
callAudioDeviceManager.selectSameDeviceOnWebViewChange()
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = !call.localMediaSources.mic))
|
||||
},
|
||||
selectDevice = { callAudioDeviceManager.selectDevice(it.id) },
|
||||
toggleVideo = {
|
||||
if (ContextCompat.checkSelfPermission(androidAppContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
Reference in New Issue
Block a user