Skip to content

Commit 8e4b066

Browse files
authored
Merge 5edbfff into 87148c9
2 parents 87148c9 + 5edbfff commit 8e4b066

File tree

6 files changed

+43
-5
lines changed

6 files changed

+43
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Firebase Android Open Source Development
1+
# Firebase Android Open Source Development T
22

33
This repository contains the source code for all Android Firebase SDKs except
44
Analytics and Auth.

firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,17 @@ internal constructor(
119119
val receivedJson = JSON.parseToJsonElement(receivedJsonStr)
120120

121121
return if (receivedJson is JsonObject && "setupComplete" in receivedJson) {
122-
LiveSession(session = webSession, blockingDispatcher = blockingDispatcher)
122+
LiveSession(
123+
session = webSession,
124+
blockingDispatcher = blockingDispatcher,
125+
firebaseApp = controller.firebaseApp
126+
)
123127
} else {
124128
webSession.close()
125129
throw ServiceConnectionHandshakeFailedException("Unable to connect to the server")
126130
}
127131
} catch (e: ClosedReceiveChannelException) {
128-
throw ServiceConnectionHandshakeFailedException("Channel was closed by the server", e)
132+
throw ServiceConnectionHandshakeFailedException("Error: Too many concurrent live requests", e)
129133
}
130134
}
131135

firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ internal constructor(
9797
private val requestOptions: RequestOptions,
9898
httpEngine: HttpClientEngine,
9999
private val apiClient: String,
100-
private val firebaseApp: FirebaseApp,
100+
internal val firebaseApp: FirebaseApp,
101101
private val appVersion: Int = 0,
102102
private val googleAppId: String,
103103
private val headerProvider: HeaderProvider?,

firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.google.firebase.ai.type.MediaData
2929
import com.google.firebase.ai.type.PublicPreviewAPI
3030
import com.google.firebase.ai.type.SessionAlreadyReceivingException
3131
import io.ktor.websocket.close
32+
import kotlinx.coroutines.isActive
3233
import kotlinx.coroutines.reactive.asPublisher
3334
import org.reactivestreams.Publisher
3435

@@ -51,6 +52,12 @@ public abstract class LiveSessionFutures internal constructor() {
5152
functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)?
5253
): ListenableFuture<Unit>
5354

55+
/** Indicates whether the underlying websocket connection is active. */
56+
public abstract fun isActive(): ListenableFuture<Boolean>
57+
58+
/** Indicates whether an audio conversation is being used for this session object. */
59+
public abstract fun isAudioConversationActive(): ListenableFuture<Boolean>
60+
5461
/**
5562
* Starts an audio conversation with the model, which can only be stopped using
5663
* [stopAudioConversation].
@@ -169,6 +176,11 @@ public abstract class LiveSessionFutures internal constructor() {
169176
override fun startAudioConversation() =
170177
SuspendToFutureAdapter.launchFuture { session.startAudioConversation() }
171178

179+
override fun isActive() = SuspendToFutureAdapter.launchFuture { session.isActive() }
180+
181+
override fun isAudioConversationActive() =
182+
SuspendToFutureAdapter.launchFuture { session.isAudioConversationActive() }
183+
172184
override fun stopAudioConversation() =
173185
SuspendToFutureAdapter.launchFuture { session.stopAudioConversation() }
174186

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ public class AudioRecordInitializationFailedException(message: String) :
231231
public class ServiceConnectionHandshakeFailedException(message: String, cause: Throwable? = null) :
232232
FirebaseAIException(message, cause)
233233

234+
/** The request is missing a permission that is required to perform the requested operation. */
235+
public class PermissionMissingException(message: String, cause: Throwable? = null) :
236+
FirebaseAIException(message, cause)
237+
234238
/** Catch all case for exceptions not explicitly expected. */
235239
public class UnknownException internal constructor(message: String, cause: Throwable? = null) :
236240
FirebaseAIException(message, cause)

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
package com.google.firebase.ai.type
1818

1919
import android.Manifest.permission.RECORD_AUDIO
20+
import android.content.pm.PackageManager
2021
import android.media.AudioFormat
2122
import android.media.AudioTrack
2223
import android.util.Log
2324
import androidx.annotation.RequiresPermission
25+
import androidx.core.content.ContextCompat
26+
import com.google.firebase.FirebaseApp
2427
import com.google.firebase.ai.common.JSON
2528
import com.google.firebase.ai.common.util.CancelledCoroutineScope
2629
import com.google.firebase.ai.common.util.accumulateUntil
@@ -58,7 +61,8 @@ public class LiveSession
5861
internal constructor(
5962
private val session: ClientWebSocketSession,
6063
@Blocking private val blockingDispatcher: CoroutineContext,
61-
private var audioHelper: AudioHelper? = null
64+
private var audioHelper: AudioHelper? = null,
65+
private val firebaseApp: FirebaseApp,
6266
) {
6367
/**
6468
* Coroutine scope that we batch data on for [startAudioConversation].
@@ -93,6 +97,14 @@ internal constructor(
9397
public suspend fun startAudioConversation(
9498
functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null
9599
) {
100+
101+
val context = firebaseApp.applicationContext
102+
if (
103+
ContextCompat.checkSelfPermission(context, RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED
104+
) {
105+
throw PermissionMissingException("Audio access not provided by the user")
106+
}
107+
96108
FirebaseAIException.catchAsync {
97109
if (scope.isActive) {
98110
Log.w(
@@ -131,6 +143,12 @@ internal constructor(
131143
}
132144
}
133145

146+
/** Indicates whether the underlying websocket connection is active. */
147+
public fun isActive(): Boolean = session.isActive
148+
149+
/** Indicates whether an audio conversation is being used for this session object. */
150+
public fun isAudioConversationActive(): Boolean = (audioHelper != null)
151+
134152
/**
135153
* Receives responses from the model for both streaming and standard requests.
136154
*

0 commit comments

Comments
 (0)