@@ -29,7 +29,6 @@ import kotlin.coroutines.CoroutineContext
2929import  kotlinx.coroutines.CoroutineScope 
3030import  kotlinx.coroutines.cancel 
3131import  kotlinx.coroutines.channels.Channel 
32- import  kotlinx.coroutines.delay 
3332import  kotlinx.coroutines.flow.Flow 
3433import  kotlinx.coroutines.flow.flow 
3534import  kotlinx.coroutines.flow.receiveAsFlow 
@@ -53,7 +52,6 @@ internal constructor(
5352  private  val  playBackQueue =  ConcurrentLinkedQueue <ByteArray >()
5453  private  var  startedReceiving =  false 
5554  private  var  receiveChannel:  Channel <Frame > =  Channel ()
56-   private  var  functionCallChannel:  Channel <List <FunctionCallPart >> =  Channel ()
5755
5856  private  companion  object  {
5957    val  TAG  =  LiveSession ::class .java.simpleName
@@ -127,16 +125,6 @@ internal constructor(
127125    }
128126  }
129127
130-   /* *
131-    * Receives all function call responses from the server for the audio conversation feature. This 
132-    * can be called only after calling [startAudioConversation] function. 
133-    * 
134-    * @return A [Flow] which will emit list of [FunctionCallPart] as they are returned by the model. 
135-    */  
136-   public  fun  receiveAudioConversationFunctionCalls (): Flow <List <FunctionCallPart >> {
137-     return  functionCallChannel.receiveAsFlow()
138-   }
139- 
140128  private  fun  fillRecordedAudioQueue () {
141129    CoroutineScope (backgroundDispatcher).launch {
142130      audioHelper!! .startRecording().collect {
@@ -163,7 +151,9 @@ internal constructor(
163151    }
164152  }
165153
166-   private  fun  fillServerResponseAudioQueue () {
154+   private  fun  fillServerResponseAudioQueue (
155+     functionCallsHandler :  ((List <FunctionCallPart >) ->  List <FunctionResponsePart >)?  = null
156+   ) {
167157    CoroutineScope (backgroundDispatcher).launch {
168158      receive(listOf (ContentModality .AUDIO )).collect {
169159        if  (! isRecording) {
@@ -173,8 +163,8 @@ internal constructor(
173163          LiveContentResponse .Status .INTERRUPTED  -> 
174164            while  (! playBackQueue.isEmpty()) playBackQueue.poll()
175165          LiveContentResponse .Status .NORMAL  -> 
176-             if  (! it.functionCalls.isNullOrEmpty()) {
177-               functionCallChannel.send( it.functionCalls)
166+             if  (! it.functionCalls.isNullOrEmpty()  &&  functionCallsHandler  !=   null ) {
167+               sendFunctionResponse(functionCallsHandler( it.functionCalls) )
178168            } else  {
179169              val  audioData =  it.data?.parts?.get(0 )?.asInlineDataPartOrNull()?.inlineData
180170              if  (audioData !=  null ) {
@@ -198,22 +188,24 @@ internal constructor(
198188  /* *
199189   * Starts an audio conversation with the Gemini server, which can only be stopped using 
200190   * [stopAudioConversation]. 
191+    * 
192+    * @param functionCallsHandler A callback function that is invoked whenever the server receives a 
193+    * function call. 
201194   */  
202-   public  suspend  fun  startAudioConversation () {
195+   public  suspend  fun  startAudioConversation (
196+     functionCallsHandler :  ((List <FunctionCallPart >) ->  List <FunctionResponsePart >)?  = null
197+   ) {
203198    if  (isRecording) {
204199      Log .w(TAG , " startAudioConversation called after the recording has already started."  )
205200      return 
206201    }
207-     functionCallChannel =  Channel ()
208202    isRecording =  true 
209203    audioHelper =  AudioHelper ()
210204    audioHelper!! .setupAudioTrack()
211205    fillRecordedAudioQueue()
212206    CoroutineScope (backgroundDispatcher).launch { sendAudioDataToServer() }
213-     fillServerResponseAudioQueue()
207+     fillServerResponseAudioQueue(functionCallsHandler )
214208    playServerResponseAudio()
215-     //  This delay is necessary to ensure that all threads have started.
216-     delay(1000 )
217209  }
218210
219211  /* *
0 commit comments