From ec7b55be9f59cc8d96d04647d97f2906402d23c2 Mon Sep 17 00:00:00 2001 From: Aleksandar Apostolov Date: Wed, 25 Oct 2023 13:20:41 +0200 Subject: [PATCH] Participant view redesign (#887) Redesign the participants grid view --- .../api/stream-video-android-compose.api | 55 +++- .../android/compose/theme/StreamColors.kt | 64 +++- .../android/compose/theme/StreamDimens.kt | 64 ++-- .../android/compose/theme/StreamShapes.kt | 11 +- .../components/avatar/UserAvatarBackground.kt | 51 ++-- .../call/renderer/ParticipantVideo.kt | 62 ++-- .../call/renderer/VideoRendererStyle.kt | 2 +- .../LandscapeScreenSharingVideoRenderer.kt | 25 +- .../internal/LandscapeVideoRenderer.kt | 252 +++++----------- .../renderer/internal/LazyRowVideoRenderer.kt | 4 +- .../PortraitScreenSharingVideoRenderer.kt | 89 ++++-- .../internal/PortraitVideoRenderer.kt | 283 +++++------------- .../renderer/internal/ScreenShareTooltip.kt | 5 +- .../connection/NetworkQualityIndicator.kt | 83 +---- .../connection/internal/ConnectionBars.kt | 97 ++++++ .../indicator/AudioVolumeIndicator.kt | 23 +- .../components/indicator/GenericIndicator.kt | 96 ++++++ .../indicator/MicrophoneIndicator.kt | 40 +-- .../ui/components/indicator/SoundIndicator.kt | 44 +-- .../src/main/res/values/colors.xml | 19 +- .../src/main/res/values/dimens.xml | 35 ++- 21 files changed, 728 insertions(+), 676 deletions(-) create mode 100644 stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/internal/ConnectionBars.kt create mode 100644 stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/GenericIndicator.kt diff --git a/stream-video-android-compose/api/stream-video-android-compose.api b/stream-video-android-compose/api/stream-video-android-compose.api index 51493fca01..4d8cc319e0 100644 --- a/stream-video-android-compose/api/stream-video-android-compose.api +++ b/stream-video-android-compose/api/stream-video-android-compose.api @@ -58,7 +58,7 @@ public abstract interface class io/getstream/video/android/compose/state/ui/part public final class io/getstream/video/android/compose/theme/StreamColors { public static final field $stable I public static final field Companion Lio/getstream/video/android/compose/theme/StreamColors$Companion; - public synthetic fun (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-0d7_KjU ()J public final fun component10-0d7_KjU ()J public final fun component11-0d7_KjU ()J @@ -90,19 +90,25 @@ public final class io/getstream/video/android/compose/theme/StreamColors { public final fun component35-0d7_KjU ()J public final fun component36-0d7_KjU ()J public final fun component37-0d7_KjU ()J + public final fun component38-0d7_KjU ()J + public final fun component39-0d7_KjU ()J public final fun component4-0d7_KjU ()J + public final fun component40-0d7_KjU ()J + public final fun component41-0d7_KjU ()J public final fun component5-0d7_KjU ()J public final fun component6-0d7_KjU ()J public final fun component7-0d7_KjU ()J public final fun component8-0d7_KjU ()J public final fun component9-0d7_KjU ()J - public final fun copy-siJM_ko (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)Lio/getstream/video/android/compose/theme/StreamColors; - public static synthetic fun copy-siJM_ko$default (Lio/getstream/video/android/compose/theme/StreamColors;JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamColors; + public final fun copy-ilA1nao (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)Lio/getstream/video/android/compose/theme/StreamColors; + public static synthetic fun copy-ilA1nao$default (Lio/getstream/video/android/compose/theme/StreamColors;JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamColors; public fun equals (Ljava/lang/Object;)Z public final fun getActivatedVolumeIndicator-0d7_KjU ()J public final fun getAppBackground-0d7_KjU ()J public final fun getAudioActionColor-0d7_KjU ()J + public final fun getAudioIndicatorBackground-0d7_KjU ()J public final fun getAudioLeaveButton-0d7_KjU ()J + public final fun getAvatarBorderColor-0d7_KjU ()J public final fun getAvatarInitials-0d7_KjU ()J public final fun getBarsBackground-0d7_KjU ()J public final fun getBorders-0d7_KjU ()J @@ -118,6 +124,7 @@ public final class io/getstream/video/android/compose/theme/StreamColors { public final fun getConnectionQualityBackground-0d7_KjU ()J public final fun getConnectionQualityBar-0d7_KjU ()J public final fun getConnectionQualityBarFilled-0d7_KjU ()J + public final fun getConnectionQualityBarFilledPoor-0d7_KjU ()J public final fun getDeActivatedVolumeIndicator-0d7_KjU ()J public final fun getDisabled-0d7_KjU ()J public final fun getErrorAccent-0d7_KjU ()J @@ -129,6 +136,7 @@ public final class io/getstream/video/android/compose/theme/StreamColors { public final fun getLiveIndicator-0d7_KjU ()J public final fun getOverlay-0d7_KjU ()J public final fun getOverlayDark-0d7_KjU ()J + public final fun getParticipantContainerBackground-0d7_KjU ()J public final fun getParticipantLabelBackground-0d7_KjU ()J public final fun getPrimaryAccent-0d7_KjU ()J public final fun getScreenSharingBackground-0d7_KjU ()J @@ -148,7 +156,7 @@ public final class io/getstream/video/android/compose/theme/StreamColors$Compani public final class io/getstream/video/android/compose/theme/StreamDimens { public static final field $stable I public static final field Companion Lio/getstream/video/android/compose/theme/StreamDimens$Companion; - public synthetic fun (FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-D9Ej5fM ()F public final fun component10-D9Ej5fM ()F public final fun component11-D9Ej5fM ()F @@ -230,9 +238,13 @@ public final class io/getstream/video/android/compose/theme/StreamDimens { public final fun component80-D9Ej5fM ()F public final fun component81-D9Ej5fM ()F public final fun component82-D9Ej5fM ()F + public final fun component83-D9Ej5fM ()F + public final fun component84-D9Ej5fM ()F + public final fun component85-D9Ej5fM ()F + public final fun component86-D9Ej5fM ()F public final fun component9-D9Ej5fM ()F - public final fun copy-uuJv5Cc (FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)Lio/getstream/video/android/compose/theme/StreamDimens; - public static synthetic fun copy-uuJv5Cc$default (Lio/getstream/video/android/compose/theme/StreamDimens;FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIIILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamDimens; + public final fun copy-lgJDAHA (FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)Lio/getstream/video/android/compose/theme/StreamDimens; + public static synthetic fun copy-lgJDAHA$default (Lio/getstream/video/android/compose/theme/StreamDimens;FFFFFFFFFFFJJJJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFIIILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamDimens; public fun equals (Ljava/lang/Object;)Z public final fun getAudioAvatarPadding-D9Ej5fM ()F public final fun getAudioAvatarSize-D9Ej5fM ()F @@ -246,6 +258,8 @@ public final class io/getstream/video/android/compose/theme/StreamDimens { public final fun getAudioRoomMicPadding-D9Ej5fM ()F public final fun getAudioRoomMicSize-D9Ej5fM ()F public final fun getAvatarAppbarPadding-D9Ej5fM ()F + public final fun getAvatarBorderPadding-D9Ej5fM ()F + public final fun getAvatarBorderWidth-D9Ej5fM ()F public final fun getButtonToggleOffAlpha ()F public final fun getButtonToggleOnAlpha ()F public final fun getCallAppBarCenterContentSpacingEnd-D9Ej5fM ()F @@ -273,6 +287,7 @@ public final class io/getstream/video/android/compose/theme/StreamDimens { public final fun getGroupCallUserNameTextSize-XSAIIZE ()J public final fun getHeaderElevation-D9Ej5fM ()F public final fun getIncomingCallOptionsBottomPadding-D9Ej5fM ()F + public final fun getIndicatorBackgroundSize-D9Ej5fM ()F public final fun getLandscapeControlActionsButtonSize-D9Ej5fM ()F public final fun getLandscapeControlActionsWidth-D9Ej5fM ()F public final fun getLandscapeTopAppBarHeight-D9Ej5fM ()F @@ -286,6 +301,7 @@ public final class io/getstream/video/android/compose/theme/StreamDimens { public final fun getOnCallStatusTextAlpha ()F public final fun getOnCallStatusTextSize-XSAIIZE ()J public final fun getOutgoingCallOptionsBottomPadding-D9Ej5fM ()F + public final fun getParticipantContentRadius-D9Ej5fM ()F public final fun getParticipantFocusedBorderWidth-D9Ej5fM ()F public final fun getParticipantInfoMenuAppBarHeight-D9Ej5fM ()F public final fun getParticipantInfoMenuOptionsHeight-D9Ej5fM ()F @@ -327,10 +343,13 @@ public final class io/getstream/video/android/compose/theme/StreamDimens$Compani public final class io/getstream/video/android/compose/theme/StreamShapes { public static final field $stable I public static final field Companion Lio/getstream/video/android/compose/theme/StreamShapes$Companion; - public fun (Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;)V + public fun (Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;)V public final fun component1 ()Landroidx/compose/ui/graphics/Shape; public final fun component10 ()Landroidx/compose/ui/graphics/Shape; public final fun component11 ()Landroidx/compose/ui/graphics/Shape; + public final fun component12 ()Landroidx/compose/ui/graphics/Shape; + public final fun component13 ()Landroidx/compose/ui/graphics/Shape; + public final fun component14 ()Landroidx/compose/ui/graphics/Shape; public final fun component2 ()Landroidx/compose/ui/graphics/Shape; public final fun component3 ()Landroidx/compose/ui/graphics/Shape; public final fun component4 ()Landroidx/compose/ui/graphics/Shape; @@ -339,8 +358,8 @@ public final class io/getstream/video/android/compose/theme/StreamShapes { public final fun component7 ()Landroidx/compose/ui/graphics/Shape; public final fun component8 ()Landroidx/compose/ui/graphics/Shape; public final fun component9 ()Landroidx/compose/ui/graphics/Shape; - public final fun copy (Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;)Lio/getstream/video/android/compose/theme/StreamShapes; - public static synthetic fun copy$default (Lio/getstream/video/android/compose/theme/StreamShapes;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;ILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamShapes; + public final fun copy (Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;)Lio/getstream/video/android/compose/theme/StreamShapes; + public static synthetic fun copy$default (Lio/getstream/video/android/compose/theme/StreamShapes;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/graphics/Shape;ILjava/lang/Object;)Lio/getstream/video/android/compose/theme/StreamShapes; public fun equals (Ljava/lang/Object;)Z public final fun getAvatar ()Landroidx/compose/ui/graphics/Shape; public final fun getCallButton ()Landroidx/compose/ui/graphics/Shape; @@ -351,6 +370,9 @@ public final class io/getstream/video/android/compose/theme/StreamShapes { public final fun getConnectionQualityIndicator ()Landroidx/compose/ui/graphics/Shape; public final fun getDialog ()Landroidx/compose/ui/graphics/Shape; public final fun getFloatingParticipant ()Landroidx/compose/ui/graphics/Shape; + public final fun getIndicatorBackground ()Landroidx/compose/ui/graphics/Shape; + public final fun getParticipantContainerShape ()Landroidx/compose/ui/graphics/Shape; + public final fun getParticipantLabelShape ()Landroidx/compose/ui/graphics/Shape; public final fun getParticipantsInfoMenuButton ()Landroidx/compose/ui/graphics/Shape; public final fun getSoundIndicatorBar ()Landroidx/compose/ui/graphics/Shape; public fun hashCode ()I @@ -568,7 +590,7 @@ 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-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 static final fun UserAvatarBackground-5WCoS_E (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;JJILjava/lang/Integer;Landroidx/compose/runtime/Composer;III)V } public final class io/getstream/video/android/compose/ui/components/avatar/UserAvatarKt { @@ -1138,6 +1160,19 @@ public final class io/getstream/video/android/compose/ui/components/indicator/Co public final fun getLambda-1$stream_video_android_compose_release ()Lkotlin/jvm/functions/Function2; } +public final class io/getstream/video/android/compose/ui/components/indicator/ComposableSingletons$GenericIndicatorKt { + public static final field INSTANCE Lio/getstream/video/android/compose/ui/components/indicator/ComposableSingletons$GenericIndicatorKt; + public static field lambda-1 Lkotlin/jvm/functions/Function3; + public static field lambda-2 Lkotlin/jvm/functions/Function3; + public static field lambda-3 Lkotlin/jvm/functions/Function3; + public static field lambda-4 Lkotlin/jvm/functions/Function2; + public fun ()V + public final fun getLambda-1$stream_video_android_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-2$stream_video_android_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-3$stream_video_android_compose_release ()Lkotlin/jvm/functions/Function3; + public final fun getLambda-4$stream_video_android_compose_release ()Lkotlin/jvm/functions/Function2; +} + public final class io/getstream/video/android/compose/ui/components/indicator/ComposableSingletons$MicrophoneIndicatorKt { public static final field INSTANCE Lio/getstream/video/android/compose/ui/components/indicator/ComposableSingletons$MicrophoneIndicatorKt; public static field lambda-1 Lkotlin/jvm/functions/Function2; diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamColors.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamColors.kt index 5f5790c8c0..fb972a8963 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamColors.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamColors.kt @@ -65,6 +65,7 @@ public data class StreamColors( public val connectionQualityBackground: Color, public val connectionQualityBar: Color, public val connectionQualityBarFilled: Color, + public val connectionQualityBarFilledPoor: Color, public val participantLabelBackground: Color, public val infoMenuOverlayColor: Color, public val callFocusedBorder: Color, @@ -79,6 +80,9 @@ public data class StreamColors( public val audioLeaveButton: Color, public val audioActionColor: Color, public val liveIndicator: Color, + public val audioIndicatorBackground: Color, + public val avatarBorderColor: Color, + public val participantContainerBackground: Color, ) { public companion object { @@ -103,14 +107,16 @@ public data class StreamColors( errorAccent = colorResource(R.color.stream_video_error_accent), infoAccent = colorResource(R.color.stream_video_info_accent), highlight = colorResource(R.color.stream_video_highlight), - avatarInitials = colorResource(id = R.color.stream_video_text_avatar_initials), - screenSharingBackground = colorResource(R.color.stream_video_app_background), + screenSharingBackground = colorResource( + R.color.stream_video_participant_container_background, + ), screenSharingTooltipBackground = colorResource( R.color.stream_video_screen_sharing_tooltip_background, ), screenSharingTooltipContent = colorResource( id = R.color.stream_video_screen_sharing_tooltip_content, ), + avatarInitials = colorResource(id = R.color.stream_video_text_avatar_initials), activatedVolumeIndicator = colorResource(id = R.color.stream_video_primary_accent), deActivatedVolumeIndicator = colorResource( id = R.color.stream_video_deactivated_volume_indicator, @@ -118,9 +124,14 @@ public data class StreamColors( connectionQualityBackground = colorResource( id = R.color.stream_video_connection_quality_background, ), - connectionQualityBarFilled = colorResource(id = R.color.stream_video_primary_accent), connectionQualityBar = colorResource( - id = R.color.stream_video_connection_quality_bar_background, + id = R.color.stream_video_connection_indicator_good, + ), + connectionQualityBarFilled = colorResource( + id = R.color.stream_video_connection_indicator_great, + ), + connectionQualityBarFilledPoor = colorResource( + id = R.color.stream_video_connection_indicator_poor, ), participantLabelBackground = colorResource( id = R.color.stream_video_participant_label_background, @@ -130,18 +141,25 @@ public data class StreamColors( callGradientStart = colorResource(id = R.color.stream_video_call_gradient_start), callGradientEnd = colorResource(id = R.color.stream_video_call_gradient_end), callDescription = colorResource(id = R.color.stream_video_call_description), - callActionIconEnabled = colorResource(id = R.color.stream_video_action_icon_enabled), - callActionIconDisabled = colorResource(id = R.color.stream_video_action_icon_disabled), callActionIconEnabledBackground = colorResource( id = R.color.stream_video_action_icon_enabled_background, ), callActionIconDisabledBackground = colorResource( id = R.color.stream_video_action_icon_disabled_background, ), + callActionIconEnabled = colorResource(id = R.color.stream_video_action_icon_enabled), + callActionIconDisabled = colorResource(id = R.color.stream_video_action_icon_disabled), callLobbyBackground = colorResource(id = R.color.stream_video_lobby_background), audioLeaveButton = colorResource(id = R.color.stream_video_audio_leave), audioActionColor = colorResource(id = R.color.stream_video_audio_room_actions), liveIndicator = colorResource(id = R.color.stream_video_live_indicator), + audioIndicatorBackground = colorResource( + id = R.color.stream_video_volume_indicator_background, + ), + avatarBorderColor = colorResource(id = R.color.stream_video_avatar_border_color), + participantContainerBackground = colorResource( + id = R.color.stream_video_participant_container_background, + ), ) /** @@ -165,7 +183,9 @@ public data class StreamColors( errorAccent = colorResource(R.color.stream_video_error_accent_dark), infoAccent = colorResource(R.color.stream_video_info_accent_dark), highlight = colorResource(R.color.stream_video_highlight_dark), - screenSharingBackground = colorResource(R.color.stream_video_app_background_dark), + screenSharingBackground = colorResource( + R.color.stream_video_participant_container_background, + ), screenSharingTooltipBackground = colorResource( R.color.stream_video_screen_sharing_tooltip_background, ), @@ -180,34 +200,46 @@ public data class StreamColors( connectionQualityBackground = colorResource( id = R.color.stream_video_connection_quality_background, ), - connectionQualityBarFilled = colorResource(id = R.color.stream_video_primary_accent), connectionQualityBar = colorResource( - id = R.color.stream_video_connection_quality_bar_background, + id = R.color.stream_video_connection_indicator_good, + ), + connectionQualityBarFilled = colorResource( + id = R.color.stream_video_connection_indicator_great, + ), + connectionQualityBarFilledPoor = colorResource( + id = R.color.stream_video_connection_indicator_poor, ), participantLabelBackground = colorResource( - id = R.color.stream_video_participant_label_background, + id = R.color.stream_video_participant_label_background_dark, ), infoMenuOverlayColor = Color.LightGray.copy(alpha = 0.7f), callFocusedBorder = colorResource(id = R.color.stream_video_focused_border_color), callGradientStart = colorResource(id = R.color.stream_video_call_gradient_start), callGradientEnd = colorResource(id = R.color.stream_video_call_gradient_end), callDescription = colorResource(id = R.color.stream_video_call_description_dark), - callActionIconEnabled = colorResource( - id = R.color.stream_video_action_icon_enabled_dark, - ), - callActionIconDisabled = colorResource( - id = R.color.stream_video_action_icon_disabled_dark, - ), callActionIconEnabledBackground = colorResource( id = R.color.stream_video_action_icon_enabled_background_dark, ), callActionIconDisabledBackground = colorResource( id = R.color.stream_video_action_icon_disabled_background_dark, ), + callActionIconEnabled = colorResource( + id = R.color.stream_video_action_icon_enabled_dark, + ), + callActionIconDisabled = colorResource( + id = R.color.stream_video_action_icon_disabled_dark, + ), callLobbyBackground = colorResource(id = R.color.stream_video_lobby_background_dark), audioLeaveButton = colorResource(id = R.color.stream_video_audio_leave_dark), audioActionColor = colorResource(id = R.color.stream_video_audio_room_actions_dark), liveIndicator = colorResource(id = R.color.stream_video_live_indicator_dark), + audioIndicatorBackground = colorResource( + id = R.color.stream_video_volume_indicator_background_dark, + ), + avatarBorderColor = colorResource(id = R.color.stream_video_avatar_border_color), + participantContainerBackground = colorResource( + id = R.color.stream_video_participant_container_background, + ), ) } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamDimens.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamDimens.kt index 3fe0344714..9ff0a45659 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamDimens.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamDimens.kt @@ -81,6 +81,7 @@ public data class StreamDimens( public val participantsInfoMenuOptionsButtonHeight: Dp, public val participantsInfoAvatarSize: Dp, public val participantsGridPadding: Dp, + public val participantContentRadius: Dp, public val floatingVideoPadding: Dp, public val floatingVideoHeight: Dp, public val floatingVideoWidth: Dp, @@ -115,6 +116,9 @@ public data class StreamDimens( public val audioRoomMicPadding: Dp, public val audioRoomAvatarPortraitPadding: Dp, public val audioRoomAvatarLandscapePadding: Dp, + public val indicatorBackgroundSize: Dp, + public val avatarBorderPadding: Dp, + val avatarBorderWidth: Dp, ) { public companion object { /** @@ -141,7 +145,6 @@ public data class StreamDimens( participantsTextPadding = dimensionResource( id = R.dimen.stream_video_participantsTextPadding, ), - topAppbarTextSize = textSizeResource(id = R.dimen.stream_video_topAppbarTextSize), directCallUserNameTextSize = textSizeResource( id = R.dimen.stream_video_directCallUserNameTextSize, ), @@ -149,6 +152,7 @@ public data class StreamDimens( id = R.dimen.stream_video_groupCallUserNameTextSize, ), onCallStatusTextSize = textSizeResource(id = R.dimen.stream_video_onCallStatusTextSize), + topAppbarTextSize = textSizeResource(id = R.dimen.stream_video_topAppbarTextSize), onCallStatusTextAlpha = floatResource(R.dimen.stream_video_onCallStatusTextAlpha), buttonToggleOnAlpha = floatResource(R.dimen.stream_video_buttonToggleOnAlpha), buttonToggleOffAlpha = floatResource(R.dimen.stream_video_buttonToggleOffAlpha), @@ -186,9 +190,24 @@ public data class StreamDimens( callAppBarRecordingIndicatorSize = dimensionResource( id = R.dimen.stream_video_callAppBarRecordingIndicatorSize, ), + controlActionsBottomPadding = dimensionResource( + id = R.dimen.stream_video_controlActionsBottomPadding, + ), + controlActionsHeight = dimensionResource( + id = R.dimen.stream_video_controlActionsHeight, + ), controlActionsButtonSize = dimensionResource( id = R.dimen.stream_video_controlActionsButtonSize, ), + controlActionsElevation = dimensionResource( + id = R.dimen.stream_video_controlActionsElevation, + ), + landscapeControlActionsWidth = dimensionResource( + id = R.dimen.stream_video_landscapeControlActionsWidth, + ), + landscapeControlActionsButtonSize = dimensionResource( + id = R.dimen.stream_video_landscapeControlActionsButtonSize, + ), participantFocusedBorderWidth = dimensionResource( id = R.dimen.stream_video_activeSpeakerBoarderWidth, ), @@ -210,21 +229,6 @@ public data class StreamDimens( participantLabelTextPaddingStart = dimensionResource( id = R.dimen.stream_video_callParticipantSoundIndicatorPaddingStart, ), - participantsGridPadding = dimensionResource( - id = R.dimen.stream_video_participantsGridPadding, - ), - landscapeControlActionsButtonSize = dimensionResource( - id = R.dimen.stream_video_landscapeControlActionsButtonSize, - ), - controlActionsHeight = dimensionResource( - id = R.dimen.stream_video_controlActionsHeight, - ), - controlActionsElevation = dimensionResource( - id = R.dimen.stream_video_controlActionsElevation, - ), - landscapeControlActionsWidth = dimensionResource( - id = R.dimen.stream_video_landscapeControlActionsWidth, - ), participantInfoMenuAppBarHeight = dimensionResource( id = R.dimen.stream_video_participantInfoMenuAppBarHeight, ), @@ -237,6 +241,12 @@ public data class StreamDimens( participantsInfoAvatarSize = dimensionResource( id = R.dimen.stream_video_participantsInfoAvatarSize, ), + participantsGridPadding = dimensionResource( + id = R.dimen.stream_video_participantsGridPadding, + ), + participantContentRadius = dimensionResource( + id = R.dimen.stream_video_callParticipant_container_radius, + ), floatingVideoPadding = dimensionResource( id = R.dimen.stream_video_floatingVideoPadding, ), @@ -260,15 +270,15 @@ public data class StreamDimens( audioLevelIndicatorBarSeparatorWidth = dimensionResource( id = R.dimen.stream_video_audioLevelIndicatorBarSeparatorWidth, ), + audioLevelIndicatorBarPadding = dimensionResource( + id = R.dimen.stream_video_audioLevelIndicatorBarPadding, + ), microphoneIndicatorSize = dimensionResource( id = R.dimen.stream_video_microphoneIndicatorSize, ), microphoneIndicatorPadding = dimensionResource( id = R.dimen.stream_video_microphoneIndicatorPadding, ), - audioLevelIndicatorBarPadding = dimensionResource( - id = R.dimen.stream_video_audioLevelIndicatorBarPadding, - ), screenShareParticipantItemSize = dimensionResource( id = R.dimen.stream_video_screenShareParticipantItemSize, ), @@ -287,18 +297,15 @@ public data class StreamDimens( screenShareParticipantsRadius = dimensionResource( id = R.dimen.stream_video_screenShareParticipantsRadius, ), + screenSharePresenterPadding = dimensionResource( + id = R.dimen.stream_video_screenSharePresenterPadding, + ), screenSharePresenterTooltipMargin = dimensionResource( id = R.dimen.stream_video_screenSharePresenterTooltipMargin, ), screenSharePresenterTooltipPadding = dimensionResource( id = R.dimen.stream_video_screenSharePresenterTooltipPadding, ), - screenSharePresenterPadding = dimensionResource( - id = R.dimen.stream_video_screenSharePresenterPadding, - ), - controlActionsBottomPadding = dimensionResource( - id = R.dimen.stream_video_controlActionsBottomPadding, - ), screenSharePresenterTooltipIconPadding = dimensionResource( id = R.dimen.stream_video_screenShareTooltipIconPadding, ), @@ -326,6 +333,13 @@ public data class StreamDimens( audioRoomAvatarLandscapePadding = dimensionResource( id = R.dimen.stream_video_audioRoomAvatarLandscapePadding, ), + indicatorBackgroundSize = dimensionResource( + id = R.dimen.stream_video_IndicatorBackgroundSize, + ), + avatarBorderPadding = dimensionResource( + id = R.dimen.stream_video_audioAvatarBorderPadding, + ), + avatarBorderWidth = dimensionResource(id = R.dimen.stream_video_avatarBorderWidth), ) } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamShapes.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamShapes.kt index bd702b0750..de740b6b28 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamShapes.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamShapes.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.unit.dp * @param callControls The shape of the call controls sheet when in a call. * @param callControlsButton Tha shape of the buttons within Call Controls. * @param participantsInfoMenuButton The shape of buttons in the Participants Info menu. + * @param indicatorBackground The indicator background shape. */ @Immutable public data class StreamShapes( @@ -47,6 +48,9 @@ public data class StreamShapes( public val soundIndicatorBar: Shape, public val floatingParticipant: Shape, public val connectionQualityIndicator: Shape, + public val indicatorBackground: Shape, + val participantLabelShape: Shape, + val participantContainerShape: Shape, ) { public companion object { /** @@ -57,16 +61,19 @@ public data class StreamShapes( @Composable public fun defaultShapes(): StreamShapes = StreamShapes( avatar = CircleShape, + dialog = RoundedCornerShape(16.dp), callButton = CircleShape, callControls = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), callControlsLandscape = RectangleShape, callControlsButton = CircleShape, participantsInfoMenuButton = RoundedCornerShape(32.dp), - dialog = RoundedCornerShape(16.dp), connectionIndicatorBar = RoundedCornerShape(16.dp), soundIndicatorBar = RoundedCornerShape(16.dp), floatingParticipant = RoundedCornerShape(16.dp), - connectionQualityIndicator = RoundedCornerShape(5.dp), + connectionQualityIndicator = RoundedCornerShape(topStart = 5.dp), + indicatorBackground = RoundedCornerShape(5.dp), + participantLabelShape = RoundedCornerShape(topEnd = 5.dp), + participantContainerShape = RoundedCornerShape(16.dp), ) } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/avatar/UserAvatarBackground.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/avatar/UserAvatarBackground.kt index a6ea9b8e57..5af0a1b48d 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/avatar/UserAvatarBackground.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/avatar/UserAvatarBackground.kt @@ -17,8 +17,10 @@ package io.getstream.video.android.compose.ui.components.avatar import androidx.annotation.DrawableRes +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable @@ -35,7 +37,6 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp 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 @@ -62,40 +63,44 @@ public fun UserAvatarBackground( userImage: String?, modifier: Modifier = Modifier, shape: Shape = VideoTheme.shapes.avatar, - avatarSize: Dp = 72.dp, + avatarSize: Dp = 84.dp, avatarShadowElevation: Dp = 12.dp, textStyle: TextStyle = VideoTheme.typography.title3Bold, contentScale: ContentScale = ContentScale.Crop, contentDescription: String? = null, requestSize: IntSize = IntSize(DEFAULT_IMAGE_SIZE, DEFAULT_IMAGE_SIZE), initialsAvatarOffset: DpOffset = DpOffset(0.dp, 0.dp), - blurRadius: Int = 20, @DrawableRes previewPlaceholder: Int = LocalAvatarPreviewProvider.getLocalAvatarPreviewPlaceholder(), @DrawableRes loadingPlaceholder: Int? = LocalAvatarPreviewProvider.getLocalAvatarLoadingPlaceholder(), ) { - Box(modifier = modifier) { - ParticipantImageBackground( - modifier = Modifier.fillMaxSize(), - userImage = userImage, - blurRadius = blurRadius, - ) - - UserAvatar( + Box(modifier = modifier.fillMaxSize()) { + Box( modifier = Modifier .size(avatarSize) .align(Alignment.Center) - .shadow(elevation = avatarShadowElevation, shape = CircleShape), - userName = userName, - userImage = userImage, - shape = shape, - textStyle = textStyle, - contentScale = contentScale, - contentDescription = contentDescription, - requestSize = requestSize, - initialsAvatarOffset = initialsAvatarOffset, - previewPlaceholder = previewPlaceholder, - loadingPlaceholder = loadingPlaceholder, - ) + .border( + width = VideoTheme.dimens.avatarBorderWidth, + color = VideoTheme.colors.avatarBorderColor, + shape = VideoTheme.shapes.avatar, + ), + ) { + UserAvatar( + modifier = Modifier + .padding(VideoTheme.dimens.avatarBorderPadding) + .align(Alignment.Center) + .shadow(elevation = avatarShadowElevation, shape = CircleShape), + userName = userName, + userImage = userImage, + shape = shape, + textStyle = textStyle, + contentScale = contentScale, + contentDescription = contentDescription, + requestSize = requestSize, + initialsAvatarOffset = initialsAvatarOffset, + previewPlaceholder = previewPlaceholder, + loadingPlaceholder = loadingPlaceholder, + ) + } } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt index 56682b94d2..bf2dd9fb37 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt @@ -46,11 +46,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.BottomEnd import androidx.compose.ui.Alignment.Companion.BottomStart +import androidx.compose.ui.Alignment.Companion.Center import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode @@ -106,7 +106,9 @@ public fun ParticipantVideo( connectionIndicatorContent: @Composable BoxScope.(NetworkQuality) -> Unit = { NetworkQualityIndicator( networkQuality = it, - modifier = Modifier.align(BottomEnd), + modifier = Modifier + .align(BottomEnd) + .height(VideoTheme.dimens.participantLabelHeight), ) }, videoFallbackContent: @Composable (Call) -> Unit = { @@ -137,6 +139,11 @@ public fun ParticipantVideo( } } + val containerShape = if (style.isScreenSharing) { + RoundedCornerShape(VideoTheme.dimens.screenShareParticipantsRadius) + } else { + VideoTheme.shapes.participantContainerShape + } val containerModifier = if (style.isFocused && participants.size > 1) { modifier.border( border = if (style.isScreenSharing) { @@ -150,22 +157,15 @@ public fun ParticipantVideo( VideoTheme.colors.callFocusedBorder, ) }, - shape = if (style.isScreenSharing) { - RoundedCornerShape(VideoTheme.dimens.screenShareParticipantsRadius) - } else { - RectangleShape - }, + shape = containerShape, ) } else { modifier } - Box( - modifier = containerModifier.apply { - if (style.isScreenSharing) { - clip(RoundedCornerShape(VideoTheme.dimens.screenShareParticipantsRadius)) - } - }, + modifier = containerModifier + .clip(containerShape) + .background(VideoTheme.colors.participantContainerBackground), ) { ParticipantVideoRenderer( call = call, @@ -294,32 +294,34 @@ public fun BoxScope.ParticipantLabel( ) }, ) { - Row( + Box( modifier = Modifier .align(labelPosition) - .padding(VideoTheme.dimens.participantLabelPadding) .height(VideoTheme.dimens.participantLabelHeight) .wrapContentWidth() - .clip(RoundedCornerShape(8.dp)) .background( VideoTheme.colors.participantLabelBackground, - shape = RoundedCornerShape(8.dp), + shape = VideoTheme.shapes.participantLabelShape, ), - verticalAlignment = CenterVertically, ) { - Text( - modifier = Modifier - .widthIn(max = VideoTheme.dimens.participantLabelTextMaxWidth) - .padding(start = VideoTheme.dimens.participantLabelTextPaddingStart) - .align(CenterVertically), - text = nameLabel, - style = VideoTheme.typography.body, - color = Color.White, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + Row( + modifier = Modifier.align(Center), + verticalAlignment = CenterVertically, + ) { + Text( + modifier = Modifier + .widthIn(max = VideoTheme.dimens.participantLabelTextMaxWidth) + .padding(start = VideoTheme.dimens.participantLabelTextPaddingStart) + .align(CenterVertically), + text = nameLabel, + style = VideoTheme.typography.body, + color = Color.White, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) - soundIndicatorContent.invoke(this) + soundIndicatorContent.invoke(this) + } } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle.kt index 2fd3eb3b34..30041a95e5 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/VideoRendererStyle.kt @@ -111,7 +111,7 @@ public data class RegularVideoRendererStyle( override val isShowingConnectionQualityIndicator: Boolean = true, override val labelPosition: Alignment = Alignment.BottomStart, override val reactionDuration: Int = 650, - override val reactionPosition: Alignment = Alignment.Center, + override val reactionPosition: Alignment = Alignment.TopEnd, ) : VideoRendererStyle( isFocused, diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeScreenSharingVideoRenderer.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeScreenSharingVideoRenderer.kt index f7c0dd89c0..d5e29f6b45 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeScreenSharingVideoRenderer.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeScreenSharingVideoRenderer.kt @@ -19,15 +19,16 @@ package io.getstream.video.android.compose.ui.components.call.renderer.internal import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview @@ -80,16 +81,16 @@ internal fun LandscapeScreenSharingVideoRenderer( val sharingParticipant = session.participant val me by call.state.me.collectAsStateWithLifecycle() - Row( - modifier = modifier - .fillMaxSize() - .background(VideoTheme.colors.screenSharingBackground), + Column( + modifier = modifier.fillMaxWidth(), ) { Box( modifier = Modifier - .fillMaxHeight() + .padding(VideoTheme.dimens.participantsGridPadding) + .clip(RoundedCornerShape(16.dp)) + .fillMaxWidth() .weight(0.65f) - .padding(VideoTheme.dimens.participantsGridPadding), + .background(VideoTheme.colors.screenSharingBackground), ) { ScreenShareVideoRenderer( modifier = Modifier.fillMaxSize(), @@ -106,10 +107,8 @@ internal fun LandscapeScreenSharingVideoRenderer( } } - LazyColumnVideoRenderer( - modifier = Modifier - .width(156.dp) - .fillMaxHeight(), + LazyRowVideoRenderer( + modifier = Modifier.fillMaxWidth(), call = call, participants = participants, dominantSpeaker = dominantSpeaker, diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeVideoRenderer.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeVideoRenderer.kt index 334ac1e40b..2d4f24092b 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeVideoRenderer.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LandscapeVideoRenderer.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.runtime.Composable @@ -84,14 +85,17 @@ internal fun BoxScope.LandscapeVideoRenderer( }, ) { val remoteParticipants by call.state.remoteParticipants.collectAsStateWithLifecycle() - + val paddedModifier = modifier.padding(VideoTheme.dimens.participantsGridPadding) when (callParticipants.size) { - 0 -> Unit - 1 -> { - val participant = callParticipants.first() + 1, 2 -> { + val participant = if (remoteParticipants.isEmpty()) { + callParticipants.first() + } else { + remoteParticipants.first() + } videoRenderer.invoke( - modifier = Modifier.fillMaxHeight(), + modifier = paddedModifier.fillMaxHeight(), call = call, participant = participant, style = style.copy( @@ -100,13 +104,12 @@ internal fun BoxScope.LandscapeVideoRenderer( ) } - 2, 3 -> { + 3, 4 -> { val rowItemWeight = 1f / callParticipants.size - Row(modifier = modifier) { remoteParticipants.take(callParticipants.size - 1).forEach { participant -> videoRenderer.invoke( - modifier = Modifier + modifier = paddedModifier .fillMaxHeight() .weight(rowItemWeight), call = call, @@ -119,181 +122,28 @@ internal fun BoxScope.LandscapeVideoRenderer( } } - 4 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - - Column(modifier) { - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - - ) - } - - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - } - } - } - - 5 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - val fifthParticipant = callParticipants[4] - - Column(modifier) { - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - ) - } - - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fifthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fifthParticipant.sessionId, - ), - ) - } - } - } - - 6 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - val fifthParticipant = callParticipants[4] - val sixthParticipant = callParticipants[5] - + 5, 6 -> { + val rowSize = if (callParticipants.size == 5) Pair(3, 2) else Pair(3, 3) Column(modifier) { - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - } - - Row(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fifthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fifthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = sixthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == sixthParticipant.sessionId, - ), - ) - } + ParticipantRow( + modifier = Modifier.weight(1f), + participants = callParticipants.take(rowSize.first), + videoRenderer = videoRenderer, + paddedModifier = paddedModifier, + call = call, + style = style, + dominantSpeaker = dominantSpeaker, + ) + ParticipantRow( + modifier = Modifier.weight(1f), + participants = callParticipants.takeLast(rowSize.second), + videoRenderer = videoRenderer, + paddedModifier = paddedModifier, + call = call, + style = style, + dominantSpeaker = dominantSpeaker, + expectedRowSize = rowSize.first, + ) } } @@ -315,7 +165,7 @@ internal fun BoxScope.LandscapeVideoRenderer( } val participant = callParticipants[key] videoRenderer.invoke( - modifier = modifier.height(itemHeight), + modifier = paddedModifier.height(itemHeight), call = call, participant = participant, style = style.copy( @@ -329,7 +179,7 @@ internal fun BoxScope.LandscapeVideoRenderer( } } - if (callParticipants.size in 2..3) { + if (callParticipants.size in 2..4) { val currentLocal by call.state.me.collectAsStateWithLifecycle() if (currentLocal != null || LocalInspectionMode.current) { @@ -347,6 +197,40 @@ internal fun BoxScope.LandscapeVideoRenderer( } } +@Composable +private fun ParticipantRow( + modifier: Modifier, + participants: List, + videoRenderer: @Composable ( + modifier: Modifier, + call: Call, + participant: ParticipantState, + style: VideoRendererStyle, + ) -> Unit, + paddedModifier: Modifier, + call: Call, + style: VideoRendererStyle, + dominantSpeaker: ParticipantState?, + expectedRowSize: Int = participants.size, +) { + Row(modifier) { + repeat(participants.size) { + val participant = participants[it] + videoRenderer.invoke( + modifier = paddedModifier.weight(1f), + call = call, + participant = participant, + style = style.copy( + isFocused = dominantSpeaker?.sessionId == participant.sessionId, + ), + ) + } + repeat(expectedRowSize - participants.size) { + Box(modifier = paddedModifier.weight(1f)) + } + } +} + @Preview(device = Devices.AUTOMOTIVE_1024p, widthDp = 1440, heightDp = 720) @Composable private fun LandscapeParticipantsPreview1() { diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LazyRowVideoRenderer.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LazyRowVideoRenderer.kt index 01c0f7ffac..faf05ee32b 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LazyRowVideoRenderer.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/LazyRowVideoRenderer.kt @@ -115,9 +115,11 @@ private fun ListVideoRenderer( ) }, ) { + val height = VideoTheme.dimens.screenShareParticipantItemSize + val width = height * 1.5f videoRenderer.invoke( modifier = Modifier - .size(VideoTheme.dimens.screenShareParticipantItemSize) + .size(width, height) .clip(RoundedCornerShape(VideoTheme.dimens.screenShareParticipantsRadius)), call = call, participant = participant, diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitScreenSharingVideoRenderer.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitScreenSharingVideoRenderer.kt index 03a509ecdc..23c12243d4 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitScreenSharingVideoRenderer.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitScreenSharingVideoRenderer.kt @@ -19,23 +19,36 @@ package io.getstream.video.android.compose.ui.components.call.renderer.internal import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.BoxWithConstraintsScope import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.call.renderer.ParticipantVideo import io.getstream.video.android.compose.ui.components.call.renderer.ScreenSharingVideoRendererStyle import io.getstream.video.android.compose.ui.components.call.renderer.VideoRendererStyle +import io.getstream.video.android.compose.ui.components.call.renderer.copy import io.getstream.video.android.core.Call import io.getstream.video.android.core.ParticipantState import io.getstream.video.android.core.model.ScreenSharingSession @@ -78,15 +91,68 @@ internal fun PortraitScreenSharingVideoRenderer( ) { val sharingParticipant = session.participant val me by call.state.me.collectAsStateWithLifecycle() + var parentSize: IntSize by remember { mutableStateOf(IntSize(0, 0)) } + val paddedModifier = modifier.padding(VideoTheme.dimens.participantsGridPadding) + BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { + LazyVerticalGrid( + modifier = Modifier.fillMaxSize(), + columns = GridCells.Fixed(2), + content = { + item(span = { GridItemSpan(2) }) { + ScreenSharingContent( + modifier, + call, + session, + isZoomable, + me, + sharingParticipant, + ) + } + items( + count = participants.size, + ) { key -> + // make 3 items exactly fit available height + val itemHeight = with(LocalDensity.current) { + (constraints.maxHeight / 6).toDp() + } + val participant = participants[key] + videoRenderer.invoke( + modifier = paddedModifier.height(itemHeight), + call = call, + participant = participant, + style = style.copy( + isFocused = dominantSpeaker?.sessionId == participant.sessionId, + ), + ) + } + }, + ) + } +} + +@Composable +private fun BoxWithConstraintsScope.ScreenSharingContent( + modifier: Modifier, + call: Call, + session: ScreenSharingSession, + isZoomable: Boolean, + me: ParticipantState?, + sharingParticipant: ParticipantState, +) { + val itemHeight = with(LocalDensity.current) { + ((constraints.maxHeight * 0.45).toInt()).toDp() + } Column( - modifier = modifier.background(VideoTheme.colors.screenSharingBackground), + modifier = modifier + .padding(VideoTheme.dimens.participantsGridPadding), ) { Box( modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .background(VideoTheme.colors.screenSharingBackground) .fillMaxWidth() - .weight(1f) - .padding(VideoTheme.dimens.participantsGridPadding), + .height(itemHeight), ) { ScreenShareVideoRenderer( modifier = Modifier.fillMaxWidth(), @@ -102,21 +168,6 @@ internal fun PortraitScreenSharingVideoRenderer( ) } } - - Spacer( - modifier = Modifier.height( - VideoTheme.dimens.screenShareParticipantsScreenShareListMargin, - ), - ) - - LazyRowVideoRenderer( - modifier = Modifier.height(VideoTheme.dimens.screenShareParticipantsRowHeight), - call = call, - dominantSpeaker = dominantSpeaker, - participants = participants, - style = style, - videoRenderer = videoRenderer, - ) } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitVideoRenderer.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitVideoRenderer.kt index 272503ddcf..bbaa376578 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitVideoRenderer.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/PortraitVideoRenderer.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.runtime.Composable @@ -89,12 +90,16 @@ internal fun BoxScope.PortraitVideoRenderer( return } + val paddedModifier = modifier.padding(VideoTheme.dimens.participantsGridPadding) when (callParticipants.size) { - 1 -> { - val participant = callParticipants.first() - + 1, 2 -> { + val participant = if (remoteParticipants.isEmpty()) { + callParticipants.first() + } else { + remoteParticipants.first() + } videoRenderer.invoke( - modifier = modifier, + modifier = paddedModifier, call = call, participant = participant, style = style.copy( @@ -103,221 +108,45 @@ internal fun BoxScope.PortraitVideoRenderer( ) } - 2 -> { - val participant = remoteParticipants.first() - - videoRenderer.invoke( - modifier = modifier, - call = call, - participant = participant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == participant.sessionId, - ), + 3, 4 -> { + ParticipantColumn( + modifier, + remoteParticipants, + videoRenderer, + paddedModifier, + call, + style, + dominantSpeaker, + 0, ) } - 3 -> { - val firstParticipant = remoteParticipants[0] - val secondParticipant = remoteParticipants[1] + 5, 6 -> { + val columnSize = if (callParticipants.size == 5) Pair(3, 2) else Pair(3, 3) - Column(modifier) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), + Row(modifier) { + ParticipantColumn( + modifier = modifier.weight(1f), + remoteParticipants = callParticipants.take(columnSize.first), + videoRenderer = videoRenderer, + paddedModifier = paddedModifier, call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), + style = style, + dominantSpeaker = dominantSpeaker, ) - videoRenderer.invoke( - modifier = Modifier.weight(1f), + ParticipantColumn( + modifier = modifier.weight(1f), + remoteParticipants = callParticipants.takeLast(columnSize.second), + videoRenderer = videoRenderer, + paddedModifier = paddedModifier, call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), + style = style, + dominantSpeaker = dominantSpeaker, + expectedColumnSize = columnSize.first, ) } } - - 4 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - - Row(modifier) { - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - ) - } - - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - } - } - } - - 5 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - val fifthParticipant = callParticipants[4] - - Row(modifier) { - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - ) - } - - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fifthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fifthParticipant.sessionId, - ), - ) - } - } - } - - 6 -> { - val firstParticipant = callParticipants[0] - val secondParticipant = callParticipants[1] - val thirdParticipant = callParticipants[2] - val fourthParticipant = callParticipants[3] - val fifthParticipant = callParticipants[4] - val sixthParticipant = callParticipants[5] - - Row(modifier) { - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = firstParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == firstParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = secondParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == secondParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = thirdParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == thirdParticipant.sessionId, - ), - ) - } - - Column(modifier = Modifier.weight(1f)) { - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fourthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fourthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = fifthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == fifthParticipant.sessionId, - ), - ) - - videoRenderer.invoke( - modifier = Modifier.weight(1f), - call = call, - participant = sixthParticipant, - style = style.copy( - isFocused = dominantSpeaker?.sessionId == sixthParticipant.sessionId, - ), - ) - } - } - } - else -> { BoxWithConstraints(modifier = Modifier.fillMaxSize()) { val gridState = lazyGridStateWithVisibilityNotification(call = call) @@ -336,7 +165,7 @@ internal fun BoxScope.PortraitVideoRenderer( } val participant = callParticipants[key] videoRenderer.invoke( - modifier = modifier.height(itemHeight), + modifier = paddedModifier.height(itemHeight), call = call, participant = participant, style = style.copy( @@ -350,7 +179,7 @@ internal fun BoxScope.PortraitVideoRenderer( } } - if (callParticipants.size in 2..3) { + if (callParticipants.size in 2..4) { val currentLocal by call.state.me.collectAsStateWithLifecycle() if (currentLocal != null || LocalInspectionMode.current) { @@ -368,6 +197,40 @@ internal fun BoxScope.PortraitVideoRenderer( } } +@Composable +private fun ParticipantColumn( + modifier: Modifier, + remoteParticipants: List, + videoRenderer: @Composable ( + modifier: Modifier, + call: Call, + participant: ParticipantState, + style: VideoRendererStyle, + ) -> Unit, + paddedModifier: Modifier, + call: Call, + style: VideoRendererStyle, + dominantSpeaker: ParticipantState?, + expectedColumnSize: Int = remoteParticipants.size, +) { + Column(modifier) { + repeat(remoteParticipants.size) { + val participant = remoteParticipants[it] + videoRenderer.invoke( + modifier = paddedModifier.weight(1f), + call = call, + participant = participant, + style = style.copy( + isFocused = dominantSpeaker?.sessionId == participant.sessionId, + ), + ) + } + repeat(expectedColumnSize - remoteParticipants.size) { + Box(modifier = paddedModifier.weight(1f)) + } + } +} + @Preview @Composable private fun PortraitParticipantsPreview1() { diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/ScreenShareTooltip.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/ScreenShareTooltip.kt index a71910d6e3..21d045f7e7 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/ScreenShareTooltip.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/ScreenShareTooltip.kt @@ -47,13 +47,12 @@ internal fun ScreenShareTooltip( Row( modifier = modifier - .padding(VideoTheme.dimens.screenSharePresenterTooltipMargin) .height(VideoTheme.dimens.screenSharePresenterTooltipHeight) .wrapContentWidth() - .clip(RoundedCornerShape(8.dp)) + .clip(RoundedCornerShape(bottomEnd = 8.dp)) .background( color = VideoTheme.colors.screenSharingTooltipBackground, - shape = RoundedCornerShape(8.dp), + shape = RoundedCornerShape(bottomEnd = 8.dp), ), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/NetworkQualityIndicator.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/NetworkQualityIndicator.kt index 5e3783c68d..ec2be4458c 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/NetworkQualityIndicator.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/NetworkQualityIndicator.kt @@ -16,20 +16,14 @@ package io.getstream.video.android.compose.ui.components.connection -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import io.getstream.video.android.compose.theme.VideoTheme +import io.getstream.video.android.compose.ui.components.connection.internal.ConnectionBars +import io.getstream.video.android.compose.ui.components.connection.internal.barColorsFromQuality +import io.getstream.video.android.compose.ui.components.indicator.GenericIndicator import io.getstream.video.android.core.model.NetworkQuality import stream.video.sfu.models.ConnectionQuality @@ -44,69 +38,13 @@ public fun NetworkQualityIndicator( networkQuality: NetworkQuality, modifier: Modifier = Modifier, ) { - val quality = networkQuality.quality - - Box( - modifier = modifier - .padding(8.dp) - .background( - shape = VideoTheme.shapes.connectionQualityIndicator, - color = VideoTheme.colors.connectionQualityBackground, - ) - .padding(6.dp), + val colors = barColorsFromQuality(networkQuality) + GenericIndicator( + modifier = modifier, + shape = VideoTheme.shapes.connectionQualityIndicator, + backgroundColor = VideoTheme.colors.connectionQualityBackground, ) { - Row( - modifier = Modifier - .height(height = VideoTheme.dimens.connectionIndicatorBarMaxHeight) - .align(Alignment.Center), - verticalAlignment = Alignment.Bottom, - ) { - Spacer( - modifier = Modifier - .width(VideoTheme.dimens.connectionIndicatorBarWidth) - .fillMaxHeight(0.33f) - .background( - color = if (quality > 0.33f) { - VideoTheme.colors.connectionQualityBarFilled - } else { - VideoTheme.colors.errorAccent - }, - shape = VideoTheme.shapes.connectionIndicatorBar, - ), - ) - - Spacer(modifier = Modifier.width(3.dp)) - - Spacer( - modifier = Modifier - .width(VideoTheme.dimens.connectionIndicatorBarWidth) - .fillMaxHeight(fraction = 0.66f) - .background( - color = if (quality >= 0.66f) { - VideoTheme.colors.connectionQualityBarFilled - } else { - VideoTheme.colors.connectionQualityBar - }, - shape = VideoTheme.shapes.connectionIndicatorBar, - ), - ) - - Spacer(modifier = Modifier.width(3.dp)) - - Spacer( - modifier = Modifier - .width(VideoTheme.dimens.connectionIndicatorBarWidth) - .fillMaxHeight(fraction = 1f) - .background( - color = if (quality >= 1) { - VideoTheme.colors.connectionQualityBarFilled - } else { - VideoTheme.colors.connectionQualityBar - }, - shape = VideoTheme.shapes.connectionIndicatorBar, - ), - ) - } + ConnectionBars(colors = colors) } } @@ -115,6 +53,9 @@ public fun NetworkQualityIndicator( private fun ConnectionQualityIndicatorPreview() { VideoTheme { Row { + NetworkQualityIndicator( + networkQuality = NetworkQuality.UnSpecified(), + ) NetworkQualityIndicator( networkQuality = NetworkQuality.Poor(), ) diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/internal/ConnectionBars.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/internal/ConnectionBars.kt new file mode 100644 index 0000000000..7c20da0b88 --- /dev/null +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/connection/internal/ConnectionBars.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014-2023 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-video-android/blob/main/LICENSE + * + * 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.getstream.video.android.compose.ui.components.connection.internal + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import io.getstream.video.android.compose.theme.VideoTheme +import io.getstream.video.android.core.model.NetworkQuality + +@Composable +internal fun barColorsFromQuality( + networkQuality: NetworkQuality, +): Triple = when (networkQuality) { + is NetworkQuality.Excellent -> Triple( + VideoTheme.colors.connectionQualityBarFilled, + VideoTheme.colors.connectionQualityBarFilled, + VideoTheme.colors.connectionQualityBarFilled, + ) + is NetworkQuality.Good -> Triple( + VideoTheme.colors.connectionQualityBarFilled, + VideoTheme.colors.connectionQualityBarFilled, + VideoTheme.colors.connectionQualityBar, + ) + is NetworkQuality.Poor -> Triple( + VideoTheme.colors.connectionQualityBarFilledPoor, + VideoTheme.colors.connectionQualityBar, + VideoTheme.colors.connectionQualityBar, + ) + is NetworkQuality.UnSpecified -> Triple( + VideoTheme.colors.connectionQualityBar, + VideoTheme.colors.connectionQualityBar, + VideoTheme.colors.connectionQualityBar, + ) +} + +@Composable +internal fun ConnectionBars(modifier: Modifier = Modifier, colors: Triple) { + Row( + modifier = modifier + .padding(VideoTheme.dimens.connectionIndicatorBarWidth) + .height(height = VideoTheme.dimens.connectionIndicatorBarMaxHeight), + verticalAlignment = Alignment.Bottom, + ) { + Spacer( + modifier = Modifier + .width(VideoTheme.dimens.connectionIndicatorBarWidth) + .fillMaxHeight(0.4f) + .background( + color = colors.first, + shape = VideoTheme.shapes.connectionIndicatorBar, + ), + ) + Spacer(modifier = Modifier.width(VideoTheme.dimens.connectionIndicatorBarSeparatorWidth)) + Spacer( + modifier = Modifier + .width(VideoTheme.dimens.connectionIndicatorBarWidth) + .fillMaxHeight(fraction = 0.7f) + .background( + color = colors.second, + shape = VideoTheme.shapes.connectionIndicatorBar, + ), + ) + Spacer(modifier = Modifier.width(VideoTheme.dimens.connectionIndicatorBarSeparatorWidth)) + Spacer( + modifier = Modifier + .width(VideoTheme.dimens.connectionIndicatorBarWidth) + .fillMaxHeight(fraction = 1f) + .background( + color = colors.third, + shape = VideoTheme.shapes.connectionIndicatorBar, + ), + ) + } +} diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/AudioVolumeIndicator.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/AudioVolumeIndicator.kt index f2375f2c48..b6c5253bbd 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/AudioVolumeIndicator.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/AudioVolumeIndicator.kt @@ -52,21 +52,28 @@ public fun AudioVolumeIndicator( Row( modifier = modifier .height(height = VideoTheme.dimens.audioLevelIndicatorBarMaxHeight) - .padding(horizontal = 4.dp), - verticalAlignment = Alignment.Bottom, + .padding(horizontal = 2.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy( VideoTheme.dimens.audioLevelIndicatorBarSeparatorWidth, ), ) { repeat(3) { index -> + // First bar 60%, second 100%, third 33% val audioLevel = - if (index == 0 || index == 2) { - // Draw "fake" side bars that reach 70% of the middle bar, similar to - // what Google Meet is doing. - audioLevels * 0.7f - } else { - audioLevels + when (index) { + 0 -> { + audioLevels * 0.6f + } + + 2 -> { + audioLevels * 0.33f + } + + else -> { + audioLevels + } } Spacer( diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/GenericIndicator.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/GenericIndicator.kt new file mode 100644 index 0000000000..04a8e68932 --- /dev/null +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/GenericIndicator.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2023 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-video-android/blob/main/LICENSE + * + * 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.getstream.video.android.compose.ui.components.indicator + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.getstream.video.android.compose.theme.VideoTheme + +/** + * A composable that wraps its content into a rounded semi-transparent background. + */ +@Composable +internal fun GenericIndicator( + modifier: Modifier = Modifier, + shape: Shape, + backgroundColor: Color, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = modifier.size(VideoTheme.dimens.indicatorBackgroundSize), + ) { + val backgroundModifier = modifier + .matchParentSize() + + // Ensure content is center aligned and padded + Box( + modifier = Modifier + .matchParentSize() + .background( + color = backgroundColor, + shape = shape, + ), + ) + Box( + modifier = Modifier + .align(Alignment.Center) + .padding(4.dp), + ) { + Box(modifier = Modifier.align(Alignment.Center)) { + content(this) + } + } + } +} + +@Preview +@Composable +private fun PreviewIndicatorBackground() { + VideoTheme { + Column { + GenericIndicator( + backgroundColor = VideoTheme.colors.audioIndicatorBackground, + shape = VideoTheme.shapes.indicatorBackground, + ) { + AudioVolumeIndicator(audioLevels = 0.5f) + } + GenericIndicator( + backgroundColor = VideoTheme.colors.audioIndicatorBackground, + shape = VideoTheme.shapes.indicatorBackground, + ) { + MicrophoneIndicator(isMicrophoneEnabled = false) + } + GenericIndicator( + backgroundColor = VideoTheme.colors.audioIndicatorBackground, + shape = VideoTheme.shapes.indicatorBackground, + ) { + MicrophoneIndicator(isMicrophoneEnabled = true) + } + } + } +} diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/MicrophoneIndicator.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/MicrophoneIndicator.kt index b7a09418e9..1353bed0be 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/MicrophoneIndicator.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/MicrophoneIndicator.kt @@ -16,11 +16,13 @@ package io.getstream.video.android.compose.ui.components.indicator +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource @@ -39,24 +41,26 @@ public fun MicrophoneIndicator( modifier: Modifier = Modifier, isMicrophoneEnabled: Boolean, ) { - if (isMicrophoneEnabled) { - Icon( - modifier = modifier - .size(VideoTheme.dimens.microphoneIndicatorSize) - .padding(end = VideoTheme.dimens.microphoneIndicatorPadding), - painter = painterResource(id = R.drawable.stream_video_ic_mic_on), - tint = Color.White, - contentDescription = "microphone enabled", - ) - } else { - Icon( - modifier = modifier - .size(VideoTheme.dimens.microphoneIndicatorSize) - .padding(end = VideoTheme.dimens.microphoneIndicatorPadding), - painter = painterResource(id = R.drawable.stream_video_ic_mic_off), - tint = VideoTheme.colors.errorAccent, - contentDescription = "microphone disabled", - ) + Box( + modifier = modifier + .size(VideoTheme.dimens.microphoneIndicatorSize) + .padding(VideoTheme.dimens.microphoneIndicatorPadding), + ) { + if (isMicrophoneEnabled) { + Icon( + modifier = Modifier.align(Alignment.Center), + painter = painterResource(id = R.drawable.stream_video_ic_mic_on), + tint = Color.White, + contentDescription = "microphone enabled", + ) + } else { + Icon( + modifier = Modifier.align(Alignment.Center), + painter = painterResource(id = R.drawable.stream_video_ic_mic_off), + tint = Color.White, + contentDescription = "microphone disabled", + ) + } } } diff --git a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/SoundIndicator.kt b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/SoundIndicator.kt index 237182216c..18be871b1b 100644 --- a/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/SoundIndicator.kt +++ b/stream-video-android-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/SoundIndicator.kt @@ -16,16 +16,12 @@ package io.getstream.video.android.compose.ui.components.indicator -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Icon +import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import io.getstream.video.android.compose.theme.VideoTheme -import io.getstream.video.android.ui.common.R /** * Used to indicate the sound state of a given participant. Either shows a mute icon or the sound @@ -34,7 +30,7 @@ import io.getstream.video.android.ui.common.R * @param modifier Modifier for styling. * @param isSpeaking Represents is user speaking or not. * @param isAudioEnabled Represents is audio enabled or not. - * @param audioLevels Indicates the audio level that will be drawn. + * @param audioLevel Indicates the audio level that will be drawn. */ @Composable public fun SoundIndicator( @@ -43,20 +39,19 @@ public fun SoundIndicator( isAudioEnabled: Boolean, audioLevel: Float, ) { - if (isAudioEnabled && isSpeaking) { - AudioVolumeIndicator( - modifier = modifier.padding(end = VideoTheme.dimens.audioLevelIndicatorBarPadding), - audioLevels = audioLevel, - ) - } else if (!isAudioEnabled) { - Icon( - modifier = modifier - .size(VideoTheme.dimens.microphoneIndicatorSize) - .padding(end = VideoTheme.dimens.microphoneIndicatorPadding), - painter = painterResource(id = R.drawable.stream_video_ic_mic_off), - tint = VideoTheme.colors.errorAccent, - contentDescription = null, - ) + GenericIndicator( + modifier = modifier, + backgroundColor = VideoTheme.colors.audioIndicatorBackground, + shape = VideoTheme.shapes.indicatorBackground, + ) { + if (isAudioEnabled && isSpeaking) { + AudioVolumeIndicator( + modifier = Modifier.align(Alignment.Center), + audioLevels = audioLevel, + ) + } else { + MicrophoneIndicator(isMicrophoneEnabled = isAudioEnabled) + } } } @@ -64,7 +59,7 @@ public fun SoundIndicator( @Composable private fun SoundIndicatorPreview() { VideoTheme { - Row { + Column { SoundIndicator( isSpeaking = true, isAudioEnabled = true, @@ -75,6 +70,11 @@ private fun SoundIndicatorPreview() { isAudioEnabled = false, audioLevel = 0.5f, ) + SoundIndicator( + isSpeaking = false, + isAudioEnabled = true, + audioLevel = 0.5f, + ) } } } diff --git a/stream-video-android-ui-common/src/main/res/values/colors.xml b/stream-video-android-ui-common/src/main/res/values/colors.xml index a5d17d5a48..ea2da16896 100644 --- a/stream-video-android-ui-common/src/main/res/values/colors.xml +++ b/stream-video-android-ui-common/src/main/res/values/colors.xml @@ -21,20 +21,22 @@ #B4B7BB #DBDDE1 #E9EAED - #F7F7F8 + #FFFFFF #FFFFFF #E9F1FF #80000000 #99000000 #005FFF - #D9303030 - #D9303030 + #A60C0D0E + #A60C0D0E #FFFFFF #D9000000 #FCFCFC #FF3742 #20E070 #005FFF + #123D82 + #1E262E #FBF4DD #FFFFFF #545A64 @@ -47,7 +49,11 @@ #4C525C #FFFFFF #1E262E - #FFFFFF + #005FFF + #A60C0D0E + #00E2A1 + #FFFFFF + #DC433B #FF3742 @@ -73,8 +79,11 @@ #4C525C #FFFFFF #FFFFFF - #FFFFFF + #005FFF + #A60C0D0E #FF3742 + #A60C0D0E + #A60C0D0E #FF8A65 diff --git a/stream-video-android-ui-common/src/main/res/values/dimens.xml b/stream-video-android-ui-common/src/main/res/values/dimens.xml index 9c5af8f358..f45ca126ae 100644 --- a/stream-video-android-ui-common/src/main/res/values/dimens.xml +++ b/stream-video-android-ui-common/src/main/res/values/dimens.xml @@ -53,20 +53,23 @@ 200dp 140dp 16dp - 14dp - 3dp - 3dp - 16dp - 3dp - 3dp - 15dp - 6dp - 4dp - 6dp - 4dp - 24dp + 16dp + 8dp + 1dp + 2dp + 22dp + 2dp + 1dp + 24dp + 18dp + 2dp + 2dp + 1dp + 2dp + 2dp + 32dp 8dp - 0dp + 4dp 8dp 64dp 4dp @@ -85,10 +88,12 @@ 280dp 12dp 32dp - 45dp + 24dp 44dp 84dp - 4dp + 8dp + 4dp + 5dp 20dp 5dp 22dp