@@ -17,6 +17,7 @@ import android.os.Build
17
17
import android.util.Log
18
18
import android.view.ViewGroup
19
19
import android.webkit.ConsoleMessage
20
+ import android.webkit.JavascriptInterface
20
21
import android.webkit.PermissionRequest
21
22
import android.webkit.WebChromeClient
22
23
import android.webkit.WebView
@@ -36,6 +37,7 @@ import androidx.compose.runtime.getValue
36
37
import androidx.compose.runtime.mutableStateListOf
37
38
import androidx.compose.runtime.mutableStateOf
38
39
import androidx.compose.runtime.remember
40
+ import androidx.compose.runtime.rememberCoroutineScope
39
41
import androidx.compose.runtime.setValue
40
42
import androidx.compose.ui.Alignment
41
43
import androidx.compose.ui.Modifier
@@ -70,8 +72,11 @@ import io.element.android.libraries.designsystem.theme.components.Text
70
72
import io.element.android.libraries.designsystem.theme.components.TextButton
71
73
import io.element.android.libraries.designsystem.theme.components.TopAppBar
72
74
import io.element.android.libraries.ui.strings.CommonStrings
75
+ import kotlinx.coroutines.delay
76
+ import kotlinx.coroutines.launch
73
77
import timber.log.Timber
74
78
import java.util.concurrent.Executors
79
+ import kotlin.time.Duration.Companion.seconds
75
80
76
81
typealias RequestPermissionCallback = (Array <String >) -> Unit
77
82
@@ -179,6 +184,7 @@ private fun CallWebView(
179
184
Column (modifier = modifier) {
180
185
var audioDeviceCallback: AudioDeviceCallback ? by remember { mutableStateOf(null ) }
181
186
187
+ val coroutinScope = rememberCoroutineScope()
182
188
AndroidView (
183
189
modifier = Modifier .fillMaxWidth(),
184
190
factory = { context ->
@@ -192,11 +198,47 @@ private fun CallWebView(
192
198
if (url is AsyncData .Success && webView.url != url.data) {
193
199
webView.loadUrl(url.data)
194
200
195
- val audioManager = webView.context.getSystemService<AudioManager >()!!
196
- val devices = audioManager.loadCommunicationAudioDevices()
197
- webView.evaluateJavascript("""
198
- controls.setOutputDevices([${devices.joinToString(" ," ) { " { 'id': '${it.id} ', 'name': '${it.description()} ' }" } } ])
199
- """ .trimIndent(), null )
201
+ val setAudioOutputCallback = object {
202
+ @JavascriptInterface
203
+ fun setOutputDevice (id : String ) {
204
+ val audioManager = webView.context.getSystemService<AudioManager >()!!
205
+ val audioDevices = audioManager.loadCommunicationAudioDevices()
206
+ val selectedDevice = audioDevices.find { it.id.toString() == id }
207
+ if (selectedDevice != null ) {
208
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
209
+ audioManager.setCommunicationDevice(selectedDevice)
210
+ } else {
211
+ when (selectedDevice.type) {
212
+ AudioDeviceInfo .TYPE_BUILTIN_SPEAKER -> {
213
+ @Suppress(" DEPRECATION" )
214
+ audioManager.isSpeakerphoneOn = true
215
+ }
216
+ AudioDeviceInfo .TYPE_BUILTIN_EARPIECE -> {
217
+ @Suppress(" DEPRECATION" )
218
+ audioManager.isSpeakerphoneOn = false
219
+ }
220
+ AudioDeviceInfo .TYPE_BLUETOOTH_SCO -> {
221
+ audioManager.isBluetoothScoOn = true
222
+ }
223
+ else -> Timber .d(" Audio device selected but it's not compatible, type: ${selectedDevice.type} " )
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+
230
+ webView.addJavascriptInterface(setAudioOutputCallback, " setOutputDeviceCallback" )
231
+
232
+ coroutinScope.launch {
233
+ delay(4 .seconds)
234
+ val audioManager = webView.context.getSystemService<AudioManager >()!!
235
+ val devices = audioManager.loadCommunicationAudioDevices()
236
+ val setDevicesScript = """
237
+ controls.setOutputDevices([${devices.joinToString(" ," ) { " { 'id': '${it.id} ', 'name': '${it.description()} ' }" } } ]);
238
+ controls.onOutputDeviceSelect = (id) => { setOutputDeviceCallback.setOutputDevice(id); };
239
+ """ .trimIndent()
240
+ webView.evaluateJavascript(setDevicesScript, null )
241
+ }
200
242
}
201
243
},
202
244
onRelease = { webView ->
0 commit comments