Skip to content

Commit

Permalink
Add support for participant attributes (#468)
Browse files Browse the repository at this point in the history
* Update protocol submodule

* Add support for participant attributes

* spotless
  • Loading branch information
davidliu authored Aug 1, 2024
1 parent 2ae93fc commit 93d0a4d
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ sealed class ParticipantEvent(open val participant: Participant) : Event() {
*/
class NameChanged(participant: Participant, val name: String?) : ParticipantEvent(participant)

/**
* When a participant's attributes are changed, fired for all participants
*/
class AttributesChanged(
participant: Participant,
/**
* The attributes that have changed and their new associated values.
*/
val changedAttributes: Map<String, String>,
/**
* The old attributes prior to change.
*/
val oldAttributes: Map<String, String>,
) : ParticipantEvent(participant)

/**
* Fired when the current participant's isSpeaking property changes. (including LocalParticipant)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,10 +511,11 @@ constructor(
sendRequest(request)
}

fun sendUpdateLocalMetadata(metadata: String?, name: String?) {
fun sendUpdateLocalMetadata(metadata: String?, name: String?, attributes: Map<String, String>? = emptyMap()) {
val update = LivekitRtc.UpdateParticipantMetadata.newBuilder()
.setMetadata(metadata ?: "")
.setName(name ?: "")
.putAllAttributes(attributes)

val request = LivekitRtc.SignalRequest.newBuilder()
.setUpdateMetadata(update)
Expand Down Expand Up @@ -765,6 +766,10 @@ constructor(
// TODO
}

LivekitRtc.SignalResponse.MessageCase.ERROR_RESPONSE -> {
// TODO
}

LivekitRtc.SignalResponse.MessageCase.MESSAGE_NOT_SET,
null,
-> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.livekit.android.room.track.RemoteTrackPublication
import io.livekit.android.room.track.Track
import io.livekit.android.room.track.TrackPublication
import io.livekit.android.util.FlowObservable
import io.livekit.android.util.diffMapChange
import io.livekit.android.util.flow
import io.livekit.android.util.flowDelegate
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -82,6 +83,8 @@ open class Participant(
private set

/**
* The participant's identity on the server. [name] should be preferred for UI usecases.
*
* Changes can be observed by using [io.livekit.android.util.flow]
*/
@FlowObservable
Expand All @@ -99,6 +102,9 @@ open class Participant(

/**
* Changes can be observed by using [io.livekit.android.util.flow]
*
* A [ParticipantEvent.SpeakingChanged] event is emitted from [events] whenever
* this changes.
*/
@FlowObservable
@get:FlowObservable
Expand All @@ -113,6 +119,14 @@ open class Participant(
}
@VisibleForTesting set

/**
* The participant's name. To be used for user-facing purposes (i.e. when displayed in the UI).
*
* Changes can be observed by using [io.livekit.android.util.flow]
*
* A [ParticipantEvent.NameChanged] event is emitted from [events] whenever
* this changes.
*/
@FlowObservable
@get:FlowObservable
var name by flowDelegate<String?>(null) { newValue, oldValue ->
Expand All @@ -123,7 +137,12 @@ open class Participant(
@VisibleForTesting set

/**
* The metadata for this participant.
*
* Changes can be observed by using [io.livekit.android.util.flow]
*
* A [ParticipantEvent.MetadataChanged] event is emitted from [events] whenever
* this changes.
*/
@FlowObservable
@get:FlowObservable
Expand All @@ -136,7 +155,33 @@ open class Participant(
@VisibleForTesting set

/**
* The attributes set on this participant.
*
* Changes can be observed by using [io.livekit.android.util.flow]
*
* A [ParticipantEvent.AttributesChanged] event is emitted from [events] whenever
* this changes.
*/
@FlowObservable
@get:FlowObservable
var attributes: Map<String, String> by flowDelegate(emptyMap()) { newAttributes, oldAttributes ->
if (newAttributes != oldAttributes) {
val diff = diffMapChange(newAttributes, oldAttributes, "")

if (diff.isNotEmpty()) {
eventBus.postEvent(ParticipantEvent.AttributesChanged(this, diff, oldAttributes), scope)
}
}
}
@VisibleForTesting set

/**
* The permissions for this participant.
*
* Changes can be observed by using [io.livekit.android.util.flow]
*
* A [ParticipantEvent.ParticipantPermissionsChanged] event is emitted from [events] whenever
* this changes.
*/
@FlowObservable
@get:FlowObservable
Expand Down Expand Up @@ -331,6 +376,7 @@ open class Participant(
if (info.hasPermission()) {
permissions = ParticipantPermission.fromProto(info.permission)
}
attributes = info.attributesMap
}

override fun equals(other: Any?): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.livekit.android.util

fun <K, V> diffMapChange(newMap: Map<K, V>, oldMap: Map<K, V>, defaultValue: V): MutableMap<K, V> {
val allKeys = newMap.keys + oldMap.keys
val diff = mutableMapOf<K, V>()

for (key in allKeys) {
if (newMap[key] != oldMap[key]) {
diff[key] = newMap[key] ?: defaultValue
}
}

return diff
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class ParticipantTest {
assertEquals(INFO.metadata, participant.metadata)
assertEquals(INFO.name, participant.name)
assertEquals(Participant.Kind.fromProto(INFO.kind), participant.kind)
assertEquals(INFO.attributesMap, participant.attributes)

assertEquals(INFO, participant.participantInfo)
}

Expand Down Expand Up @@ -108,6 +110,29 @@ class ParticipantTest {
assertEquals(participant, event.participant)
}

@Test
fun setAttributesChangedEvent() = runTest {
participant.attributes = INFO.attributesMap

val eventCollector = EventCollector(participant.events, coroutineRule.scope)
val oldAttributes = participant.attributes

val newAttributes = mapOf("newAttribute" to "newValue")
participant.attributes = newAttributes

val events = eventCollector.stopCollecting()

assertEquals(1, events.size)
assertEquals(true, events[0] is ParticipantEvent.AttributesChanged)

val event = events[0] as ParticipantEvent.AttributesChanged

val expectedDiff = mapOf("attribute" to "", "newAttribute" to "newValue")
assertEquals(expectedDiff, event.changedAttributes)
assertEquals(oldAttributes, event.oldAttributes)
assertEquals(participant, event.participant)
}

@Test
fun setIsSpeakingChangedEvent() = runTest {
val eventCollector = EventCollector(participant.events, coroutineRule.scope)
Expand Down Expand Up @@ -171,6 +196,7 @@ class ParticipantTest {
.setMetadata("metadata")
.setName("name")
.setKind(LivekitModels.ParticipantInfo.Kind.STANDARD)
.putAttributes("attribute", "value")
.build()

val TRACK_INFO = LivekitModels.TrackInfo.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.livekit.android.util

import org.junit.Assert.assertEquals
import org.junit.Test

class MapDiffUtilTest {

@Test
fun newMapChangesValues() {
val oldMap = mapOf("a" to "1", "b" to "2", "c" to "3")
val newMap = mapOf("a" to "1", "b" to "0", "c" to "3")

val diff = diffMapChange(newMap, oldMap, "").entries
assertEquals(1, diff.size)
val entry = diff.first()
assertEquals("b", entry.key)
assertEquals("0", entry.value)
}

@Test
fun newMapAddsValues() {
val oldMap = mapOf("a" to "1", "b" to "2", "c" to "3")
val newMap = mapOf("a" to "1", "b" to "2", "c" to "3", "d" to "4")

val diff = diffMapChange(newMap, oldMap, "").entries
assertEquals(1, diff.size)
val entry = diff.first()
assertEquals("d", entry.key)
assertEquals("4", entry.value)
}

@Test
fun newMapDeletesValues() {
val oldMap = mapOf("a" to "1", "b" to "2", "c" to "3")
val newMap = mapOf("a" to "1", "b" to "2")

val diff = diffMapChange(newMap, oldMap, "").entries
assertEquals(1, diff.size)
val entry = diff.first()
assertEquals("c", entry.key)
assertEquals("", entry.value)
}
}
2 changes: 1 addition & 1 deletion protocol
Submodule protocol updated 72 files
+28 −0 CHANGELOG.md
+18 −0 auth/accesstoken.go
+5 −1 auth/accesstoken_test.go
+24 −0 auth/grants.go
+4 −1 auth/verifier_test.go
+13 −6 go.mod
+36 −15 go.sum
+7 −7 infra/link.pb.go
+19 −0 livekit/attrs.go
+897 −356 livekit/livekit_agent.pb.go
+200 −0 livekit/livekit_agent_grpc.pb.go
+293 −299 livekit/livekit_analytics.pb.go
+411 −401 livekit/livekit_egress.pb.go
+188 −186 livekit/livekit_egress.twirp.go
+20 −20 livekit/livekit_ingress.pb.go
+291 −275 livekit/livekit_internal.pb.go
+598 −571 livekit/livekit_models.pb.go
+566 −294 livekit/livekit_room.pb.go
+80 −69 livekit/livekit_room.twirp.go
+756 −560 livekit/livekit_rtc.pb.go
+1,193 −352 livekit/livekit_sip.pb.go
+1,291 −147 livekit/livekit_sip.twirp.go
+3 −3 livekit/livekit_webhook.pb.go
+96 −0 livekit/sip.go
+37 −0 livekit/sip_test.go
+22 −6 livekit/types.go
+2 −0 livekit/types_test
+66 −0 livekit/types_test.go
+7 −6 magefile.go
+1 −1 package.json
+20 −0 packages/javascript/CHANGELOG.md
+1 −1 packages/javascript/package.json
+826 −1,697 pnpm-lock.yaml
+131 −87 protobufs/livekit_agent.proto
+7 −8 protobufs/livekit_analytics.proto
+1 −0 protobufs/livekit_egress.proto
+2 −0 protobufs/livekit_internal.proto
+6 −3 protobufs/livekit_models.proto
+35 −1 protobufs/livekit_room.proto
+23 −2 protobufs/livekit_rtc.proto
+111 −9 protobufs/livekit_sip.proto
+6 −5 protobufs/rpc/agent.proto
+27 −0 protobufs/rpc/analytics.proto
+22 −1 protobufs/rpc/ingress.proto
+8 −2 protobufs/rpc/io.proto
+2 −1 protobufs/rpc/sip.proto
+43 −36 rpc/agent.pb.go
+24 −24 rpc/agent.psrpc.go
+109 −0 rpc/analytics.pb.go
+19 −18 rpc/analytics_grpc.pb.go
+6 −6 rpc/egress.pb.go
+279 −116 rpc/ingress.pb.go
+68 −41 rpc/ingress.psrpc.go
+176 −131 rpc/io.pb.go
+75 −70 rpc/io.psrpc.go
+3 −3 rpc/keepalive.pb.go
+2 −2 rpc/participant.pb.go
+2 −2 rpc/room.pb.go
+4 −4 rpc/signal.pb.go
+45 −8 rpc/sip.go
+73 −50 rpc/sip.pb.go
+36 −32 rpc/sip.psrpc.go
+77 −0 rpc/sip_test.go
+129 −76 sip/sip.go
+145 −21 sip/sip_test.go
+12 −5 sip/token.go
+14 −0 utils/configobserver.go
+14 −0 utils/configobserver_test.go
+178 −0 utils/guid/id.go
+8 −8 utils/guid/id_test.go
+23 −137 utils/id.go
+5 −5 xtls/tls.go

0 comments on commit 93d0a4d

Please sign in to comment.