davidliu
Committed by GitHub

Route AudioSwitch calls through main thread (#120)

@@ -2,6 +2,8 @@ package io.livekit.android.audio @@ -2,6 +2,8 @@ package io.livekit.android.audio
2 2
3 import android.content.Context 3 import android.content.Context
4 import android.media.AudioManager 4 import android.media.AudioManager
  5 +import android.os.Handler
  6 +import android.os.Looper
5 import com.twilio.audioswitch.AudioDevice 7 import com.twilio.audioswitch.AudioDevice
6 import com.twilio.audioswitch.AudioDeviceChangeListener 8 import com.twilio.audioswitch.AudioDeviceChangeListener
7 import com.twilio.audioswitch.AudioSwitch 9 import com.twilio.audioswitch.AudioSwitch
@@ -19,23 +21,32 @@ constructor(private val context: Context) : AudioHandler { @@ -19,23 +21,32 @@ constructor(private val context: Context) : AudioHandler {
19 21
20 private var audioSwitch: AudioSwitch? = null 22 private var audioSwitch: AudioSwitch? = null
21 23
  24 + // AudioSwitch is not threadsafe, so all calls should be done on the main thread.
  25 + private val handler = Handler(Looper.getMainLooper())
  26 +
22 override fun start() { 27 override fun start() {
23 if (audioSwitch == null) { 28 if (audioSwitch == null) {
24 - val switch = AudioSwitch(  
25 - context = context,  
26 - loggingEnabled = loggingEnabled,  
27 - audioFocusChangeListener = onAudioFocusChangeListener ?: defaultOnAudioFocusChangeListener,  
28 - preferredDeviceList = preferredDeviceList ?: defaultPreferredDeviceList  
29 - )  
30 - audioSwitch = switch  
31 - switch.start(audioDeviceChangeListener ?: defaultAudioDeviceChangeListener)  
32 - switch.activate() 29 + handler.removeCallbacksAndMessages(null)
  30 + handler.post {
  31 + val switch = AudioSwitch(
  32 + context = context,
  33 + loggingEnabled = loggingEnabled,
  34 + audioFocusChangeListener = onAudioFocusChangeListener ?: defaultOnAudioFocusChangeListener,
  35 + preferredDeviceList = preferredDeviceList ?: defaultPreferredDeviceList
  36 + )
  37 + audioSwitch = switch
  38 + switch.start(audioDeviceChangeListener ?: defaultAudioDeviceChangeListener)
  39 + switch.activate()
  40 + }
33 } 41 }
34 } 42 }
35 43
36 override fun stop() { 44 override fun stop() {
37 - audioSwitch?.stop()  
38 - audioSwitch = null 45 + handler.removeCallbacksAndMessages(null)
  46 + handler.post {
  47 + audioSwitch?.stop()
  48 + audioSwitch = null
  49 + }
39 } 50 }
40 51
41 val selectedAudioDevice: AudioDevice? 52 val selectedAudioDevice: AudioDevice?
@@ -19,21 +19,23 @@ import io.livekit.android.room.participant.Participant @@ -19,21 +19,23 @@ import io.livekit.android.room.participant.Participant
19 import io.livekit.android.room.participant.RemoteParticipant 19 import io.livekit.android.room.participant.RemoteParticipant
20 import io.livekit.android.room.track.* 20 import io.livekit.android.room.track.*
21 import io.livekit.android.util.flow 21 import io.livekit.android.util.flow
22 -import kotlinx.coroutines.ExperimentalCoroutinesApi  
23 import kotlinx.coroutines.flow.* 22 import kotlinx.coroutines.flow.*
24 import kotlinx.coroutines.launch 23 import kotlinx.coroutines.launch
25 import livekit.LivekitRtc 24 import livekit.LivekitRtc
26 25
27 -@OptIn(ExperimentalCoroutinesApi::class)  
28 class CallViewModel( 26 class CallViewModel(
29 val url: String, 27 val url: String,
30 val token: String, 28 val token: String,
31 application: Application 29 application: Application
32 ) : AndroidViewModel(application) { 30 ) : AndroidViewModel(application) {
33 31
  32 + val audioHandler = AudioSwitchHandler(application)
34 val room = LiveKit.create( 33 val room = LiveKit.create(
35 appContext = application, 34 appContext = application,
36 options = RoomOptions(adaptiveStream = true, dynacast = true), 35 options = RoomOptions(adaptiveStream = true, dynacast = true),
  36 + overrides = LiveKitOverrides(
  37 + audioHandler = audioHandler
  38 + )
37 ) 39 )
38 40
39 val participants = room::remoteParticipants.flow 41 val participants = room::remoteParticipants.flow
@@ -73,7 +75,6 @@ class CallViewModel( @@ -73,7 +75,6 @@ class CallViewModel(
73 private val mutablePermissionAllowed = MutableStateFlow(true) 75 private val mutablePermissionAllowed = MutableStateFlow(true)
74 val permissionAllowed = mutablePermissionAllowed.hide() 76 val permissionAllowed = mutablePermissionAllowed.hide()
75 77
76 - val audioHandler = AudioSwitchHandler(application)  
77 init { 78 init {
78 viewModelScope.launch { 79 viewModelScope.launch {
79 launch { 80 launch {