Skip to content

Commit

Permalink
Refactor user fields in ParticipantInfo + Add custom data docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielNovak committed Sep 25, 2023
1 parent 05f1ea2 commit b26b410
Show file tree
Hide file tree
Showing 30 changed files with 171 additions and 130 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,12 @@ Video roadmap and changelog is available [here](https://github.com/GetStream/pro
- [X] Implement Chat overlay for Dogfooding (Jaewoong)
- [X] Migrate Stream Chat SDK v6 stable (Jaewoong)
- [X] Add Dogfooding instructions + directs Google Play (Jaewoong)
- [X] Support participant.custom field which was previously ignored. ParticipantState line 216 (Daniel)
- [ ] Default livestream player UI + docs (Jaewoong/ Daniel)
- [ ] Reaction dialog API for Compose (Jaewoong)
- [ ] Android SDK development.md cleanup (Daniel)
- [ ] Upgrade to more recent versions of webrtc (Kanat)
- [ ] Review foreground service vs backend for audio rooms etc. (Daniel)
- [ ] Support participant.custom field which was previously ignored. ParticipantState line 216 (Daniel)
- [ ] Logging is too verbose (rtc is very noisy), clean it up to focus on the essential for info and higher (Daniel)

### 0.5.0 milestone
Expand Down
31 changes: 31 additions & 0 deletions docusaurus/docs/Android/06-advanced/11-custom-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Custom Data
description: How can you use custom data in your applications?
---

Custom data is additional information that can be added to the default data of Stream. It is a dictionary of key-value pairs that can be attached to users, events, and pretty much almost every domain model in the Stream SDK.

On Android, custom data is represented as `Map<String, Any?>` where the `Any` value can be a String, Integer, Double, Long, Boolean, List, Map or null. The values inside the lists and maps can also only contain the listed types. Custom classes are not supported and we recommend to convert your custom object to a JSON String.

## Adding Custom Data

Adding extra data can be done through the Server-Side SDKs or through the Client SDKs. In the Android Stream Video SDK, you can add extra data when creating/updating a user, event, reaction and other models.
As a simple example, let's see how you can add a new custom field to a Video Reaction.

```kotlin
call.sendReaction(
type = "default",
emoji = ":fireworks:",
custom = mapOf(Pair("yourCustomKey", "customStringValue"))
)
```

## Reading Custom Data

All of the most important domain models in the SDK have an `customData` property that you can read the additional information added by your app.

The following code snippet shows how to get an email from a user's custom data.

```Kotlin
val email = user.custom["email"]
```
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,11 @@ private fun CallJoinHeader(
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically,
) {
val name =
user?.name?.ifBlank { user?.id }?.ifBlank { user!!.custom["email"] }.orEmpty()

if (user != null) {
user?.let {
UserAvatar(
modifier = Modifier.size(24.dp),
user = user!!,
userName = it.userNameOrId,
userImage = it.image,
)

Spacer(modifier = Modifier.width(8.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ private fun CallLobbyHeader(
if (userValue != null) {
UserAvatar(
modifier = Modifier.size(32.dp),
user = userValue,
userName = userValue.userNameOrId,
userImage = userValue.image,
)

Spacer(modifier = Modifier.width(4.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,11 @@ public final class io/getstream/video/android/compose/ui/components/avatar/Onlin
}

public final class io/getstream/video/android/compose/ui/components/avatar/UserAvatarBackgroundKt {
public static final fun UserAvatarBackground-1fnCaGY (Lio/getstream/video/android/model/User;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;FFLandroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/String;JJIILjava/lang/Integer;Landroidx/compose/runtime/Composer;III)V
public static final fun UserAvatarBackground-GIi8pss (Ljava/lang/String;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;FFLandroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/String;JJIILjava/lang/Integer;Landroidx/compose/runtime/Composer;III)V
}

public final class io/getstream/video/android/compose/ui/components/avatar/UserAvatarKt {
public static final fun UserAvatar-ts-XYlQ (Lio/getstream/video/android/model/User;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/String;JILjava/lang/Integer;JZLio/getstream/video/android/compose/ui/components/avatar/OnlineIndicatorAlignment;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;III)V
public static final fun UserAvatar-8W8krDU (Ljava/lang/String;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/String;JILjava/lang/Integer;JZLio/getstream/video/android/compose/ui/components/avatar/OnlineIndicatorAlignment;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;III)V
}

public final class io/getstream/video/android/compose/ui/components/background/CallBackgroundKt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ public fun ParticipantAudio(
},
roleBadgeContent: @Composable RowScope.(ParticipantState) -> Unit = {},
) {
val user by participant.user.collectAsStateWithLifecycle()
val nameOrId by participant.userNameOrId.collectAsStateWithLifecycle()
val userImage by participant.image.collectAsStateWithLifecycle()
val isSpeaking by participant.speaking.collectAsStateWithLifecycle()
val audioEnabled by participant.audioEnabled.collectAsStateWithLifecycle()

Expand All @@ -86,7 +86,8 @@ public fun ParticipantAudio(
) {
Box(modifier = Modifier.size(VideoTheme.dimens.audioAvatarSize)) {
UserAvatar(
user = user,
userName = nameOrId,
userImage = userImage,
modifier = Modifier
.fillMaxSize()
.padding(VideoTheme.dimens.audioAvatarPadding),
Expand Down Expand Up @@ -130,9 +131,11 @@ public fun ParticipantAudio(
roleBadgeContent.invoke(this, participant)
}

val roles by participant.roles.collectAsStateWithLifecycle()

Text(
modifier = Modifier.fillMaxWidth(),
text = user.role,
text = roles.firstOrNull().orEmpty(),
fontSize = 11.sp,
color = VideoTheme.colors.textHighEmphasis,
textAlign = TextAlign.Center,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.ContentScale
Expand All @@ -31,6 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.getstream.video.android.compose.R
import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.mock.StreamMockUtils
Expand Down Expand Up @@ -59,7 +61,8 @@ import io.getstream.video.android.model.User
*/
@Composable
public fun UserAvatar(
user: User,
userName: String?,
userImage: String?,
modifier: Modifier = Modifier,
shape: Shape = VideoTheme.shapes.avatar,
textStyle: TextStyle = VideoTheme.typography.title3Bold,
Expand All @@ -79,8 +82,8 @@ public fun UserAvatar(
Box(modifier = modifier) {
Avatar(
modifier = Modifier.fillMaxSize(),
imageUrl = user.image,
initials = user.name.ifBlank { user.id },
imageUrl = userImage,
initials = userName,
textStyle = textStyle,
shape = shape,
contentScale = contentScale,
Expand Down Expand Up @@ -111,8 +114,14 @@ internal fun BoxScope.DefaultOnlineIndicator(onlineIndicatorAlignment: OnlineInd
private fun UserAvatarPreview() {
StreamMockUtils.initializeStreamVideo(LocalContext.current)
VideoTheme {
val participant = mockParticipantList[0]
val userId by participant.userId.collectAsStateWithLifecycle()
val userImage by participant.image.collectAsStateWithLifecycle()
val userName by participant.userNameOrId.collectAsStateWithLifecycle()

UserAvatar(
user = mockParticipantList[0].initialUser,
userImage = userImage,
userName = userName,
modifier = Modifier.size(82.dp),
isShowingOnlineIndicator = true,
previewPlaceholder = io.getstream.video.android.ui.common.R.drawable.stream_video_call_sample,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.background.ParticipantImageBackground
import io.getstream.video.android.mock.StreamMockUtils
import io.getstream.video.android.mock.mockUsers
import io.getstream.video.android.model.User

/**
* A background that displays a user avatar and a background that reflects the avatar.
Expand All @@ -59,7 +58,8 @@ import io.getstream.video.android.model.User
*/
@Composable
public fun UserAvatarBackground(
user: User,
userName: String?,
userImage: String?,
modifier: Modifier = Modifier,
shape: Shape = VideoTheme.shapes.avatar,
avatarSize: Dp = 72.dp,
Expand All @@ -76,7 +76,7 @@ public fun UserAvatarBackground(
Box(modifier = modifier) {
ParticipantImageBackground(
modifier = Modifier.fillMaxSize(),
userImage = user.image,
userImage = userImage,
blurRadius = blurRadius,
)

Expand All @@ -85,7 +85,8 @@ public fun UserAvatarBackground(
.size(avatarSize)
.align(Alignment.Center)
.shadow(elevation = avatarShadowElevation, shape = CircleShape),
user = user,
userName = userName,
userImage = userImage,
shape = shape,
textStyle = textStyle,
contentScale = contentScale,
Expand All @@ -103,8 +104,10 @@ public fun UserAvatarBackground(
private fun UserAvatarBackgroundPreview() {
StreamMockUtils.initializeStreamVideo(LocalContext.current)
VideoTheme {
val user = mockUsers[0]
UserAvatarBackground(
user = mockUsers[0],
userName = user.name.ifBlank { user.id },
userImage = user.image,
modifier = Modifier.fillMaxSize(),
previewPlaceholder = io.getstream.video.android.ui.common.R.drawable.stream_video_call_sample,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -42,7 +41,6 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.getstream.video.android.compose.lifecycle.MediaPiPLifecycle
import io.getstream.video.android.compose.permission.VideoPermissionsState
import io.getstream.video.android.compose.permission.rememberCallPermissionsState
Expand Down Expand Up @@ -132,8 +130,6 @@ public fun CallLobby(
)
},
) {
val participant = remember(user) { ParticipantState(initialUser = user, call = call) }

DefaultPermissionHandler(videoPermission = permissions)

MediaPiPLifecycle(call = call)
Expand All @@ -153,11 +149,10 @@ public fun CallLobby(
onDisabledContent.invoke()
}

val userNameOrId by participant.userNameOrId.collectAsStateWithLifecycle()
val nameLabel = if (participant.isLocal) {
val nameLabel = if (user.id == StreamVideo.instance().user.id) {
stringResource(id = R.string.stream_video_myself)
} else {
userNameOrId
user.userNameOrId
}

ParticipantLabel(
Expand Down Expand Up @@ -220,7 +215,8 @@ private fun OnDisabledContent(user: User) {
modifier = Modifier
.size(VideoTheme.dimens.callAvatarSize)
.align(Alignment.Center),
user = user,
userImage = user.image,
userName = user.name.ifBlank { user.id },
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ public fun ParticipantVideo(
)
},
videoFallbackContent: @Composable (Call) -> Unit = {
val user by participant.user.collectAsStateWithLifecycle()
UserAvatarBackground(user = user)
val userName by participant.userNameOrId.collectAsStateWithLifecycle()
val userImage by participant.image.collectAsStateWithLifecycle()
UserAvatarBackground(userName = userName, userImage = userImage)
},
reactionContent: @Composable BoxScope.(ParticipantState) -> Unit = {
DefaultReaction(
Expand Down Expand Up @@ -184,8 +185,9 @@ public fun ParticipantVideoRenderer(
call: Call,
participant: ParticipantState,
videoFallbackContent: @Composable (Call) -> Unit = {
val user by participant.user.collectAsStateWithLifecycle()
UserAvatarBackground(user = user)
val userName by participant.userNameOrId.collectAsStateWithLifecycle()
val userImage by participant.image.collectAsStateWithLifecycle()
UserAvatarBackground(userName = userName, userImage = userImage)
},
) {
if (LocalInspectionMode.current) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.participants.internal.CallParticipantsList
import io.getstream.video.android.compose.ui.components.participants.internal.InviteUserList
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.ParticipantState
import io.getstream.video.android.core.call.state.CallAction
import io.getstream.video.android.core.call.state.InviteUsersToCall
import io.getstream.video.android.core.call.state.ToggleMicrophone
import io.getstream.video.android.mock.StreamMockUtils
import io.getstream.video.android.mock.mockCall
import io.getstream.video.android.model.User

/**
* Represents a menu that shows information on the current call participants, while allowing the user
Expand All @@ -65,7 +65,7 @@ public fun CallParticipantsInfoMenu(
val audioEnabled = me?.audioEnabled?.collectAsStateWithLifecycle()

var infoStateMode by remember { mutableStateOf<CallParticipantInfoMode>(ParticipantListMode) }
var selectedUsers: List<User> = remember { mutableStateListOf() }
var selectedUsers: List<ParticipantState> = remember { mutableStateListOf() }

val onBackPressed: () -> Unit = {
when (infoStateMode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,15 @@ private fun CallParticipantInfoItem(
) {
Spacer(modifier = Modifier.width(8.dp))

val user by participant.user.collectAsStateWithLifecycle()
val userName by participant.userNameOrId.collectAsStateWithLifecycle()
val userImage by participant.image.collectAsStateWithLifecycle()
UserAvatar(
modifier = Modifier.size(VideoTheme.dimens.participantsInfoAvatarSize),
user = user,
userName = userName,
userImage = userImage,
isShowingOnlineIndicator = true,
)

val userName by participant.userNameOrId.collectAsStateWithLifecycle()
Text(
modifier = Modifier
.padding(start = 8.dp)
Expand Down
Loading

0 comments on commit b26b410

Please sign in to comment.