Expose glanceable hub transition state

This CL exposes the full transition state of the glanceable hub's
SceneTransitionLayout so that other pieces of SysUI can use it for
transitions.

Bug: 315203819
Bug: 315490861
Fixed: 315203819
Test: atest CommunalRepositoryImplTest
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT
Change-Id: Ic496a40657ec6cd7e7c548d703de2c8be01d2788
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 185a06c..d83f3aa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -14,6 +14,7 @@
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -28,15 +29,20 @@
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.FixedSizeEdgeDetector
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.transform
 
 object Communal {
@@ -60,7 +66,7 @@
  * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture
  * handling and transitions before the full Flexiglass layout is ready.
  */
-@OptIn(ExperimentalComposeUiApi::class)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class)
 @Composable
 fun CommunalContainer(
     modifier: Modifier = Modifier,
@@ -81,6 +87,15 @@
         return
     }
 
+    // This effect exposes the SceneTransitionLayout's observable transition state to the rest of
+    // the system, and unsets it when the view is disposed to avoid a memory leak.
+    DisposableEffect(viewModel, sceneTransitionLayoutState) {
+        viewModel.setTransitionState(
+            sceneTransitionLayoutState.observableTransitionState().map { it.toModel() }
+        )
+        onDispose { viewModel.setTransitionState(null) }
+    }
+
     Box(modifier = modifier.fillMaxSize()) {
         SceneTransitionLayout(
             modifier = Modifier.fillMaxSize(),
@@ -171,18 +186,40 @@
     Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
 }
 
-// TODO(b/293899074): Remove these conversions once Compose can be used throughout SysUI.
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
 object TransitionSceneKey {
     val Blank = CommunalSceneKey.Blank.toTransitionSceneKey()
     val Communal = CommunalSceneKey.Communal.toTransitionSceneKey()
 }
 
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
+fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
+    return this.identity as CommunalSceneKey
+}
+
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
 fun CommunalSceneKey.toTransitionSceneKey(): SceneKey {
     return SceneKey(name = toString(), identity = this)
 }
 
-fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
-    return this.identity as CommunalSceneKey
+/**
+ * Converts between the [SceneTransitionLayout] state class and our forked data class that can be
+ * used throughout SysUI.
+ */
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
+fun ObservableTransitionState.toModel(): ObservableCommunalTransitionState {
+    return when (this) {
+        is ObservableTransitionState.Idle ->
+            ObservableCommunalTransitionState.Idle(scene.toCommunalSceneKey())
+        is ObservableTransitionState.Transition ->
+            ObservableCommunalTransitionState.Transition(
+                fromScene = fromScene.toCommunalSceneKey(),
+                toScene = toScene.toCommunalSceneKey(),
+                progress = progress,
+                isInitiatedByUserInput = isInitiatedByUserInput,
+                isUserInputOngoing = isUserInputOngoing,
+            )
+    }
 }
 
 object ContainerDimensions {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 7196de6..65176e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -20,6 +20,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
@@ -29,6 +30,8 @@
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -40,29 +43,30 @@
 class CommunalRepositoryImplTest : SysuiTestCase() {
     private lateinit var underTest: CommunalRepositoryImpl
 
-    private lateinit var testScope: TestScope
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
 
     private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
-    private lateinit var sceneContainerFlags: FakeSceneContainerFlags
     private lateinit var sceneContainerRepository: SceneContainerRepository
 
     @Before
     fun setUp() {
-        testScope = TestScope()
-
         val sceneTestUtils = SceneTestUtils(this)
-        sceneContainerFlags = FakeSceneContainerFlags(enabled = false)
         sceneContainerRepository = sceneTestUtils.fakeSceneContainerRepository()
         featureFlagsClassic = FakeFeatureFlagsClassic()
 
         featureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
 
-        underTest =
-            CommunalRepositoryImpl(
-                featureFlagsClassic,
-                sceneContainerFlags,
-                sceneContainerRepository,
-            )
+        underTest = createRepositoryImpl(false)
+    }
+
+    private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl {
+        return CommunalRepositoryImpl(
+            testScope.backgroundScope,
+            featureFlagsClassic,
+            FakeSceneContainerFlags(enabled = sceneContainerEnabled),
+            sceneContainerRepository,
+        )
     }
 
     @Test
@@ -86,13 +90,7 @@
     @Test
     fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
         testScope.runTest {
-            sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
-            underTest =
-                CommunalRepositoryImpl(
-                    featureFlagsClassic,
-                    sceneContainerFlags,
-                    sceneContainerRepository,
-                )
+            underTest = createRepositoryImpl(true)
 
             sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Communal))
 
@@ -103,17 +101,49 @@
     @Test
     fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
         testScope.runTest {
-            sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
-            underTest =
-                CommunalRepositoryImpl(
-                    featureFlagsClassic,
-                    sceneContainerFlags,
-                    sceneContainerRepository,
-                )
+            underTest = createRepositoryImpl(true)
 
             sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Lockscreen))
 
             val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
             assertThat(isCommunalHubShowing).isFalse()
         }
