diff options
| author | 2024-06-10 14:38:20 +0000 | |
|---|---|---|
| committer | 2024-06-10 14:38:20 +0000 | |
| commit | c47ff374772d47a2570f96cbc54dd90884fb4446 (patch) | |
| tree | ab4990be47c175e85d685a20b630d4dc9e9145ca | |
| parent | 8d16528ff18cd318be4c2dd6de134a566170fba2 (diff) | |
| parent | 07ce7a6bc3438b1a4efd230d32cc1fd39dab5266 (diff) | |
Merge "Transform and consolidate transitionValue() API" into main
8 files changed, 130 insertions, 100 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 49df345397d4..194f362d984c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -39,7 +39,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil @@ -290,6 +292,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) // Make sure the value hasn't changed since we're GONE keyguardRepository.topClippingBounds.value = 5 assertThat(topClippingBounds).isEqualTo(1000) @@ -518,11 +521,14 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() to = KeyguardState.GONE, testScope = testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) assertThat(alpha).isEqualTo(0f) - // Try pulling down shade and ensure the value doesn't change - shadeTestUtil.setQsExpansion(0.5f) - assertThat(alpha).isEqualTo(0f) + if (!SceneContainerFlag.isEnabled) { + // Try pulling down shade and ensure the value doesn't change + shadeTestUtil.setQsExpansion(0.5f) + assertThat(alpha).isEqualTo(0f) + } } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 881ce413a022..92e6b16e5001 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -21,16 +21,19 @@ package com.android.systemui.scene.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys -import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource @@ -54,48 +57,27 @@ class SceneInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val fakeSceneDataSource = kosmos.fakeSceneDataSource - private lateinit var underTest: SceneInteractor + private val underTest = kosmos.sceneInteractor @Test fun allSceneKeys() { - underTest = kosmos.sceneInteractor assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys) } @Test fun changeScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) + val unknownScene = SceneKey("UNKNOWN") val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.changeScene(Scenes.Bouncer, "reason") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.changeScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun changeScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -106,8 +88,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun changeScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -122,15 +102,11 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun changeScene_toGoneWhenStillLocked_throws() = - testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.changeScene(Scenes.Gone, "reason") - } + testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") } @Test fun changeScene_toGoneWhenTransitionToLockedFromGone() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val transitionTo by collectLastValue(underTest.transitioningTo) kosmos.sceneContainerRepository.setTransitionState( @@ -155,7 +131,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun changeScene_toHomeSceneFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) underTest.changeScene(SceneFamilies.Home, "reason") @@ -167,37 +142,17 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun snapToScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.snapToScene(Scenes.Bouncer, "reason") + val unknownScene = SceneKey("UNKNOWN") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.snapToScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun snapToScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -208,8 +163,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun snapToScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -224,15 +177,11 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun snapToScene_toGoneWhenStillLocked_throws() = - testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.snapToScene(Scenes.Gone, "reason") - } + testScope.runTest { underTest.snapToScene(Scenes.Gone, "reason") } @Test fun snapToScene_toHomeSceneFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) underTest.snapToScene(SceneFamilies.Home, "reason") @@ -244,7 +193,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun sceneChanged_inDataSource() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -256,14 +204,14 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitionState() = testScope.runTest { - underTest = kosmos.sceneInteractor - val underTest = kosmos.sceneContainerRepository + val sceneContainerRepository = kosmos.sceneContainerRepository val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Lockscreen) ) - underTest.setTransitionState(transitionState) - val reflectedTransitionState by collectLastValue(underTest.transitionState) + sceneContainerRepository.setTransitionState(transitionState) + val reflectedTransitionState by + collectLastValue(sceneContainerRepository.transitionState) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) val progress = MutableStateFlow(1f) @@ -284,7 +232,7 @@ class SceneInteractorTest : SysuiTestCase() { progress.value = 0.9f assertThat(reflectedTransitionState).isEqualTo(transitionState.value) - underTest.setTransitionState(null) + sceneContainerRepository.setTransitionState(null) assertThat(reflectedTransitionState) .isEqualTo( ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey) @@ -294,7 +242,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitioningTo() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(underTest.currentScene.value) @@ -332,7 +279,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_idle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Shade) @@ -347,7 +293,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_transition_true() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -369,7 +314,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateMidTransition_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -403,7 +347,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateOnIdle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -429,7 +372,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isTrue() @@ -443,7 +385,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible_duringRemoteUserInteraction_forcedVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor underTest.setVisible(false, "reason") val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isFalse() @@ -458,7 +399,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun resolveSceneFamily_home() = testScope.runTest { - underTest = kosmos.sceneInteractor assertThat(underTest.resolveSceneFamily(SceneFamilies.Home)) .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene) } @@ -466,8 +406,46 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun resolveSceneFamily_nonFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor val resolved = underTest.resolveSceneFamily(Scenes.Gone).toList() assertThat(resolved).containsExactly(Scenes.Gone).inOrder() } + + @Test + fun transitionValue_test_idle() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + + kosmos.setSceneTransition(Idle(Scenes.Gone)) + assertThat(transitionValue).isEqualTo(1f) + + kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) + assertThat(transitionValue).isEqualTo(0f) + } + + @Test + fun transitionValue_test_transitions() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + val progress = MutableStateFlow(0f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Gone, progress = progress) + ) + assertThat(transitionValue).isEqualTo(0f) + + progress.value = 0.4f + assertThat(transitionValue).isEqualTo(0.4f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Gone, to = Scenes.Lockscreen, progress = progress) + ) + progress.value = 0.7f + assertThat(transitionValue).isEqualTo(0.3f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Shade, progress = progress) + ) + progress.value = 0.9f + assertThat(transitionValue).isEqualTo(0f) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 8ec460a7088f..1b201ceda310 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.animation.ValueAnimator.AnimatorUpdateListener import android.annotation.FloatRange +import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.tracing.coroutines.withContext @@ -117,10 +118,11 @@ class KeyguardTransitionRepositoryImpl constructor( @Main val mainDispatcher: CoroutineDispatcher, ) : KeyguardTransitionRepository { - /* - * Each transition between [KeyguardState]s will have an associated Flow. - * In order to collect these events, clients should call [transition]. + /** + * Each transition between [KeyguardState]s will have an associated Flow. In order to collect + * these events, clients should call [transition]. */ + @SuppressLint("SharedFlowCreation") private val _transitions = MutableSharedFlow<TransitionStep>( replay = 2, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 73835a3c1c96..48660f25907a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -50,7 +50,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.notification.NotificationUtils.interpolate import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample import javax.inject.Inject @@ -77,6 +77,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -91,7 +92,7 @@ constructor( bouncerRepository: KeyguardBouncerRepository, configurationInteractor: ConfigurationInteractor, shadeRepository: ShadeRepository, - keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, sceneInteractorProvider: Provider<SceneInteractor>, private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>, private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>, @@ -248,21 +249,17 @@ constructor( val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Keyguard can be clipped at the top as the shade is dragged */ - val topClippingBounds: Flow<Int?> = - combineTransform( - configurationInteractor.onAnyConfigurationChange, + val topClippingBounds: Flow<Int?> by lazy { + repository.topClippingBounds + .sampleFilter( keyguardTransitionInteractor - .transitionValue(GONE) - .map { it == 1f } - .onStart { emit(false) } - .distinctUntilChanged(), - repository.topClippingBounds - ) { _, isGone, topClippingBounds -> - if (!isGone) { - emit(topClippingBounds) - } + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .onStart { emit(0f) } + ) { goneValue -> + goneValue != 1f } .distinctUntilChanged() + } /** Last point that [KeyguardRootView] view was tapped */ val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 2766b71fae02..2c12945bb0cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -223,6 +223,17 @@ constructor( } } + fun transitionValue( + scene: SceneKey, + stateWithoutSceneContainer: KeyguardState, + ): Flow<Float> { + return if (SceneContainerFlag.isEnabled) { + sceneInteractor.get().transitionProgress(scene) + } else { + transitionValue(stateWithoutSceneContainer) + } + } + /** * The amount of transition into or out of the given [KeyguardState]. * @@ -232,6 +243,10 @@ constructor( fun transitionValue( state: KeyguardState, ): Flow<Float> { + if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) { + Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.") + return transitionValue(state.mapToSceneContainerScene()!!, state) + } return getTransitionValueFlow(state) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index ee52ad024e24..5027524e7a4b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -203,7 +203,7 @@ constructor( combine( communalInteractor.isIdleOnCommunal, keyguardTransitionInteractor - .transitionValue(GONE) + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f } .onStart { emit(false) }, keyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 2f66d6bb7be1..998537c98f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -156,6 +156,28 @@ constructor( ) /** + * The amount of transition into or out of the given [scene]. + * + * The value will be `0` if not in this scene or `1` when fully in the given scene. + */ + fun transitionProgress(scene: SceneKey): Flow<Float> { + return transitionState.flatMapLatest { transition -> + when (transition) { + is ObservableTransitionState.Idle -> { + flowOf(if (transition.currentScene == scene) 1f else 0f) + } + is ObservableTransitionState.Transition -> { + when { + transition.toScene == scene -> transition.progress + transition.fromScene == scene -> transition.progress.map { 1f - it } + else -> flowOf(0f) + } + } + } + } + } + + /** * Returns the keys of all scenes in the container. * * The scenes will be sorted in z-order such that the last one is the one that should be diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt index 405b57a1a04d..2f9fb0115eb7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt @@ -19,6 +19,8 @@ package com.android.systemui.util.kotlin import android.content.Context import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map class Utils { companion object { @@ -50,6 +52,14 @@ class Utils { ) /** + * Samples the provided flow, performs a filter on the sampled value, then returns the + * original value. + */ + fun <A, B> Flow<A>.sampleFilter(b: Flow<B>, predicate: (B) -> Boolean): Flow<A> { + return this.sample(b, ::Pair).filter { (_, b) -> predicate(b) }.map { (a, _) -> a } + } + + /** * Samples the provided flows, emitting a tuple of the original flow's value as well as each * of the combined flows' values. * |