Skip to content

Commit 16298ca

Browse files
MaxHeimbrockclaude
andauthored
Default connect to homepage agent (#68)
* Connect to homepage agent by default * Remove cached from token sources because it might trip up agent dispatch going to the same room twice * Update the README * Hyperlinks in README * no-code * Adding >Waiting for agent< text info * Mention homepage agent as default * Review cleanups: dead import, formatting, README links, misconfig warning - Remove unused .cached() import left over from intentional removal - Warn when only one of hardcodedUrl/hardcodedToken is set before falling back to the homepage agent - Fix import order, brace spacing, missing EOF newline - README: relative anchor link, kotlin code fence, trailing whitespace Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Bump libs to sdk version 2.27.1 and components version 2.4.1 * Revert to latest public release, will bump in a new PR once the bug fix is released --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent fbfdb9a commit 16298ca

7 files changed

Lines changed: 76 additions & 32 deletions

File tree

README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,30 @@ This template is free for you to use or modify as you see fit.
88

99
## Getting started
1010

11-
The easiest way to get this app running is with a [token server](https://docs.livekit.io/frontends/authentication/tokens/sandbox-token-server/) and the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/).
12-
13-
First, enable the token server from your project's **Options** on the [Settings](https://cloud.livekit.io/projects/p_/settings/project) page in LiveKit Cloud and copy the `sandboxID`.
14-
15-
Then, run the following command to automatically clone this template and connect it to LiveKit Cloud:
11+
The easiest way to get this app running is with the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/). Run the following command to automatically clone this template and connect it to LiveKit Cloud:
1612

1713
```bash
18-
lk app create --template agent-starter-android --sandbox <token_server_sandbox_id>
14+
lk app create --template agent-starter-android
1915
```
2016

2117
Build and run the app in Android Studio.
2218

23-
You'll also need an agent to speak with. Try our starter agent for [Python](https://github.com/livekit-examples/agent-starter-python), [Node.js](https://github.com/livekit-examples/agent-starter-node), or [create your own from scratch](https://docs.livekit.io/agents/start/voice-ai/).
19+
The app is configured to connect to the LiveKit homepage agent by default, which you can also try at [livekit.com](https://www.livekit.com). To point the app at your own agent, see [Connect to your agent](#connect-to-your-agent).
2420

2521
> [!NOTE]
26-
> To setup without the LiveKit CLI, clone the repository and edit the `TokenExt.kt` file to add either a `sandboxID` (from your project's **Options** on the [Settings](https://cloud.livekit.io/projects/p_/settings/project) page), or a [manually generated](#token-generation) URL and token.
22+
> To set up without the LiveKit CLI, clone the repository via git.
23+
24+
## Connect to your agent
25+
26+
To switch from the default agent to your own, you first need a LiveKit agent to speak with. For a no-code setup, use the [Agent Builder](https://docs.livekit.io/agents/start/builder/). For more customization, try our starter agent for [Python](https://github.com/livekit-examples/agent-starter-python), [Node.js](https://github.com/livekit-examples/agent-starter-node), or [create your own from scratch](https://docs.livekit.io/agents/start/voice-ai/).
27+
28+
Second, you need a token server. For development, the easiest option is the sandbox token server: enable it from your project's Options on the Settings page in LiveKit Cloud and copy the sandboxId.
29+
30+
Then edit `TokenExt.kt`:
31+
32+
```kotlin
33+
const val sandboxID = "your sandbox id"
34+
```
2735

2836
## Token generation
2937

app/src/main/java/io/livekit/android/example/voiceassistant/TokenExt.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ const val sandboxID = ""
88
// and using one of your API Keys to generate a token with custom TTL and permissions.
99
const val hardcodedUrl = ""
1010
const val hardcodedToken = ""
11+
12+
// Fallback: if other connection details are empty, connect to the homepage agent.
13+
const val homepageAgentEndpoint = "https://livekit.com/api/homepage-agent/token"

app/src/main/java/io/livekit/android/example/voiceassistant/screen/ConnectScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import androidx.compose.ui.unit.sp
4040
import io.livekit.android.example.voiceassistant.R
4141
import io.livekit.android.example.voiceassistant.hardcodedToken
4242
import io.livekit.android.example.voiceassistant.hardcodedUrl
43+
import io.livekit.android.example.voiceassistant.homepageAgentEndpoint
4344
import io.livekit.android.example.voiceassistant.sandboxID
4445
import io.livekit.android.example.voiceassistant.ui.theme.Blue500
4546
import kotlinx.serialization.Serializable
@@ -107,7 +108,8 @@ fun ConnectScreen(
107108
val route = VoiceAssistantRoute(
108109
sandboxId = sandboxID,
109110
hardcodedUrl = hardcodedUrl,
110-
hardcodedToken = hardcodedToken
111+
hardcodedToken = hardcodedToken,
112+
homepageAgentEndpoint = homepageAgentEndpoint
111113
)
112114
navigateToVoiceAssistant(route)
113115
}

app/src/main/java/io/livekit/android/example/voiceassistant/screen/VoiceAssistantScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ import kotlinx.serialization.Serializable
7171
data class VoiceAssistantRoute(
7272
val sandboxId: String,
7373
val hardcodedUrl: String,
74-
val hardcodedToken: String
74+
val hardcodedToken: String,
75+
val homepageAgentEndpoint: String
7576
)
7677

7778
@Composable

app/src/main/java/io/livekit/android/example/voiceassistant/ui/AgentVisualization.kt

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package io.livekit.android.example.voiceassistant.ui
22

33
import androidx.compose.animation.core.Spring
4+
import androidx.compose.animation.core.animateFloatAsState
45
import androidx.compose.animation.core.spring
56
import androidx.compose.foundation.background
67
import androidx.compose.foundation.layout.Box
8+
import androidx.compose.foundation.layout.Column
79
import androidx.compose.foundation.layout.fillMaxHeight
810
import androidx.compose.foundation.layout.fillMaxSize
911
import androidx.compose.foundation.layout.fillMaxWidth
12+
import androidx.compose.foundation.layout.padding
1013
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Text
1115
import androidx.compose.runtime.Composable
1216
import androidx.compose.runtime.derivedStateOf
1317
import androidx.compose.runtime.getValue
@@ -17,6 +21,7 @@ import androidx.compose.runtime.remember
1721
import androidx.compose.runtime.setValue
1822
import androidx.compose.ui.Alignment
1923
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.draw.alpha
2025
import androidx.compose.ui.graphics.SolidColor
2126
import androidx.compose.ui.layout.onSizeChanged
2227
import androidx.compose.ui.platform.LocalDensity
@@ -80,22 +85,37 @@ fun AgentVisualization(
8085
return@derivedStateOf max(0f, (widthPx / height))
8186
}
8287
}
83-
VoiceAssistantBarVisualizer(
84-
agentState = agent.agentState,
85-
audioTrackRef = agent.audioTrack,
86-
barCount = 5,
87-
minHeight = barMinHeightPercent,
88-
barWidth = barWidth,
89-
brush = SolidColor(MaterialTheme.colorScheme.onBackground),
90-
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
91-
modifier = Modifier
92-
.fillMaxWidth(0.75f)
93-
.fillMaxHeight(0.22f)
94-
.onSizeChanged { size ->
95-
width = size.width
96-
height = size.height
97-
}
98-
)
88+
Column(horizontalAlignment = Alignment.CenterHorizontally) {
89+
VoiceAssistantBarVisualizer(
90+
agentState = agent.agentState,
91+
audioTrackRef = agent.audioTrack,
92+
barCount = 5,
93+
minHeight = barMinHeightPercent,
94+
barWidth = barWidth,
95+
brush = SolidColor(MaterialTheme.colorScheme.onBackground),
96+
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
97+
modifier = Modifier
98+
.fillMaxWidth(0.75f)
99+
.fillMaxHeight(0.22f)
100+
.onSizeChanged { size ->
101+
width = size.width
102+
height = size.height
103+
}
104+
)
105+
106+
val waitingAlpha by animateFloatAsState(
107+
targetValue = if (agent.isConnected) 0f else 1f,
108+
label = "waitingAlpha"
109+
)
110+
Text(
111+
text = "Waiting for agent",
112+
style = MaterialTheme.typography.bodySmall,
113+
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f),
114+
modifier = Modifier
115+
.padding(top = 16.dp)
116+
.alpha(waitingAlpha)
117+
)
118+
}
99119
}
100120
},
101121
modifier = Modifier.fillMaxSize(),