+
+    @Test
+    fun transitionState_idleByDefault() =
+        testScope.runTest {
+            val transitionState by collectLastValue(underTest.transitionState)
+            assertThat(transitionState)
+                .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+        }
+
+    @Test
+    fun transitionState_setTransitionState_returnsNewValue() =
+        testScope.runTest {
+            val expectedSceneKey = CommunalSceneKey.Communal
+            underTest.setTransitionState(
+                flowOf(ObservableCommunalTransitionState.Idle(expectedSceneKey))
+            )
+
+            val transitionState by collectLastValue(underTest.transitionState)
+            assertThat(transitionState)
+                .isEqualTo(ObservableCommunalTransitionState.Idle(expectedSceneKey))
+        }
+
+    @Test
+    fun transitionState_setNullTransitionState_returnsDefaultValue() =
+        testScope.runTest {
+            // Set a value for the transition state flow.
+            underTest.setTransitionState(
+                flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal))
+            )
+
+            // Set the transition state flow back to null.
+            underTest.setTransitionState(null)
+
+            // Flow returns default scene key.
+            val transitionState by collectLastValue(underTest.transitionState)
+            assertThat(transitionState)
+                .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 3119b9e..1f4be40 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -18,18 +18,26 @@
 
 import com.android.systemui.Flags.communalHub
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.scene.data.repository.SceneContainerRepository
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates the state of communal mode. */
 interface CommunalRepository {
@@ -45,14 +53,26 @@
      */
     val desiredScene: StateFlow<CommunalSceneKey>
 
+    /** Exposes the transition state of the communal [SceneTransitionLayout]. */
+    val transitionState: StateFlow<ObservableCommunalTransitionState>
+
     /** Updates the requested scene. */
     fun setDesiredScene(desiredScene: CommunalSceneKey)
+
+    /**
+     * Updates the transition state of the hub [SceneTransitionLayout].
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?)
 }
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class CommunalRepositoryImpl
 @Inject
 constructor(
+    @Background backgroundScope: CoroutineScope,
     private val featureFlagsClassic: FeatureFlagsClassic,
     sceneContainerFlags: SceneContainerFlags,
     sceneContainerRepository: SceneContainerRepository,
@@ -61,13 +81,34 @@
         get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
 
     private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
-        MutableStateFlow(CommunalSceneKey.Blank)
+        MutableStateFlow(CommunalSceneKey.DEFAULT)
     override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
 
+    private val defaultTransitionState =
+        ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
+    private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
+    override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+        _transitionState
+            .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
+            .stateIn(
+                scope = backgroundScope,
+                started = SharingStarted.Lazily,
+                initialValue = defaultTransitionState,
+            )
+
     override fun setDesiredScene(desiredScene: CommunalSceneKey) {
         _desiredScene.value = desiredScene
     }
 
+    /**
+     * Updates the transition state of the hub [SceneTransitionLayout].
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+        _transitionState.value = transitionState
+    }
+
     override val isCommunalHubShowing: Flow<Boolean> =
         if (sceneContainerFlags.isEnabled()) {
             sceneContainerRepository.desiredScene.map { scene -> scene.key == SceneKey.Communal }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index e630fd4..1a2a425 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -62,6 +63,19 @@
      */
     val desiredScene: StateFlow<CommunalSceneKey> = communalRepository.desiredScene
 
+    /** Transition state of the hub mode. */
+    val transitionState: StateFlow<ObservableCommunalTransitionState> =
+        communalRepository.transitionState
+
+    /**
+     * Updates the transition state of the hub [SceneTransitionLayout].
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+        communalRepository.setTransitionState(transitionState)
+    }
+
     /**
      * Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
      * [CommunalSceneKey.Communal].
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
index 2be909c..c68dd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
@@ -29,4 +29,8 @@
     override fun toString(): String {
         return loggingName
     }
+
+    companion object {
+        val DEFAULT = Blank
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
new file mode 100644
index 0000000..d834715
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * This is a fork of the `com.android.compose.animation.scene.ObservableTransitionState` class.
+ *
+ * TODO(b/315490861): remove this fork, once we can compile Compose into System UI.
+ */
+sealed class ObservableCommunalTransitionState {
+    /** No transition/animation is currently running. */
+    data class Idle(val scene: CommunalSceneKey) : ObservableCommunalTransitionState()
+
+    /** There is a transition animating between two scenes. */
+    data class Transition(
+        val fromScene: CommunalSceneKey,
+        val toScene: CommunalSceneKey,
+        val progress: Flow<Float>,
+
+        /**
+         * Whether the transition was originally triggered by user input rather than being
+         * programmatic. If this value is initially true, it will remain true until the transition
+         * fully completes, even if the user input that triggered the transition has ended. Any
+         * sub-transitions launched by this one will inherit this value. For example, if the user
+         * drags a pointer but does not exceed the threshold required to transition to another
+         * scene, this value will remain true after the pointer is no longer touching the screen and
+         * will be true in any transition created to animate back to the original position.
+         */
+        val isInitiatedByUserInput: Boolean,
+
+        /**
+         * Whether user input is currently driving the transition. For example, if a user is
+         * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+         * the transition completes/settles.
+         */
+        val isUserInputOngoing: Flow<Boolean>,
+    ) : ObservableCommunalTransitionState()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index bed4283..ab9ffd8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.shade.ShadeViewController
 import kotlinx.coroutines.flow.Flow
@@ -42,6 +43,15 @@
         communalInteractor.onSceneChanged(scene)
     }
 
+    /**
+     * Updates the transition state of the hub [SceneTransitionLayout].
+     *
+     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     */
+    fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+        communalInteractor.setTransitionState(transitionState)
+    }
+
     // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
     //  touches anymore.
     /** Called when a touch is received outside the edge swipe area when hub mode is closed. */
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index 2cb17b5..c85c27e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,19 +1,47 @@
 package com.android.systemui.communal.data.repository
 
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.dagger.qualifiers.Background
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.test.TestScope
 
 /** Fake implementation of [CommunalRepository]. */
+@OptIn(ExperimentalCoroutinesApi::class)
 class FakeCommunalRepository(
+    @Background applicationScope: CoroutineScope = TestScope(),
     override var isCommunalEnabled: Boolean = false,
     override val desiredScene: MutableStateFlow<CommunalSceneKey> =
-        MutableStateFlow(CommunalSceneKey.Blank)
+        MutableStateFlow(CommunalSceneKey.DEFAULT),
 ) : CommunalRepository {
     override fun setDesiredScene(desiredScene: CommunalSceneKey) {
         this.desiredScene.value = desiredScene
     }
 
+    private val defaultTransitionState =
+        ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
+    private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
+    override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+        _transitionState
+            .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = defaultTransitionState,
+            )
+
+    override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+        _transitionState.value = transitionState
+    }
+
     fun setIsCommunalEnabled(value: Boolean) {
         isCommunalEnabled = value
     }