diff --git a/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml b/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml index be90d31d..45e6dab7 100644 --- a/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml +++ b/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanistPermissions = "0.34.0" agp = "8.7.3" constraintlayoutCompose = "1.1.0" -pipecatClient = "0.3.7" +pipecatClient = "1.1.0" kotlin = "2.0.20" coreKtx = "1.15.0" lifecycleRuntimeKtx = "2.8.7" diff --git a/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts b/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts index c1810cd1..2fb4fe28 100644 --- a/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts +++ b/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts @@ -32,12 +32,12 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "11" } buildFeatures { diff --git a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt index 4e833970..0c2fdf42 100644 --- a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt +++ b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt @@ -32,7 +32,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -123,7 +122,6 @@ class MainActivity : ComponentActivity() { } } -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ConnectSettings( voiceClientManager: VoiceClientManager, @@ -131,9 +129,13 @@ fun ConnectSettings( val scrollState = rememberScrollState() val start = { - val backendUrl = Preferences.backendUrl.value + val backendUrl = Preferences.backendUrl.value ?: "" + val apiKey = Preferences.apiKey.value ?: "" - voiceClientManager.start(baseUrl = backendUrl!!) + voiceClientManager.start( + baseUrl = backendUrl, + apiKey = apiKey + ) } Box( @@ -196,6 +198,31 @@ fun ConnectSettings( Spacer(modifier = Modifier.height(36.dp)) + Text( + text = "API key", + fontSize = 16.sp, + fontWeight = FontWeight.W400, + style = TextStyles.base + ) + + Spacer(modifier = Modifier.height(12.dp)) + + TextField( + modifier = Modifier + .fillMaxWidth() + .border(1.dp, Colors.textFieldBorder, RoundedCornerShape(12.dp)), + value = Preferences.apiKey.value ?: "", + onValueChange = { Preferences.apiKey.value = it }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Unspecified, + imeAction = ImeAction.Next + ), + colors = textFieldColors(), + shape = RoundedCornerShape(12.dp) + ) + + Spacer(modifier = Modifier.height(36.dp)) + Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp) diff --git a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt index 667dc261..421dff9a 100644 --- a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt +++ b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt @@ -11,13 +11,14 @@ private val JSON_INSTANCE = Json { ignoreUnknownKeys = true } object Preferences { private const val PREF_BACKEND_URL = "backend_url" + private const val PREF_API_KEY = "api_key" private lateinit var prefs: SharedPreferences fun initAppStart(context: Context) { prefs = context.applicationContext.getSharedPreferences("prefs", Context.MODE_PRIVATE) - listOf(backendUrl).forEach { it.init() } + listOf(backendUrl, apiKey).forEach { it.init() } } private fun getString(key: String): String? = prefs.getString(key, null) @@ -72,4 +73,5 @@ object Preferences { } val backendUrl = StringPref(PREF_BACKEND_URL) + val apiKey = StringPref(PREF_API_KEY) } \ No newline at end of file diff --git a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt index 2225d8ac..ed22854b 100644 --- a/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt +++ b/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt @@ -1,20 +1,21 @@ package ai.pipecat.small_webrtc_client -import ai.pipecat.client.RTVIClient -import ai.pipecat.client.RTVIClientOptions -import ai.pipecat.client.RTVIClientParams -import ai.pipecat.client.RTVIEventCallbacks +import ai.pipecat.client.PipecatClient +import ai.pipecat.client.PipecatClientOptions +import ai.pipecat.client.PipecatEventCallbacks import ai.pipecat.client.result.Future import ai.pipecat.client.result.RTVIError -import ai.pipecat.client.result.Result +import ai.pipecat.client.small_webrtc_transport.PipecatClientSmallWebRTC import ai.pipecat.client.small_webrtc_transport.SmallWebRTCTransport -import ai.pipecat.client.types.ActionDescription +import ai.pipecat.client.transport.MsgServerToClient +import ai.pipecat.client.types.APIRequest +import ai.pipecat.client.types.BotReadyData import ai.pipecat.client.types.Participant import ai.pipecat.client.types.PipecatMetrics -import ai.pipecat.client.types.ServiceConfig import ai.pipecat.client.types.Tracks import ai.pipecat.client.types.Transcript import ai.pipecat.client.types.TransportState +import ai.pipecat.client.types.Value import ai.pipecat.small_webrtc_client.utils.Timestamp import android.content.Context import android.util.Log @@ -34,14 +35,12 @@ class VoiceClientManager(private val context: Context) { private const val TAG = "VoiceClientManager" } - private val client = mutableStateOf(null) + private val client = mutableStateOf(null) val state = mutableStateOf(null) val errors = mutableStateListOf() - val actionDescriptions = - mutableStateOf, RTVIError>?>(null) val expiryTime = mutableStateOf(null) @@ -60,21 +59,15 @@ class VoiceClientManager(private val context: Context) { errors.add(Error(it.description)) } - fun start(baseUrl: String) { + fun start(baseUrl: String, apiKey: String) { if (client.value != null) { return } - val options = RTVIClientOptions( - params = RTVIClientParams(baseUrl = null), - enableMic = true, - enableCam = true - ) - state.value = TransportState.Disconnected - val callbacks = object : RTVIEventCallbacks() { + val callbacks = object : PipecatEventCallbacks() { override fun onTransportStateChanged(state: TransportState) { this@VoiceClientManager.state.value = state } @@ -86,21 +79,19 @@ class VoiceClientManager(private val context: Context) { } } - override fun onBotReady(version: String, config: List) { - - Log.i(TAG, "Bot ready. Version $version, config: $config") - + override fun onBotReady(data: BotReadyData) { + Log.i(TAG, "Bot ready: $data") botReady.value = true - - client.value?.describeActions()?.withCallback { - actionDescriptions.value = it - } } - override fun onPipecatMetrics(data: PipecatMetrics) { + override fun onMetrics(data: PipecatMetrics) { Log.i(TAG, "Pipecat metrics: $data") } + override fun onBotOutput(data: MsgServerToClient.Data.BotOutputData) { + Log.i(TAG, "Bot output: $data") + } + override fun onUserTranscript(data: Transcript) { Log.i(TAG, "User transcript: $data") } @@ -138,17 +129,11 @@ class VoiceClientManager(private val context: Context) { this@VoiceClientManager.mic.value = mic } - override fun onConnected() { - expiryTime.value = client.value?.expiry?.let(Timestamp::ofEpochSecs) - } - override fun onDisconnected() { expiryTime.value = null - actionDescriptions.value = null botIsTalking.value = false userIsTalking.value = false state.value = null - actionDescriptions.value = null botReady.value = false tracks.value = null @@ -165,9 +150,24 @@ class VoiceClientManager(private val context: Context) { } } - val client = RTVIClient(SmallWebRTCTransport.Factory(context, baseUrl), callbacks, options) + val options = PipecatClientOptions( + enableMic = true, + enableCam = true, + callbacks = callbacks + ) + + val client = PipecatClient( + transport = SmallWebRTCTransport(context), + options = options + ) - client.connect().displayErrors().withErrorCallback { + client.startBotAndConnect(APIRequest( + endpoint = baseUrl, + requestData = Value.Object(), + headers = listOfNotNull( + apiKey.trim().takeIf { it.isNotEmpty() }?.let {"Authorization" to "Bearer $it"} + ).toMap() + )).displayErrors().withErrorCallback { callbacks.onDisconnected() }