app/src/main/java/io/livekit/android/example/voiceassistant/viewmodel/VoiceAssistantViewModel.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package io.livekit.android.example.voiceassistant.viewmodel
22

33
import android.app.Application
4+
import android.util.Log
45
import androidx.lifecycle.AndroidViewModel
56
import androidx.lifecycle.SavedStateHandle
67
import androidx.navigation.toRoute
78
import io.livekit.android.LiveKit
89
import io.livekit.android.example.voiceassistant.screen.VoiceAssistantRoute
910
import io.livekit.android.token.TokenSource
10-
import io.livekit.android.token.cached
11+
import java.net.URI
1112

1213
/**
1314
* This ViewModel handles holding onto the Room object, so that it is
@@ -20,12 +21,17 @@ class VoiceAssistantViewModel(application: Application, savedStateHandle: SavedS
2021
val tokenSource: TokenSource
2122

2223
init {
23-
val (sandboxId, url, token) = savedStateHandle.toRoute<VoiceAssistantRoute>()
24+
val (sandboxId, url, token, homepageAgentEndpoint) = savedStateHandle.toRoute<VoiceAssistantRoute>()
2425

2526
tokenSource = if (sandboxId.isNotEmpty()) {
26-
TokenSource.fromSandboxTokenServer(sandboxId = sandboxId).cached()
27+
TokenSource.fromSandboxTokenServer(sandboxId = sandboxId)
28+
} else if (url.isNotEmpty() && token.isNotEmpty()) {
29+
TokenSource.fromLiteral(url, token)
2730
} else {
28-
TokenSource.fromLiteral(url, token).cached()
31+
if (url.isNotEmpty() || token.isNotEmpty()) {
32+
Log.w(TAG, "hardcodedUrl and hardcodedToken must both be set; falling back to the homepage agent.")
33+
}
34+
TokenSource.fromEndpoint(URI(homepageAgentEndpoint).toURL())
2935
}
3036
}
3137

@@ -34,4 +40,8 @@ class VoiceAssistantViewModel(application: Application, savedStateHandle: SavedS
3440
room.disconnect()
3541
room.release()
3642
}
43+
44+
companion object {
45+
private const val TAG = "VoiceAssistantViewModel"
46+
}
3747
}

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
3535
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
3636
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
3737
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
38-
livekit-lib = { group = "io.livekit", name = "livekit-android", version = "2.23.1" }
39-
livekit-components = { group = "io.livekit", name = "livekit-android-compose-components", version = "2.1.1" }
38+
livekit-lib = { group = "io.livekit", name = "livekit-android", version = "2.27.0" }
39+
livekit-components = { group = "io.livekit", name = "livekit-android-compose-components", version = "2.4.1" }
4040
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
4141
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
4242
timberkt = { module = "com.github.ajalt:timberkt", version.ref = "timberkt" }

0 commit comments

Comments
 (0)