Skip to content

Commit 46fcd01

Browse files
committed
Extract VideoUiConfig from VideoTheme
Move behavior settings out of VideoTheme into a VideoUiConfig data class, mirroring ChatUiConfig in the chat SDK. VideoTheme now takes a single config parameter (first, as in ChatTheme) exposed through VideoTheme.config and LocalVideoUiConfig. Removed VideoTheme parameters (breaking, targets develop-v2): - allowUIAutomationTest and reactionMapper: moved into VideoUiConfig. - isInDarkMode: was unused in the function body; there is no light palette yet. The StreamDesign work later on this branch reintroduces it wired to the light and dark palettes. - rippleConfiguration: the type is an object, so the parameter could never carry a custom value. StreamRippleConfiguration is internal now; the colors-derived ripple comes with StreamDesign later on this branch. Also removed the StreamTheme.reactionMapper and StreamTheme.rippleConfiguration accessors. Reaction mapper call sites now read VideoTheme.config.reactionMapper. VideoRendererStyle flags were evaluated and stay where they are: they are per-renderer-instance state (focus, screen share variants), not global behavior. Revisit during AND-1425. The Paparazzi helpers keep their isInDarkMode parameter as a documented no-op so the test call sites do not churn twice; the StreamDesign work wires it to the light and dark palettes. Snapshot goldens are unchanged.
1 parent a87b8f0 commit 46fcd01

7 files changed

Lines changed: 88 additions & 54 deletions

File tree

demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CustomReactionContent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fun BoxScope.CustomReactionContent(
7373
val emojiCode = currentReaction?.response?.emojiCode
7474
if (currentReaction != null && emojiCode != null) {
7575
var isEmojiVisible by remember { mutableStateOf(true) }
76-
val emojiMapper = VideoTheme.reactionMapper
76+
val emojiMapper = VideoTheme.config.reactionMapper
7777
val emojiText = emojiMapper.map(emojiCode)
7878

7979
LaunchedEffect(key1 = Unit) {

stream-video-android-ui-compose/api/stream-video-android-ui-compose.api

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -838,12 +838,6 @@ public final class io/getstream/video/android/compose/theme/StreamDimens$Compani
838838
public final fun defaultDimens (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/StreamDimens;
839839
}
840840

841-
public final class io/getstream/video/android/compose/theme/StreamRippleConfiguration {
842-
public static final field $stable I
843-
public static final field INSTANCE Lio/getstream/video/android/compose/theme/StreamRippleConfiguration;
844-
public final fun default (Landroidx/compose/runtime/Composer;I)Landroidx/compose/material/RippleConfiguration;
845-
}
846-
847841
public final class io/getstream/video/android/compose/theme/StreamShapes {
848842
public static final field $stable I
849843
public static final field Companion Lio/getstream/video/android/compose/theme/StreamShapes$Companion;
@@ -878,9 +872,8 @@ public final class io/getstream/video/android/compose/theme/StreamShapes$Compani
878872
public abstract interface class io/getstream/video/android/compose/theme/StreamTheme {
879873
public fun getColors (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/StreamColors;
880874
public fun getComponentFactory (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/VideoComponentFactory;
875+
public fun getConfig (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/VideoUiConfig;
881876
public fun getDimens (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/StreamDimens;
882-
public fun getReactionMapper (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/core/mapper/ReactionMapper;
883-
public fun getRippleConfiguration (Landroidx/compose/runtime/Composer;I)Landroidx/compose/material/RippleConfiguration;
884877
public fun getShapes (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/StreamShapes;
885878
public fun getStyles (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/ui/components/base/styling/CompositeStyleProvider;
886879
public fun getTypography (Landroidx/compose/runtime/Composer;I)Lio/getstream/video/android/compose/theme/StreamTypography;
@@ -972,8 +965,25 @@ public final class io/getstream/video/android/compose/theme/VideoTheme : io/gets
972965
}
973966

974967
public final class io/getstream/video/android/compose/theme/VideoThemeKt {
975-
public static final fun VideoTheme (ZLio/getstream/video/android/compose/theme/StreamColors;Lio/getstream/video/android/compose/theme/StreamDimens;Lio/getstream/video/android/compose/theme/StreamTypography;Lio/getstream/video/android/compose/theme/StreamShapes;Lio/getstream/video/android/compose/theme/StreamRippleConfiguration;Lio/getstream/video/android/core/mapper/ReactionMapper;ZLio/getstream/video/android/compose/ui/components/base/styling/CompositeStyleProvider;Lio/getstream/video/android/compose/theme/VideoComponentFactory;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;III)V
968+
public static final fun VideoTheme (Lio/getstream/video/android/compose/theme/VideoUiConfig;Lio/getstream/video/android/compose/theme/StreamColors;Lio/getstream/video/android/compose/theme/StreamDimens;Lio/getstream/video/android/compose/theme/StreamTypography;Lio/getstream/video/android/compose/theme/StreamShapes;Lio/getstream/video/android/compose/ui/components/base/styling/CompositeStyleProvider;Lio/getstream/video/android/compose/theme/VideoComponentFactory;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
976969
public static final fun getLocalComponentFactory ()Landroidx/compose/runtime/ProvidableCompositionLocal;
970+
public static final fun getLocalVideoUiConfig ()Landroidx/compose/runtime/ProvidableCompositionLocal;
971+
}
972+
973+
public final class io/getstream/video/android/compose/theme/VideoUiConfig {
974+
public static final field $stable I
975+
public fun <init> ()V
976+
public fun <init> (ZLio/getstream/video/android/core/mapper/ReactionMapper;)V
977+
public synthetic fun <init> (ZLio/getstream/video/android/core/mapper/ReactionMapper;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
978+
public final fun component1 ()Z
979+
public final fun component2 ()Lio/getstream/video/android/core/mapper/ReactionMapper;
980+
public final fun copy (ZLio/getstream/video/android/core/mapper/ReactionMapper;)Lio/getstream/video/android/compose/theme/VideoUiConfig;
981+
public static synthetic fun copy$default (Lio/getstream/video/android/compose/theme/VideoUiConfig;ZLio/getstream/video/android/core/mapper/ReactionMapper;ILjava/lang/Object;)Lio/getstream/video/android/compose/theme/VideoUiConfig;
982+
public fun equals (Ljava/lang/Object;)Z
983+
public final fun getAllowUIAutomationTest ()Z
984+
public final fun getReactionMapper ()Lio/getstream/video/android/core/mapper/ReactionMapper;
985+
public fun hashCode ()I
986+
public fun toString ()Ljava/lang/String;
977987
}
978988

979989
public final class io/getstream/video/android/compose/ui/ComposableSingletons$StreamCallActivityComposeDelegateKt {

stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/theme/StreamRippleConfiguration.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ import androidx.compose.runtime.ReadOnlyComposable
3030
* A modified version of the default [RippleConfiguration] from [MaterialTheme] which
3131
* works in case the [MaterialTheme] is not initialized.
3232
*/
33-
public object StreamRippleConfiguration {
33+
internal object StreamRippleConfiguration {
3434

3535
@Composable
3636
@ReadOnlyComposable
37-
public fun default(): RippleConfiguration {
37+
fun default(): RippleConfiguration {
3838
val rippleConfiguration = LocalRippleConfiguration.current
3939
if (rippleConfiguration != null) return rippleConfiguration
4040

stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/theme/VideoTheme.kt

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@
1818

1919
package io.getstream.video.android.compose.theme
2020

21-
import androidx.compose.foundation.isSystemInDarkTheme
2221
import androidx.compose.foundation.layout.Box
2322
import androidx.compose.material.ExperimentalMaterialApi
2423
import androidx.compose.material.LocalRippleConfiguration
25-
import androidx.compose.material.RippleConfiguration
2624
import androidx.compose.runtime.Composable
2725
import androidx.compose.runtime.CompositionLocalProvider
2826
import androidx.compose.runtime.LaunchedEffect
@@ -36,7 +34,6 @@ import androidx.compose.ui.semantics.testTagsAsResourceId
3634
import io.getstream.video.android.compose.ui.components.base.styling.CompositeStyleProvider
3735
import io.getstream.video.android.core.header.HeadersUtil
3836
import io.getstream.video.android.core.header.VersionPrefixHeader
39-
import io.getstream.video.android.core.mapper.ReactionMapper
4037

4138
/**
4239
* Local providers for various properties we connect to our components, for styling.
@@ -56,12 +53,6 @@ private val LocalTypography = compositionLocalOf<StreamTypography> {
5653
private val LocalShapes = compositionLocalOf<StreamShapes> {
5754
error("No shapes provided! Make sure to wrap all usages of Stream components in a VideoTheme.")
5855
}
59-
private val LocalReactionMapper = compositionLocalOf<ReactionMapper> {
60-
error(
61-
"No reaction mapper provided! Make sure to wrap all usages of Stream components in a VideoTheme.",
62-
)
63-
}
64-
6556
private val LocalStyles = compositionLocalOf<CompositeStyleProvider> {
6657
error(
6758
"No styles provided! Make sure to wrap all usages of Stream components in a VideoTheme.",
@@ -79,31 +70,36 @@ public val LocalComponentFactory: ProvidableCompositionLocal<VideoComponentFacto
7970
)
8071
}
8172

73+
/**
74+
* The local composition containing the current [VideoUiConfig].
75+
*/
76+
public val LocalVideoUiConfig: ProvidableCompositionLocal<VideoUiConfig> =
77+
compositionLocalOf {
78+
error(
79+
"No VideoUiConfig provided! Make sure to wrap all usages of Stream components " +
80+
"in a VideoTheme.",
81+
)
82+
}
83+
8284
/**
8385
* Our theme that provides all the important properties for styling to the user.
8486
*
85-
* @param isInDarkMode If we're currently in the dark mode or not. Affects only the default color palette that's
86-
* provided. If you customize [colors], make sure to add your own logic for dark/light colors.
87+
* @param config Central behavioral configuration for the Video SDK. See [VideoUiConfig].
8788
* @param colors The set of colors we provide, wrapped in [StreamColors].
8889
* @param dimens The set of dimens we provide, wrapped in [StreamDimens].
8990
* @param typography The set of typography styles we provide, wrapped in [StreamTypography].
9091
* @param shapes The set of shapes we provide, wrapped in [StreamShapes].
91-
* @param rippleConfiguration Defines the appearance for ripples.
92-
* @param reactionMapper Defines a mapper of the emoji code from the reaction events.
9392
* @param componentFactory Provide to customize the components used throughout the UI.
9493
* @param content The content shown within the theme wrapper.
9594
*/
9695
@Composable
9796
@OptIn(ExperimentalMaterialApi::class)
9897
public fun VideoTheme(
99-
isInDarkMode: Boolean = isSystemInDarkTheme(),
98+
config: VideoUiConfig = VideoUiConfig(),
10099
colors: StreamColors = StreamColors.defaultColors(),
101100
dimens: StreamDimens = StreamDimens.defaultDimens(),
102101
typography: StreamTypography = StreamTypography.defaultTypography(colors, dimens),
103102
shapes: StreamShapes = StreamShapes.defaultShapes(dimens),
104-
rippleConfiguration: StreamRippleConfiguration = StreamRippleConfiguration,
105-
reactionMapper: ReactionMapper = ReactionMapper.defaultReactionMapper(),
106-
allowUIAutomationTest: Boolean = true,
107103
styles: CompositeStyleProvider = CompositeStyleProvider(),
108104
componentFactory: VideoComponentFactory = DefaultVideoComponentFactory,
109105
content: @Composable () -> Unit,
@@ -112,27 +108,33 @@ public fun VideoTheme(
112108
HeadersUtil.VERSION_PREFIX_HEADER = VersionPrefixHeader.Compose
113109
}
114110
CompositionLocalProvider(
111+
LocalVideoUiConfig provides config,
115112
LocalColors provides colors,
116113
LocalDimens provides dimens,
117114
LocalTypography provides typography,
118115
LocalShapes provides shapes,
119-
LocalRippleConfiguration provides rippleConfiguration.default(),
120-
LocalReactionMapper provides reactionMapper,
116+
LocalRippleConfiguration provides StreamRippleConfiguration.default(),
121117
LocalStyles provides styles,
122118
LocalComponentFactory provides componentFactory,
123119
) {
124120
Box(
125121
modifier = Modifier.semantics {
126-
testTagsAsResourceId = allowUIAutomationTest
122+
testTagsAsResourceId = config.allowUIAutomationTest
127123
},
128124
) {
129125
content()
130126
}
131127
}
132128
}
133129

134-
@OptIn(ExperimentalMaterialApi::class)
135130
public interface StreamTheme {
131+
/**
132+
* Retrieves the current [VideoUiConfig] at the call site's position in the hierarchy.
133+
*/
134+
public val config: VideoUiConfig
135+
@Composable @ReadOnlyComposable
136+
get() = LocalVideoUiConfig.current
137+
136138
/**
137139
* Retrieves the current [StreamColors] at the call site's position in the hierarchy.
138140
*/
@@ -162,21 +164,7 @@ public interface StreamTheme {
162164
get() = LocalShapes.current
163165

164166
/**
165-
* Retrieves the current [RippleConfiguration] at the call site's position in the hierarchy.
166-
*/
167-
public val rippleConfiguration: RippleConfiguration?
168-
@Composable @ReadOnlyComposable
169-
get() = StreamRippleConfiguration.default()
170-
171-
/**
172-
* Retrieves the current [ReactionMapper] at the call site's position in the hierarchy.
173-
*/
174-
public val reactionMapper: ReactionMapper
175-
@Composable @ReadOnlyComposable
176-
get() = LocalReactionMapper.current
177-
178-
/**
179-
* Retrieves the current [ReactionMapper] at the call site's position in the hierarchy.
167+
* Retrieves the current [CompositeStyleProvider] at the call site's position in the hierarchy.
180168
*/
181169
public val styles: CompositeStyleProvider
182170
@Composable @ReadOnlyComposable
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.getstream.video.android.compose.theme
18+
19+
import io.getstream.video.android.core.mapper.ReactionMapper
20+
21+
/**
22+
* Central behavioral configuration for the Video SDK, accessible through [VideoTheme.config].
23+
*
24+
* Groups the behavior settings that are unrelated to styling, so [VideoTheme] keeps a small
25+
* parameter list of visual properties while feature behavior is configured in a single place.
26+
*
27+
* @param allowUIAutomationTest Whether the Compose test tags used by the SDK are exposed as
28+
* resource ids for UI automation tools.
29+
* @param reactionMapper Maps the emoji code from reaction events to the emoji shown in the UI.
30+
*/
31+
public data class VideoUiConfig(
32+
val allowUIAutomationTest: Boolean = true,
33+
val reactionMapper: ReactionMapper = ReactionMapper.defaultReactionMapper(),
34+
)

stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ internal fun BoxScope.DefaultReaction(
580580

581581
val emojiCode = currentReaction?.response?.emojiCode
582582
if (currentReaction != null && emojiCode != null) {
583-
val emojiMapper = VideoTheme.reactionMapper
583+
val emojiMapper = VideoTheme.config.reactionMapper
584584
val emojiText = emojiMapper.map(emojiCode)
585585
Text(
586586
text = emojiText,

stream-video-android-ui-compose/src/test/kotlin/io/getstream/video/android/compose/ui/PaparazziComposeTest.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,16 @@ internal interface PaparazziComposeTest {
7878
val paparazzi: Paparazzi
7979

8080
fun snapshot(
81+
// Palette selector, currently a no-op: VideoTheme has a single dark palette until
82+
// StreamDesign lands (AND-1419). Kept so the call sites don't churn twice.
8183
isInDarkMode: Boolean = false,
8284
contentAlignment: Alignment = Alignment.TopStart,
8385
backgroundColor: Color = Color.Unspecified,
8486
composable: @Composable () -> Unit,
8587
) {
8688
paparazzi.snapshot {
8789
TestEnvironment {
88-
VideoTheme(isInDarkMode = isInDarkMode) {
90+
VideoTheme {
8991
Box(
9092
modifier = Modifier
9193
.background(
@@ -112,7 +114,7 @@ internal interface PaparazziComposeTest {
112114
paparazzi.snapshot {
113115
TestEnvironment {
114116
Column {
115-
VideoTheme(isInDarkMode = true) {
117+
VideoTheme {
116118
Box(
117119
modifier = Modifier
118120
.weight(weight = .5f, fill = false)
@@ -124,7 +126,7 @@ internal interface PaparazziComposeTest {
124126
composable()
125127
}
126128
}
127-
VideoTheme(isInDarkMode = false) {
129+
VideoTheme {
128130
Box(
129131
modifier = Modifier
130132
.weight(weight = .5f, fill = false)
@@ -149,7 +151,7 @@ internal interface PaparazziComposeTest {
149151
paparazzi.snapshot {
150152
TestEnvironment {
151153
Row {
152-
VideoTheme(isInDarkMode = true) {
154+
VideoTheme {
153155
Box(
154156
modifier = Modifier
155157
.weight(weight = .5f, fill = false)
@@ -161,7 +163,7 @@ internal interface PaparazziComposeTest {
161163
composable()
162164
}
163165
}
164-
VideoTheme(isInDarkMode = false) {
166+
VideoTheme {
165167
Box(
166168
modifier = Modifier
167169
.weight(weight = .5f, fill = false)

0 commit comments

Comments
 (0)