microscopic-hydrogen-58853
07/30/2025, 2:29 PMtall-airline-16756
07/30/2025, 2:31 PMtall-airline-16756
07/30/2025, 2:32 PMmicroscopic-hydrogen-58853
07/30/2025, 3:27 PMtall-airline-16756
07/30/2025, 3:27 PMrememberLiveKitRoom function, your room instance stays put unless you specifically change the parameters. It's like having a reliable friend who doesn't randomly switch things up!
• Connection Management: Think of the connection logic as a careful doorkeeper. It only tries to connect when something important changes, like:
◦ The room instance
◦ The connect boolean flag
◦ The server URL
◦ The authentication token
◦ The connection options
• Disconnection Control: The RoomScope gives you flexibility with the disconnectOnDispose parameter. By default, it'll disconnect when the composable is no longer in view, but you can easily set it to false if you want to keep the connection alive.
The bottom line? LiveKit is designed to keep your connections smooth and stable. It only reconnects when absolutely necessary, so you can focus on what really matters – your awesome app!
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/state/R...microscopic-hydrogen-58853
07/30/2025, 3:30 PMtall-airline-16756
07/30/2025, 3:31 PMrememberLiveKitRoom function in the LiveKit Compose SDK for you – think of it like a helpful buddy that makes managing your video chat room super smooth. 😊
What's rememberLiveKitRoom all about?
Imagine this function as your personal room management assistant that takes care of:
• Creating your room (or using an existing one)
• Connecting automatically with just a URL and token
• Handling all your audio and video tracks like a pro
• Managing connections so you don't have to worry about disconnecting
• Keeping you updated with all the room's state changes
It's super flexible and lets you customize things like:
• Connection details
• Turning audio/video on or off
• Controlling how and when you connect
• Setting up all sorts of cool configurations
So, what makes it different from RoomScope?
Great question! Think of it like this:
• `rememberLiveKitRoom`:
◦ Gives you the Room object directly – you're in control!
◦ You'll need to manually pass the Room around
• `RoomScope`:
◦ Works behind the scenes to create your Room
◦ Magically makes the Room available everywhere in your app
◦ Handles all the context sharing for you
When should you use each?
• Grab rememberLiveKitRoom when:
◦ You want hands-on control of your Room
◦ You're building something custom
◦ You prefer doing things manually
• Go for RoomScope when:
◦ You want things to just work automatically
◦ You're using other LiveKit components
◦ You like a more "set it and forget it" approach
The cool part? Most LiveKit functions are super flexible and will work with either approach. They're like those friendly team players who get along with everyone! 🤝
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/chat/Ch...microscopic-hydrogen-58853
07/30/2025, 3:33 PMtall-airline-16756
07/30/2025, 3:34 PMrememberLiveKitRoom and RoomScope in the LiveKit Compose SDK. I'll break down their differences in a way that's super easy to understand.
Example: Using RoomScope
RoomScope(
url = connectionDetails.wsUrl,
token = connectionDetails.token,
audio = rememberEnableMic(enableAudio),
video = rememberEnableCamera(enableVideo),
roomOptions = DefaultRoomOptions { options ->
options.copy(
videoTrackCaptureDefaults = LocalVideoTrackOptions(
position = initialCameraPosition
)
)
},
liveKitOverrides = DefaultLKOverrides(context),
onConnected = {
// Handle connection
},
onDisconnected = {
// Handle disconnection
},
onError = { _, error ->
// Handle errors
}
) { room ->
// Content that can access the room
// Child composables can also access room via RoomLocal.current
}
Example: Using rememberLiveKitRoom
@Composable
fun MyRoomComponent() {
val room = rememberLiveKitRoom(
url = "<wss://your-livekit-server.com>",
token = "your-token",
audio = true,
video = true,
connect = true,
onConnected = { room ->
// Handle connection
},
onDisconnected = { room ->
// Handle disconnection
},
onError = { room, error ->
// Handle errors
}
)
// Use the room directly
val connectionState = rememberConnectionState(room)
// ... rest of your UI
}
Key Differences
• Room Access Pattern:
◦ `RoomScope`: Think of this like a magic box that automatically shares the room with all its child components. Super convenient!
◦ `rememberLiveKitRoom`: More of a DIY approach where you manually pass the room around.
• Implementation Relationship: Fun fact - RoomScope actually uses rememberLiveKitRoom under the hood!
• Composition Local Integration:
◦ `RoomScope`: Automatically sets up sharing, so your components just "know" about the room.
◦ `rememberLiveKitRoom`: Gives you more hands-on control over how the room is managed.
• Use Cases:
◦ Grab RoomScope when you want a clean, easy setup with multiple components needing room access.
◦ Choose rememberLiveKitRoom when you want more precise control over your room's behavior.
Pro tip: Both of these functions are super flexible and support the same cool parameters like disconnectOnDispose for managing your connection lifecycle. Pretty neat, right?
Sources: sample-livestream/src/main/java/io/livekit/android/sample/livestream/room/s... | app/src/main/java/io/livekit/android/example/voiceassistant/screen/VoiceAss... | livekit-compose-components/src/test/java/io/livekit/android/compose/flows/R...microscopic-hydrogen-58853
07/30/2025, 3:38 PMtall-airline-16756
07/30/2025, 3:39 PMRoomScope in a way that's super easy to understand.
1. Room Creation vs. Room Reuse
• Passing Parameters: If you don't have a room ready to go (passedRoom = null), no worries! RoomScope will whip up a brand new Room instance using the parameters you provide.
• Passing an Existing Room: Already have a room? Cool! RoomScope will happily use that room instead of creating a new one.
2. How Parameters Work Differently
• When creating a new room: All your settings (like roomOptions, liveKitOverrides, connectOptions) get applied to set up the room just the way you want.
• When passing an existing room:
◦ roomOptions get applied to the existing room through DisposableEffect.
◦ liveKitOverrides won't change since the room is already set up.
◦ Connection details (like url, token, connectOptions) are still used for connecting.
3. Lifecycle Management Nuances
• When RoomScope creates a room:
◦ The room gets disconnected and released when the composable is done.
• When you pass a room to `RoomScope`:
◦ The disconnectOnDispose parameter controls disconnection behavior.
◦ The room can stick around even after the composable is gone.
4. Connection Management Pro Tip
The disconnectOnDispose parameter is super handy when using a passed room. It lets you keep the room connected for things like background services.
5. When to Use Each Approach
• Pass an existing room: Perfect when you want the room to live on beyond a single UI component, like for background services or sharing across multiple screens.
• Pass parameters to create a room: Great when the room's lifecycle is tied closely to a specific UI component.
Both ways work great – just pick the one that fits your specific needs!
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/state/R... | app/src/main/java/io/livekit/android/example/voiceassistant/screen/VoiceAss...microscopic-hydrogen-58853
07/30/2025, 3:48 PMtall-airline-16756
07/30/2025, 3:50 PMRoomScope in a way that'll make total sense. Think of it like choosing the right tool for the job – I'll walk you through when and how to connect your room, just like a friend helping you set up something awesome.
When to Use Each Approach
• Use an Already Created Room:
◦ When you want to keep your Room running in the background, kind of like having a chat room that stays open even when you're not looking at it.
◦ When you've got a room that's being shared across different parts of your app – super handy for keeping things connected!
• Use Parameters to Create a Room:
◦ When you want the room to live and die with your current screen – simple and straightforward.
◦ Perfect for those quick, no-fuss connections where you don't need anything too complicated.
Code Examples
1. Using an Already Created Room with ViewModel
// ViewModel
class CallViewModel : ViewModel() {
private val _room = LiveKit.create(
appContext = context.applicationContext,
options = RoomOptions(),
overrides = LiveKitOverrides()
)
val room: Room = _room
// Other ViewModel logic...
}
// Composable
@Composable
fun CallScreen(viewModel: CallViewModel) {
RoomScope(
passedRoom = viewModel.room,
disconnectOnDispose = false, // Keep room alive beyond UI
onConnected = { room ->
// Handle connection
},
onDisconnected = { room ->
// Handle disconnection
}
) { room ->
// Your UI content
Content(room)
}
}
2. Using Parameters to Create a Room
@Composable
fun SimpleRoomScreen(url: String, token: String) {
RoomScope(
url = url,
token = token,
audio = true,
video = true,
connect = true,
onConnected = { room ->
// Room connected
},
onDisconnected = { room ->
// Room disconnected
}
) { room ->
// Your UI content
Content(room)
}
}
Listening to Events
Using HandleRoomState for Event Handling
@Composable
fun RoomEventHandler() {
// Listen to specific states
HandleRoomState(
states = listOf(Room.State.CONNECTED, Room.State.DISCONNECTED),
onState = { room, state ->
when (state) {
Room.State.CONNECTED -> {
// Handle connection
}
Room.State.DISCONNECTED -> {
// Handle disconnection
}
else -> {}
}
}
)
// Listen to a single state
HandleRoomState(
state = Room.State.CONNECTED,
onState = { room, state ->
// Handle connected state
}
)
}
Complete Example with ViewModel Integration
@Composable
fun RoomScreenWithViewModel(
viewModel: CallViewModel,
url: String,
token: String
) {
val participants by viewModel.participants.collectAsState(initial = emptyList())
val micEnabled by viewModel.micEnabled.collectAsState(true)
val videoEnabled by viewModel.cameraEnabled.collectAsState(true)
RoomScope(
url = url,
token = token,
audio = micEnabled,
video = videoEnabled,
connect = true,
onConnected = { room ->
viewModel.onRoomConnected(room)
},
onDisconnected = { room ->
viewModel.onRoomDisconnected(room)
},
onError = { room, error ->
viewModel.handleError(error)
}
) { room ->
// Handle room state changes
HandleRoomState(
states = listOf(Room.State.CONNECTED),
passedRoom = room,
onState = { _, _ ->
// Enable audio/video once connected
launch {
room.localParticipant.setMicrophoneEnabled(micEnabled)
room.localParticipant.setCameraEnabled(videoEnabled)
}
}
)
// Your UI content
Content(
room = room,
participants = participants,
onToggleMic = { viewModel.toggleMicrophone() },
onToggleCamera = { viewModel.toggleCamera() }
)
}
}
Event Handling in ViewModel
class CallViewModel : ViewModel() {
private val _participants = MutableStateFlow<List<Participant>>(emptyList())
val participants = _participants.asStateFlow()
private val _micEnabled = MutableStateFlow(true)
val micEnabled = _micEnabled.asStateFlow()
fun onRoomConnected(room: Room) {
// Set up room event listeners
viewModelScope.launch {
room::participants.flow.collect { participantList ->
_participants.value = participantList
}
}
}
fun toggleMicrophone() {
_micEnabled.value = !_micEnabled.value
}
fun handleError(error: Exception?) {
// Handle room errors
}
}
Pro tip: Using passedRoom with disconnectOnDispose = false gives you super flexible room management. It's like having a chat room that stays open in the background, ready whenever you need it. Pretty cool, right? 😊
Sources: livekit-compose-components/src/test/java/io/livekit/android/compose/local/R...microscopic-hydrogen-58853
07/31/2025, 3:35 PMtall-airline-16756
07/31/2025, 3:37 PMRoomScope in the LiveKit Compose SDK, explaining what they do, when you might use them, and how they affect your app:
Connection Parameters
• url: String? = null
◦ Use Case: This is where you put the WebSocket URL for your LiveKit server.
◦ Impact: It’s essential for connecting; if it’s empty or null, the connection won’t happen.
◦ Functionality: It tells the app which server to connect to.
• token: String? = null
◦ Use Case: This token handles authentication and lets you into the room.
◦ Impact: You need it to connect; without it, no connection will be made.
◦ Functionality: It carries your identity, permissions, and access rights.
• connect: Boolean = true
◦ Use Case: Controls whether the connection happens automatically or not.
◦ Impact: If set to false, even with a URL and token, it won’t connect automatically.
◦ Functionality: Gives you control to connect manually when you’re ready.
Media Parameters
• audio: Boolean = false
◦ Use Case: Decides if your microphone is on when you join.
◦ Impact: Sets whether your mic is enabled right as you connect.
◦ Functionality: Automatically turns your mic on or off for the local participant.
• video: Boolean = false
◦ Use Case: Controls if your camera is on when you join.
◦ Impact: Sets whether your camera is enabled right as you connect.
◦ Functionality: Automatically turns your camera on or off for the local participant.
Configuration Parameters
• roomOptions: RoomOptions? = null
◦ Use Case: Lets you customize how the room behaves, including default track settings and advanced features.
◦ Impact: Controls audio/video track capture, publishing defaults, adaptive streaming, and encryption options.
◦ Functionality: Gives you full control over room configuration and media settings.
• liveKitOverrides: LiveKitOverrides? = null
◦ Use Case: Makes it possible to tweak the LiveKit SDK’s internal behavior and settings.
◦ Impact: Only applies when the room is first created; changes afterward won’t take effect.
◦ Functionality: Perfect for platform-specific or advanced SDK customizations.
• connectOptions: ConnectOptions? = null
◦ Use Case: Lets you set connection details like timeouts and protocols.
◦ Impact: Only matters when establishing the connection; ignored if already connected.
◦ Functionality: Helps you control how the connection behaves.
Event Handlers
• onConnected: (suspend CoroutineScope.(Room) -> Unit)? = null
◦ Use Case: Run your code right after the room connects successfully.
◦ Impact: Fires when the room state changes to CONNECTED.
◦ Functionality: Handy for initializing things once you’re connected.
• onDisconnected: (suspend CoroutineScope.(Room) -> Unit)? = null
◦ Use Case: Clean up or navigate away when the room disconnects.
◦ Impact: Fires when the room state changes to DISCONNECTED.
◦ Functionality: Helps you handle disconnections smoothly.
• onError: ((Room, Exception?) -> Unit)? = null
◦ Use Case: Deal with any connection errors or exceptions.
◦ Impact: If you don’t provide one, a default error handler will log errors for you.
◦ Functionality: Lets you manage errors however you want.
Advanced Parameters
• passedRoom: Room? = null
◦ Use Case: Use an existing Room instance instead of creating a new one.
◦ Impact: Makes it easy to share a Room across different parts of your UI.
◦ Functionality: Great for managing room lifecycle more flexibly.
• disconnectOnDispose: Boolean = true
◦ Use Case: Decide if the room disconnects when the UI component is disposed.
◦ Impact: If true, disconnects automatically; if false, keeps the connection alive.
◦ Functionality: Useful for background tasks or keeping connections persistent.
• content: @Composable (room: Room) -> Unit
◦ Use Case: Define the UI that works with the Room object.
◦ Impact: Makes the Room available to child composables via RoomLocal.
◦ Functionality: Lets your UI components easily access the Room through composition local.
All these parameters give you lots of flexibility to manage your room connections, media settings, and lifecycle events, making RoomScope really adaptable to whatever you need. If you have any questions or want to dive deeper into any part, just let me know—I’m here to help!
Sources: livekit-compose-components/src/test/java/io/livekit/android/compose/local/R... | sample-livestream/src/main/java/io/livekit/android/sample/livestream/room/s...microscopic-hydrogen-58853
07/31/2025, 3:41 PMtall-airline-16756
07/31/2025, 3:42 PMRoomScope so you get a clear picture:
1. roomOptions: RoomOptions? = null
• Purpose: This is where you set up how your room behaves and what features it has.
• Key Properties:
◦ `adaptiveStream`: Turns adaptive streaming on or off, which smartly adjusts video quality based on the network.
◦ `e2eeOptions`: Lets you set up end-to-end encryption (E2EE) for keeping your communication secure.
◦ `audioTrackCaptureDefaults`: Sets the default way audio tracks are captured.
◦ `videoTrackCaptureDefaults`: Sets default options for capturing video, like resolution and frame rate.
◦ `audioTrackPublishDefaults`: Defines how audio tracks are published by default.
◦ `videoTrackPublishDefaults`: Defines default settings for publishing video tracks.
• Use Case: Think of this as your go-to for customizing how media tracks are captured and shared, plus turning on cool features like adaptive streaming and encryption.
2. liveKitOverrides: LiveKitOverrides? = null
• Purpose: Lets you tweak platform-specific details for how LiveKit works.
• Impact: These settings kick in only when the room is first created — you can’t change them later.
• Use Case: Use this if you need to fine-tune the SDK for special platform needs or advanced setups.
3. connectOptions: ConnectOptions? = null
• Purpose: Controls connection settings when you first connect to the LiveKit server.
• Impact: These options apply only at the initial connection — they won’t affect rooms you’re already connected to.
• Use Case: Handy for managing connection details like timeouts, protocols, or other connection-specific preferences.
All in all, these configuration options give you plenty of flexibility to tailor RoomScope exactly how you need it — whether it’s the room’s behavior, media handling, or connection setup. Feel free to experiment and find what works best for you!
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/local/R...microscopic-hydrogen-58853
07/31/2025, 3:45 PMtall-airline-16756
07/31/2025, 3:47 PMroomOptions parameter for RoomScope. I’ll break it down so it’s easy to follow:
Core Room Behavior Properties
• adaptiveStream: Boolean (default: false)
◦ What it does: Lets LiveKit automatically adjust the quality of video tracks you’re subscribed to, so it optimizes bandwidth and CPU usage.
◦ Why it matters: When turned on, it changes the video resolution based on how big the video is on your screen and even pauses the video data if you’re not actually looking at it.
• dynacast: Boolean (default: false)
◦ What it does: Enables dynamic casting, meaning it pauses video layers that no one is watching.
◦ Why it matters: This can save a lot on CPU power and bandwidth when publishing video.
• e2eeOptions: E2EEOptions? (default: null)
◦ What it does: Sets up end-to-end encryption for your room’s communication.
◦ Why it matters: When you add this, all communication in the room is securely encrypted.
Audio Track Configuration
• audioTrackCaptureDefaults: LocalAudioTrackOptions? (default: null)
◦ What it does: Lets you set default options for capturing audio tracks.
◦ Why it matters: You can control things like the audio source and processing settings.
• audioTrackPublishDefaults: AudioTrackPublishDefaults? (default: null)
◦ What it does: Sets default options for publishing your audio tracks.
◦ Why it matters: Helps you manage how audio is encoded and sent out.
Video Track Configuration
• videoTrackCaptureDefaults: LocalVideoTrackOptions? (default: null)
◦ What it does: Lets you set default options for capturing video tracks.
◦ Why it matters: You can specify resolution, frame rate, camera position, and more.
• videoTrackPublishDefaults: VideoTrackPublishDefaults? (default: null)
◦ What it does: Sets default options for publishing video tracks.
◦ Why it matters: Controls video encoding, simulcast, and other publishing settings.
Screen Share Configuration
• screenShareTrackCaptureDefaults: LocalVideoTrackOptions? (default: null)
◦ What it does: Sets default options just for capturing screen share tracks.
◦ Why it matters: Similar to video capture settings but tailored for screen sharing.
• screenShareTrackPublishDefaults: VideoTrackPublishDefaults? (default: null)
◦ What it does: Sets default options for publishing screen share tracks.
◦ Why it matters: Controls how the screen share video is encoded and sent.
All these properties give you the flexibility to customize your room’s behavior and media settings exactly how you want. Don’t worry about setting everything — they’re all optional and come with smart defaults, so you only need to adjust what matters most for your situation.
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/local/R... | livekit-android-sdk/src/main/java/io/livekit/android/RoomOptions.ktmicroscopic-hydrogen-58853
07/31/2025, 3:49 PMtall-airline-16756
07/31/2025, 3:50 PMaudioTrackCaptureDefaults: LocalAudioTrackOptions?
◦ Purpose: This sets your go-to options for capturing audio from your microphone.
◦ Impact: It helps control how your audio is processed and captured.
◦ Example: audioTrackCaptureDefaults = LocalAudioTrackOptions()
• audioTrackPublishDefaults: AudioTrackPublishDefaults?
◦ Purpose: Here, you define the default settings for publishing your audio tracks.
◦ Impact: This influences encoding, bitrate, and cool features like DTX (Discontinuous Transmission) to optimize audio quality.
◦ Key Properties:
▪︎ `audioBitrate`: Sets your target audio bitrate (for example, 48,000 bps, great for music).
▪︎ `dtx`: Turns on DTX for mono tracks (usually true by default).
▪︎ `red`: Enables redundant audio data for mono tracks (also true by default).
◦ Example: audioTrackPublishDefaults = AudioTrackPublishDefaults(
audioBitrate = 48000,
dtx = true,
red = true
)
Video Configuration
• videoTrackCaptureDefaults: LocalVideoTrackOptions?
◦ Purpose: This sets your default options for capturing video from your camera.
◦ Impact: You get to control things like resolution, frame rate, and camera position.
◦ Example: videoTrackCaptureDefaults = LocalVideoTrackOptions(
resolution = VideoResolution.HD,
frameRate = 30
)
• videoTrackPublishDefaults: VideoTrackPublishDefaults?
◦ Purpose: These are your default settings for publishing video tracks.
◦ Impact: You can manage video encoding, simulcast options, and codec preferences.
◦ Key Properties:
▪︎ `videoEncoding`: Defines specific encoding parameters for your video.
▪︎ `simulcast`: Lets you enable simulcast for multiple resolutions (default is true).
▪︎ `videoCodec`: Specifies which video codec to use (default is VP8).
▪︎ `degradationPreference`: Helps handle quality drops if network conditions get tricky.
◦ Example: videoTrackPublishDefaults = VideoTrackPublishDefaults(
videoEncoding = VideoEncoding.H264,
simulcast = true,
videoCodec = VideoCodec.VP8
)
Screen Share Configuration
• screenShareTrackCaptureDefaults: LocalVideoTrackOptions?
◦ Purpose: This one’s tailored for capturing your screen share tracks.
◦ Impact: Similar to video capture settings but optimized specifically for screen sharing.
◦ Example: screenShareTrackCaptureDefaults = LocalVideoTrackOptions(
isScreencast = true,
captureParams = ScreenSharePresets.ORIGINAL.capture
)
• screenShareTrackPublishDefaults: VideoTrackPublishDefaults?
◦ Purpose: Sets your default options for publishing screen share tracks.
◦ Impact: Controls how your screen share content is encoded and sent over.
◦ Example: screenShareTrackPublishDefaults = VideoTrackPublishDefaults(
videoEncoding = ScreenSharePresets.ORIGINAL.encoding
)
These options give you the flexibility to fine-tune how your audio and video are captured and shared, helping you get the best performance and quality that fits exactly what you need. If you have any questions or want to tweak something specific, just let me know—I’m here to help!
Sources: livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Local... | livekit-compose-components/src/main/java/io/livekit/android/compose/local/R...microscopic-hydrogen-58853
07/31/2025, 9:12 PMtall-airline-16756
07/31/2025, 9:13 PMRoom object is your go-to for managing connections, participants, and data exchange. It’s super important to handle its lifecycle properly to avoid any resource leaks or unexpected hiccups.
2. How to Use Room in ViewModel or Compose
• ViewModel Approach:
◦ Keep the Room object inside your ViewModel, connecting it when your ViewModel initializes and cleaning up in onCleared.
◦ This is great for keeping the Room alive through configuration changes or when its lifecycle isn’t tied to just one UI component.
◦ For example, a ViewModel can hold onto the Room to keep audio or video states steady even as the UI changes.
• Compose Scoping Approach:
◦ Use RoomScope or rememberLiveKitRoom to manage the Room within your Compose UI hierarchy.
◦ Compose will take care of connecting when your component appears and disconnecting when it’s gone.
◦ This works best when the Room’s lifecycle matches your UI component’s lifecycle.
3. Important Tips When Using Room in a ViewModel
• Lifecycle Management: Make sure to connect the Room when needed and clean everything up properly in onCleared to avoid surprises.
• State Management: Use flows or LiveData to keep track of the Room’s state (like connected or disconnected) and update your UI smoothly.
• Audio/Video Permissions: Double-check that you have the right permissions before turning on audio or video.
• Error Handling: Set up error listeners so you can handle connection issues without stressing your users.
• Configuration Changes: If your ViewModel keeps the Room across configuration changes, make sure you preserve the state properly.
4. Example of How to Do This in a ViewModel
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val room = LiveKit.create(application)
init {
viewModelScope.launch {
room.withPreconnectAudio {
room.connect(url, token)
}
}
}
override fun onCleared() {
super.onCleared()
room.disconnect()
room.release()
}
}
5. Quick Best Practices Recap
• Use ViewModel for Lifecycles Beyond One Component: When your Room needs to stick around across UI changes or multiple components.
• Use Compose Scoping for UI-Focused Use: When the Room’s lifecycle matches your UI components.
• Handle Permissions and Errors: Always check permissions and have error handling in place to keep things running smoothly.
• Clean Up Properly: Don’t forget to disconnect and release the Room in onCleared to keep your app healthy.
Follow these tips, and you’ll be managing your Room object inside a ViewModel like a pro—keeping resources in check and your users happy.microscopic-hydrogen-58853
08/01/2025, 9:15 AMtall-airline-16756
08/01/2025, 9:16 AMRoomScope can totally handle reconnecting automatically when you pass a Room object from a ViewModel. That said, there are a few important things to keep in mind:
How Reconnection Works:
• Automatic Reconnection: LiveKit’s connection reliability features take care of network changes and reconnecting for you. So, if the connection drops because of network issues or a server shutdown, the Room will try to reconnect and pick up right where it left off.
• RoomScope Behavior: When connect is set to true (which is the default), RoomScope will do its best to keep the connection alive. And if you provide a passedRoom, it’ll use that Room object instead of creating a new one.
A Couple of Things to Watch Out For:
• Explicit Disconnection: If your ViewModel explicitly disconnects the Room or if you have disconnectOnDispose set to true, then RoomScope won’t try to reconnect automatically.
• Lifecycle Management: When you’re passing a passedRoom, the reconnection depends on how you manage the Room in your ViewModel. For example, if disconnectOnDispose is set to false, the Room will stay connected even after RoomScope gets disposed.
Best Practices to Keep Things Smooth:
• Make sure you handle connection and disconnection properly within your ViewModel’s lifecycle methods, like onCleared().
• When passing a Room to RoomScope from a ViewModel, try setting disconnectOnDispose = false to keep the connection alive across composition changes.
Following these tips will help you keep the Room connected and let it handle reconnections automatically whenever needed. If you have any questions or need a hand, just let me know!
Sources: livekit-compose-components/src/main/java/io/livekit/android/compose/state/R... | livekit-android-test/src/test/java/io/livekit/android/room/RoomTest.kt | Connecting to LiveKit | LiveKit Docs | app/src/main/java/io/livekit/android/example/voiceassistant/screen/VoiceAss...microscopic-hydrogen-58853
08/01/2025, 9:49 AMtall-airline-16756
08/01/2025, 9:49 AMdisconnectOnDispose = false in RoomScope, the Room will stay connected and be managed separately from the ViewModel’s lifecycle.
How This Works:
• Disconnecting in ViewModel: By explicitly disconnecting the Room in the ViewModel’s onCleared(), you’re making sure everything is cleaned up properly when the ViewModel is destroyed.
• `disconnectOnDispose = false`: This stops RoomScope from disconnecting the Room when the composable goes out of scope, so the Room can keep going through composition changes.
Best Practices:
• Make sure the Room is connected in RoomScope or somewhere else appropriate if it’s not connected in the ViewModel.
• Handle reconnection logic in RoomScope or use LiveKit’s automatic reconnection features to keep things smooth.
• Always clean up resources in the ViewModel to avoid any memory leaks.
This way, you get the flexibility you need while keeping the Room’s lifecycle managed just right.microscopic-hydrogen-58853
08/01/2025, 10:07 AMtall-airline-16756
08/01/2025, 10:07 AMRoom.activeSpeakers includes everyone who's currently chatting, sorted from the loudest to the quietest. If you need help with it, just let me know!
Sources: pkg/rtc/room.go | src/room/events.tsmicroscopic-hydrogen-58853
08/01/2025, 10:08 AMtall-airline-16756
08/01/2025, 10:08 AMRoom.activeSpeakers only shows the participants who are actually speaking at the moment. So, anyone who's quiet or not talking right now won’t appear on that list.
Sources: pkg/rtc/room.go | Subscribing to tracks | LiveKit Docs | src/room/events.tsmicroscopic-hydrogen-58853
08/01/2025, 12:41 PMtall-airline-16756
08/01/2025, 12:42 PMparticipant.identity can be null in LiveKit because it’s set up as an optional field in the protocol buffer schema and SDKs. This gives you some flexibility depending on what you need, like:
• If the developer doesn’t provide an identity explicitly.
• For certain participant types where an identity isn’t really necessary.
If no identity is given, the server might just create a random one for the participant. This way, everything stays smooth and compatible across different situations and SDKs.
Sources: livekit-protocol/src/livekit.rs | Sources/LiveKit/Participant/Participant.swift | livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Local... | Runtime/Scripts/Proto/Participant.cs | Rooms, participants, and tracks | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 12:43 PMtall-airline-16756
08/01/2025, 12:43 PMparticipant.identity can sometimes be null is just to cover cases where it might not be set or needed. But once the server assigns it, you can count on it having a value.microscopic-hydrogen-58853
08/01/2025, 12:47 PMtall-airline-16756
08/01/2025, 12:48 PMVideoTrackView composable in the LiveKit Compose SDK is made specifically for showing video tracks and doesn’t handle audio. But don’t worry—audio tracks are taken care of automatically by the LiveKit SDK and usually play through your device’s audio output.
Sources: README.md | Android quickstart (Jetpack Compose) | LiveKit Docs | Android SDK | livekit/livekit_composite | DeepWikimicroscopic-hydrogen-58853
08/01/2025, 12:48 PMtall-airline-16756
08/01/2025, 12:48 PMVideoTrackView. If you have any questions, I’m here to help!microscopic-hydrogen-58853
08/01/2025, 12:51 PMtall-airline-16756
08/01/2025, 12:52 PM// Kotlin (Android Compose)
val videoPub = videoPubs.firstOrNull { pub -> pub.source == Track.Source.SCREEN_SHARE }
?: videoPubs.firstOrNull { pub -> pub.source == Track.Source.CAMERA }
?: videoPubs.firstOrNull()
if (videoTrack != null && !videoMuted) {
// Render the video track
VideoRenderer(
room = room,
videoTrack = videoTrack,
modifier = modifier,
)
} else {
// Show placeholder
Box(modifier = modifier) {
Icon(
painter = painterResource(id = R.drawable.outline_videocam_off_24),
contentDescription = null,
tint = Color.White,
modifier = Modifier.align(Alignment.Center),
)
}
}
In this example, we’re giving priority to screen share over camera video, and if the video is muted or not there, it shows a nice placeholder instead.
2. Showing a Placeholder when Video is Muted
Just a heads-up: LiveKit doesn’t automatically pop up a placeholder when a video track is muted. But no worries—you can easily add this to your UI yourself. Here’s how you might do it:
// Kotlin (Android Compose)
if (videoTrack != null && !videoMuted) {
VideoRenderer(...)
} else {
Box(modifier = modifier) {
Icon(
painter = painterResource(id = R.drawable.outline_videocam_off_24),
contentDescription = null,
tint = Color.White,
modifier = Modifier.align(Alignment.Center),
)
}
}
This little snippet will show a video-off icon anytime the video track is muted or just not available.
Additional Notes:
• Feel free to tweak the placeholder by changing the UI components inside the "else" block to match your style.
• LiveKit keeps track of mute status updates and lets subscribers know through its event system, so you’re always in the loop.
If you want me to walk you through anything else or need more examples, just give me a shout!
Sources: README.md | src/ParticipantView.tsx | sample-app-compose/src/main/java/io/livekit/android/composesample/VideoItem... | Core API primitives | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 3:08 PMtall-airline-16756
08/01/2025, 3:09 PMtrackReference in the LiveKit Compose SDK, here are some easy ways to do it:
1. Use the Composition Local TrackReferenceLocal
• If you're inside a TrackReferenceScope, you can simply grab the trackReference directly using TrackReferenceLocal.current. Pretty straightforward!
2. Use the requireTrack() Function
@Composable
@Throws(IllegalStateException::class)
fun requireTrack(passedTrack: TrackReference? = null): TrackReference {
return passedTrack ?: TrackReferenceLocal.current
}
This handy function will give you the track reference or let you know if it’s not available by throwing an exception.
3. Use the rememberTracks() Composable
@Composable
fun rememberTracks(
sources: List<Track.Source> = listOf(
Track.Source.CAMERA,
Track.Source.SCREEN_SHARE
),
usePlaceholders: Set<Track.Source> = emptySet(),
passedRoom: Room? = null,
onlySubscribed: Boolean = true,
): List<TrackReference>
This one returns a list of TrackReference objects based on the sources and options you specify. Super useful!
4. Use the rememberParticipantTrackReferences() Composable
@Composable
fun rememberParticipantTrackReferences(
sources: List<Track.Source> = listOf(Track.Source.CAMERA, Track.Source.SCREEN_SHARE),
usePlaceholders: Set<Track.Source> = emptySet(),
passedParticipant: Participant? = null,
onlySubscribed: Boolean = true,
): List<TrackReference>
This lets you get track references specifically for a participant you choose. Handy when you want to focus on someone!
Example Usage:
@Composable
fun MyComposable() {
// Remember track references for camera and screen share
val tracks = rememberTracks(
sources = listOf(Track.Source.CAMERA, Track.Source.SCREEN_SHARE),
usePlaceholders = setOf(Track.Source.CAMERA),
)
ForEachTrack(tracks) { trackReference ->
TrackItem(trackReference)
}
}
@Composable
fun TrackItem(trackReference: TrackReference) {
val participant = trackReference.participant
val publication = trackReference.publication
val source = trackReference.source
// You can use this info to display the track however you like
}
Just a quick note: The TrackReference includes:
• participant: The participant who owns the track.
• publication: Details about the track publication.
• source: Where the track is coming from (like camera or screen share).
If you want me to walk you through any part of this or need more examples, just give me a shout—I’m here to help!microscopic-hydrogen-58853
08/01/2025, 3:52 PMtall-airline-16756
08/01/2025, 3:52 PMmicroscopic-hydrogen-58853
08/01/2025, 4:08 PMtall-airline-16756
08/01/2025, 4:09 PMisSpeaking property in the LiveKit Android SDK, you’ve got a couple of friendly options: the Flow API or the ParticipantEvent system. Here’s a quick rundown:
1. Using Flow API
participant::isSpeaking.flow.collect { isSpeaking ->
if (isSpeaking) {
showFocus(viewBinding)
} else {
hideFocus(viewBinding)
}
}
This way, you can easily watch the isSpeaking property changes using Kotlin’s Flow API. Pretty neat, right?
2. Using ParticipantEvent
eventBus.postEvent(
ParticipantEvent.SpeakingChanged(this, newValue),
scope
)
Or, if you prefer a bit more flexibility, you can listen for the ParticipantEvent.SpeakingChanged event. It’s great for handling events in a centralized way.
Just a couple of things to keep in mind:
• The Flow API is super straightforward for observing changes.
• The event system gives you more flexibility if you want to manage events in one place.
Pick whichever feels right for your project! And if you want me to walk you through more examples or clarify anything, just give me a shout—I’m here to help!microscopic-hydrogen-58853
08/01/2025, 4:53 PMtall-airline-16756
08/01/2025, 4:53 PM// Node.js SDK
const token = new AccessToken(apiKey, apiSecret, {
identity: 'participant-1',
metadata: 'joined_first=true',
attributes: {
joinOrder: '1',
role: 'moderator'
}
});
# Python SDK
access_token = AccessToken(api_key, api_secret)
access_token.with_metadata("joined_first=true")
access_token.with_attributes({"joinOrder": "1", "role": "moderator"})
// Kotlin SDK
val token = AccessToken(apiKey, apiSecret)
token.metadata = "joined_first=true"
token.attributes["joinOrder"] = "1"
token.attributes["role"] = "moderator"
A few quick notes:
• `metadata`: Great for simple or unstructured info.
• `attributes`: Perfect when you want to organize data with key-value pairs.
• Both of these get stored on the LiveKit server and are shared with everyone in the room, keeping everyone in sync.
If you want a hand setting this up in a particular language or SDK, just let me know—I’m here to help!
Sources: Participant attributes and metadata | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 8:23 PMtall-airline-16756
08/01/2025, 8:23 PM// Access the track you want to mute
localParticipant.trackPublications["track_sid"]?.muted = true
This will stop the data from being sent to the server and will let everyone know by triggering a TrackMuted event.
2. Muting Remote Participants
To mute someone else’s track remotely, you can use this handy API:
// Use the MutePublishedTrack API
res, err := roomClient.MutePublishedTrack(context.Background(), &livekit.MuteRoomTrackRequest{
Room: roomName,
Identity: identity,
TrackSid: "track_sid",
Muted: true,
})
This takes care of muting the track right on the server side.
3. What Happens When a Participant is Muted by the Server
• The server sends out a TrackMuted event to everyone, so their UI updates accordingly.
• The participant’s LocalTrackPublication will also show that they’re muted.
• By default, remote unmuting is turned off, but you can easily turn it on in your project settings or by setting room.enable_remote_unmute: true if you’re self-hosting.
If you have any questions or want me to walk you through more examples, just let me know—I’m here to help!microscopic-hydrogen-58853
08/01/2025, 8:26 PMtall-airline-16756
08/01/2025, 8:27 PMconfig.yaml file with this:
room:
enable_remote_unmute: true
This setting lets admins unmute tracks remotely, making things a bit easier to manage.
2. Can a Participant or Others Unmute a Remotely Muted Track?
• With remote unmute turned on, only admins can unmute tracks that were muted remotely.
• If you'd like other participants (non-admins) to unmute tracks, you can tweak their permissions using the UpdateParticipant API. For example, set CanPublish to true for those participants so they can unmute tracks themselves.
Example API Usage:
// Mute or unmute a track using RoomServiceClient
res, err := roomClient.MutePublishedTrack(context.Background(), &livekit.MuteRoomTrackRequest{
Room: roomName,
Identity: identity,
TrackSid: "track_sid",
Muted: false, // Set to true to mute, false to unmute
})
If you have any questions or want me to walk you through an example tailored to your setup, just let me know—I’m here to help!
Sources: config-sample.yaml | Managing participants | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 8:29 PMtall-airline-16756
08/01/2025, 8:30 PM// Find the participant’s track publication
const trackPublication = participant.trackPublications.get(trackSid);
// Unsubscribe from that track
trackPublication.setSubscribed(false);
This will make sure you no longer get data from that participant’s track.
3. Difference Between Subscribing/Unsubscribing vs Enabling/Disabling Tracks
• Subscribing/Unsubscribing:
◦ What it means: Subscribing asks the server to send you track data; unsubscribing stops that request.
◦ How it works: It involves a handshake with the server, so there might be a little delay.
◦ When to use: When you don’t want to receive a track’s data anymore.
• Enabling/Disabling:
◦ What it means: This controls whether the track is active and receiving data on your side, without talking to the server.
◦ How it works: Disabling pauses data reception temporarily, enabling resumes it.
◦ When to use: Handy for saving bandwidth when you don’t need the track momentarily (like muting a video that’s offscreen).
Quick tip: To actually get data, a track needs to be both subscribed and enabled. If either one is off, you won’t receive data.
// Turn a track on or off
publication.setEnabled(false); // Turn off
publication.setEnabled(true); // Turn on
If anything’s unclear or you want some examples, just let me know—I’m here to help!
Sources: Subscribing to tracks | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 8:31 PMtall-airline-16756
08/01/2025, 8:33 PMautoSubscribe is turned off.
◦ For spatial applications where you need precise control over which tracks each participant gets.
◦ Whenever you’re making lasting changes to what you’re subscribed to.
2. Enabling/Disabling
• What it means: Enabling or disabling a track just controls whether the track is active and sending data, and it doesn’t require any server negotiation.
• Quick and light changes: Disabling a track pauses data temporarily, and enabling it resumes data flow.
• Instant effect: These changes happen quickly without the overhead of reconnecting.
• When to use it:
◦ To save bandwidth when content is offscreen.
◦ For temporary mutes, like muting a video track that’s not visible.
◦ When responding to UI changes like showing or hiding a track.
3. Key Differences
• Negotiation: Subscribing needs server negotiation; enabling/disabling doesn’t.
• State: To actually get data, a track must be both subscribed and enabled. If either is off, you won’t receive data.
• Performance: Enabling/disabling is great for quick, frequent changes, while subscribing/unsubscribing is better for longer-term adjustments.
4. Implementation Examples
// Controlling subscription - for permanent changes
publication.setSubscribed(true); // Connect and start receiving data
publication.setSubscribed(false); // Disconnect and stop receiving data
// Controlling enable/disable - for temporary changes
publication.setEnabled(false); // Pause data flow
publication.setEnabled(true); // Resume data flow
5. How to decide what to use
• Go with subscribing/unsubscribing when:
◦ You’re setting up the room initially.
◦ You want to make permanent changes to which tracks are available.
◦ You’re doing selective subscriptions based on your app’s needs.
• Choose enabling/disabling when:
◦ You want to save bandwidth for content that’s offscreen.
◦ You need a quick mute or unmute option.
◦ You’re toggling tracks on and off frequently.
The main thing to remember is that subscribing sets up the connection pipeline, while enabling controls whether data actually flows through it. Both work together to make sure you get the best performance and resource use in LiveKit.
Sources: Core API primitives | LiveKit Docs | livekit-android-sdk/src/main/java/io/livekit/android/room/track/RemoteTrack... | Subscribing to tracks | LiveKit Docsmicroscopic-hydrogen-58853
08/01/2025, 8:41 PMtall-airline-16756
08/01/2025, 8:41 PMmicroscopic-hydrogen-58853
08/02/2025, 7:59 AMtall-airline-16756
08/02/2025, 8:00 AMRemoteTrack or LocalTrack object.
2. Enable the Track: Just call the enable() method on that track to start receiving its data.
track.enable();
3. Disable the Track: If you want to pause receiving data without unsubscribing, call the disable() method.
track.disable();
Here’s a quick JavaScript example:
// Example: Disabling a track when it’s subscribed
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
// Disable the track
publication.setEnabled(false);
});
Just a few friendly notes:
• Enabling or disabling a track only affects your local experience—it controls what you receive.
• Disabling tracks, especially video, is a great way to save bandwidth when you’re not viewing them.
• These methods are super efficient since they don’t need to renegotiate with the server, so feel free to toggle them as often as you like. Sources: Subscribing to tracks | LiveKit Docsmicroscopic-hydrogen-58853
08/02/2025, 8:13 AMtall-airline-16756
08/02/2025, 8:13 AMLocalParticipant.setCameraEnabled(). `room.localParticipant.setCameraEnabled(true); // Turn the camera on
room.localParticipant.setCameraEnabled(false); // Turn the camera off`This will create and publish a camera video track if it hasn’t been done yet, and start the camera for you.
• Enable/Disable Microphone: Similarly, use LocalParticipant.setMicrophoneEnabled(). room.localParticipant.setMicrophoneEnabled(true); // Turn the mic on
room.localParticipant.setMicrophoneEnabled(false); // Turn the mic off
2. For Remote Tracks:
• Enable/Disable Remote Track: Use RemoteTrackPublication.setEnabled(). `publication.setEnabled(false); // Pause the track
publication.setEnabled(true); // Resume the track`This is super helpful if you want to save bandwidth when you don’t need that track.
Just a couple more things to keep in mind:
• You can always peek at the current state of a track by checking the enabled property in the Track class.
• These methods make managing your track states really smooth and don’t require any server back-and-forth.
Hope this helps! If you have any questions, just ask—I'm here to help. 😊 Sources: examples/virtual-background/src/main/java/io/livekit/android/selfie/MainAct...microscopic-hydrogen-58853
08/02/2025, 8:43 AMtall-airline-16756
08/02/2025, 8:44 AMprivate fun requestPermissions() {
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { grants ->
for (grant in grants.entries) {
if (!grant.value) {
Toast.makeText(
this,
"Missing permission: ${grant.key}",
Toast.LENGTH_SHORT
).show()
}
}
}
val neededPermissions = listOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
).filter {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_DENIED
}.toTypedArray()
if (neededPermissions.isNotEmpty()) {
requestPermissionLauncher.launch(neededPermissions)
}
}
A couple of quick notes:
• While LiveKit offers guidance and sample code, it’s up to you to implement the permission requests in your app.
• Make sure these permissions are granted before trying to use the camera or mic to avoid any hiccups during runtime.
If you want a hand with setting this up or have any other questions, just let me know—I’m here to help!
Sources: livekit-android-sdk/src/main/AndroidManifest.xmlmicroscopic-hydrogen-58853
08/02/2025, 9:27 AMtall-airline-16756
08/02/2025, 9:28 AMdisconnect twice in LiveKit won’t cause any errors. The method is smart enough to check its state, so if the room’s already disconnected, it’ll simply return without doing anything extra.
Here’s the scoop:
• State Management: Before disconnecting, the disconnect method checks if the room is already disconnected. If it is, it just skips the rest.
• Race Conditions: The SDK uses locks to keep things safe and avoid any tricky timing issues when disconnecting.
• Graceful Handling: So, if you call disconnect multiple times, it handles it smoothly without any hiccups or errors.
If anything’s still unclear or you want to dive deeper, just give me a shout—I’m here to help!microscopic-hydrogen-58853
08/02/2025, 11:35 AMtall-airline-16756
08/02/2025, 11:35 AMmicroscopic-hydrogen-58853
08/02/2025, 4:21 PMtall-airline-16756
08/02/2025, 4:23 PMonConnected isn’t being called in RoomScope, it might be due to timing or connection state hiccups. No worries though—here’s a friendly guide to help you troubleshoot and get things working smoothly:
1. Make Sure Your RoomScope Setup Is Just Right
RoomScope(
url = "your_url",
token = "your_token",
connect = true,
onConnected = { room ->
Log.d("LiveKit", "Connected to room: ${room.name}")
}
) { room ->
// Your UI content
}
2. Keep an Eye Out for Connection Failures
Adding some error handling can really help catch any connection issues:
RoomScope(
url = url,
token = token,
connect = true,
onConnected = { room ->
Log.d("LiveKit", "Successfully connected!")
},
onError = { room, error ->
Log.e("LiveKit", "Connection error: $error")
}
) { room ->
// Monitor connection state
HandleRoomState { room, state ->
Log.d("LiveKit", "Room state changed to: $state")
}
// Your UI content
}
3. Debug the Room State Changes
Watching all room state changes can help confirm that the room actually reaches the CONNECTED state:
HandleRoomState(
states = listOf(
Room.State.CONNECTING,
Room.State.CONNECTED,
Room.State.DISCONNECTED,
Room.State.RECONNECTING
)
) { room, state ->
Log.d("LiveKit", "Room state: $state")
when (state) {
Room.State.CONNECTING -> Log.d("LiveKit", "Connecting...")
Room.State.CONNECTED -> Log.d("LiveKit", "Connected!")
Room.State.DISCONNECTED -> Log.d("LiveKit", "Disconnected")
Room.State.RECONNECTING -> Log.d("LiveKit", "Reconnecting...")
}
}
4. Double-Check Your Connection Details
• Make sure your WebSocket URL and JWT token are spot on.
• Set connect = true so it auto-connects.
5. Tackle Recomposition Issues
If your onConnected callback comes from a composable that recomposes a lot, wrapping it in remember can keep things stable:
@Composable
fun MyRoomScreen() {
val onConnected = remember {
{ room: Room ->
Log.d("LiveKit", "Connected to room")
}
}
RoomScope(
url = url,
token = token,
connect = true,
onConnected = onConnected
) { room ->
// Content
}
}
6. Here’s a Friendly Example to Bring It All Together
@Composable
fun RoomScreen(url: String, token: String) {
RoomScope(
url = url,
token = token,
connect = true,
onConnected = { room ->
Log.d("LiveKit", "Successfully connected to room: ${room.name}")
},
onDisconnected = { room ->
Log.d("LiveKit", "Disconnected from room")
},
onError = { room, error ->
Log.e("LiveKit", "Room error: $error")
}
) { room ->
var isConnected by remember { mutableStateOf(false) }
HandleRoomState { room, state ->
isConnected = state == Room.State.CONNECTED
}
if (isConnected) {
Text("Connected to room!")
} else {
Text("Connecting...")
}
}
}
If you hit any snags or want to chat more about this, just let me know—I’m here to help!
Sources: livekit-compose-components/src/test/java/io/livekit/android/compose/flows/R... | livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt | sample-livestream/src/main/java/io/livekit/android/sample/livestream/room/s...microscopic-hydrogen-58853
08/02/2025, 8:20 PMtall-airline-16756
08/02/2025, 8:22 PMmicroscopic-hydrogen-58853
08/02/2025, 8:54 PMroom.localParticipant.attributes["host"] == "true"
this line of code doesn't seem to be workingmicroscopic-hydrogen-58853
08/03/2025, 8:56 AM