Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ constructor( shadeExpansionCollectorJob = scope.launch { // wait for it to emit true once shadeInteractorLazy.get().anyExpanding.first { it } shadeInteractorLazy.get().isAnyExpanding.first { it } onShadeInteraction.run() } shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null } Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +56 −2 Original line number Diff line number Diff line Loading @@ -35,15 +35,20 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.isActive import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.isActive /** Business logic for shade interactions. */ @OptIn(ExperimentalCoroutinesApi::class) Loading Loading @@ -119,18 +124,41 @@ constructor( repository.qsExpansion } /** The amount [0-1] either QS or the shade has been opened */ /** The amount [0-1] either QS or the shade has been opened. */ val anyExpansion: StateFlow<Float> = combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) } .stateIn(scope, SharingStarted.Eagerly, 0f) /** Whether either the shade or QS is expanding from a fully collapsed state. */ val anyExpanding = val isAnyExpanding = anyExpansion .pairwise(1f) .map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f } .distinctUntilChanged() /** * Whether the user is expanding or collapsing the shade with user input. This will be true even * if the user's input gesture has ended but a transition they initiated is animating. */ val isUserInteractingWithShade: Flow<Boolean> = userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion) /** * Whether the user is expanding or collapsing quick settings with user input. This will be true * even if the user's input gesture has ended but a transition they initiated is still * animating. */ val isUserInteractingWithQs: Flow<Boolean> = userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion) /** * Whether the user is expanding or collapsing either the shade or quick settings with user * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended * but a transition they initiated is still animating. */ val isUserInteracting: Flow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = combine( Loading Loading @@ -169,4 +197,30 @@ constructor( } } .distinctUntilChanged() /** * Return a flow for whether a user is interacting with an expandable shade component using * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that * [expansion.first] checks the current value of the flow. */ private fun userInteractingFlow( tracking: Flow<Boolean>, expansion: StateFlow<Float> ): Flow<Boolean> { return flow { // initial value is false emit(false) while (currentCoroutineContext().isActive) { // wait for tracking to become true tracking.first { it } emit(true) // wait for tracking to become false tracking.first { !it } // wait for expansion to complete in either direction expansion.first { it <= 0f || it >= 1f } // interaction complete emit(false) } } } } packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +234 −6 Original line number Diff line number Diff line Loading @@ -402,6 +402,7 @@ class ShadeInteractorTest : SysuiTestCase() { assertThat(actual).isEqualTo(0.8f) } @Test fun shadeExpansionWhenInSplitShadeAndQsExpanded() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) Loading @@ -410,27 +411,31 @@ class ShadeInteractorTest : SysuiTestCase() { keyguardRepository.setStatusBarState(StatusBarState.SHADE) overrideResource(R.bool.config_use_split_notification_shade, true) configurationRepository.onAnyConfigurationChange() runCurrent() shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(.7f) runCurrent() // THEN legacy shade expansion is passed through assertThat(actual).isEqualTo(.7f) } @Test fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) overrideResource(R.bool.config_use_split_notification_shade, false) shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN shade expansion is zero assertThat(actual).isEqualTo(0f) } @Test fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) Loading Loading @@ -471,7 +476,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedDown_expandingTrue() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade and QS collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -489,7 +494,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_qsDraggedDown_expandingTrue() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade and QS collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -507,7 +512,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // WHEN shade starts collapsed then partially expanded shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -532,7 +537,7 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN anyExpanding is still true assertThat(actual).isTrue() // WHEN shade fully shadeExpanded // WHEN shade fully expanded shadeRepository.setLegacyShadeExpansion(1f) runCurrent() Loading @@ -550,7 +555,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedDownThenUp_expandingFalse() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade starts collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading Loading @@ -708,4 +713,227 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN expansion is still 0 assertThat(expansionAmount).isEqualTo(0f) } @Test fun userInteractingWithShade_shadeDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged down halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully expanded but tracking is not stopped shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully collapsed but tracking is not stopped shadeRepository.setLegacyShadeExpansion(0f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged halfway and tracking is stopped shadeRepository.setLegacyShadeExpansion(.6f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade completes expansion stopped shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadeExpanded() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged down halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully expanded and tracking is stopped shadeRepository.setLegacyShadeExpansion(1f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadePartiallyExpanded() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade partially expanded shadeRepository.setLegacyShadeExpansion(.4f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN tracking is stopped shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade goes back to collapsed shadeRepository.setLegacyShadeExpansion(0f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadeCollapsed() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade expanded and not tracking input shadeRepository.setLegacyShadeExpansion(1f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged up halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully collapsed and tracking is stopped shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithQs_qsDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithQs) // GIVEN qs collapsed and not tracking input shadeRepository.setQsExpansion(0f) shadeRepository.setLegacyQsTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN qs tracking starts shadeRepository.setLegacyQsTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs dragged down halfway shadeRepository.setQsExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs fully expanded but tracking is not stopped shadeRepository.setQsExpansion(1f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs fully collapsed but tracking is not stopped shadeRepository.setQsExpansion(0f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs dragged halfway and tracking is stopped shadeRepository.setQsExpansion(.6f) shadeRepository.setLegacyQsTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs completes expansion stopped shadeRepository.setQsExpansion(1f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ constructor( shadeExpansionCollectorJob = scope.launch { // wait for it to emit true once shadeInteractorLazy.get().anyExpanding.first { it } shadeInteractorLazy.get().isAnyExpanding.first { it } onShadeInteraction.run() } shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null } Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +56 −2 Original line number Diff line number Diff line Loading @@ -35,15 +35,20 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.isActive import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.isActive /** Business logic for shade interactions. */ @OptIn(ExperimentalCoroutinesApi::class) Loading Loading @@ -119,18 +124,41 @@ constructor( repository.qsExpansion } /** The amount [0-1] either QS or the shade has been opened */ /** The amount [0-1] either QS or the shade has been opened. */ val anyExpansion: StateFlow<Float> = combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) } .stateIn(scope, SharingStarted.Eagerly, 0f) /** Whether either the shade or QS is expanding from a fully collapsed state. */ val anyExpanding = val isAnyExpanding = anyExpansion .pairwise(1f) .map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f } .distinctUntilChanged() /** * Whether the user is expanding or collapsing the shade with user input. This will be true even * if the user's input gesture has ended but a transition they initiated is animating. */ val isUserInteractingWithShade: Flow<Boolean> = userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion) /** * Whether the user is expanding or collapsing quick settings with user input. This will be true * even if the user's input gesture has ended but a transition they initiated is still * animating. */ val isUserInteractingWithQs: Flow<Boolean> = userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion) /** * Whether the user is expanding or collapsing either the shade or quick settings with user * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended * but a transition they initiated is still animating. */ val isUserInteracting: Flow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = combine( Loading Loading @@ -169,4 +197,30 @@ constructor( } } .distinctUntilChanged() /** * Return a flow for whether a user is interacting with an expandable shade component using * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that * [expansion.first] checks the current value of the flow. */ private fun userInteractingFlow( tracking: Flow<Boolean>, expansion: StateFlow<Float> ): Flow<Boolean> { return flow { // initial value is false emit(false) while (currentCoroutineContext().isActive) { // wait for tracking to become true tracking.first { it } emit(true) // wait for tracking to become false tracking.first { !it } // wait for expansion to complete in either direction expansion.first { it <= 0f || it >= 1f } // interaction complete emit(false) } } } }
packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +234 −6 Original line number Diff line number Diff line Loading @@ -402,6 +402,7 @@ class ShadeInteractorTest : SysuiTestCase() { assertThat(actual).isEqualTo(0.8f) } @Test fun shadeExpansionWhenInSplitShadeAndQsExpanded() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) Loading @@ -410,27 +411,31 @@ class ShadeInteractorTest : SysuiTestCase() { keyguardRepository.setStatusBarState(StatusBarState.SHADE) overrideResource(R.bool.config_use_split_notification_shade, true) configurationRepository.onAnyConfigurationChange() runCurrent() shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(.7f) runCurrent() // THEN legacy shade expansion is passed through assertThat(actual).isEqualTo(.7f) } @Test fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) overrideResource(R.bool.config_use_split_notification_shade, false) shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN shade expansion is zero assertThat(actual).isEqualTo(0f) } @Test fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() = testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) Loading Loading @@ -471,7 +476,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedDown_expandingTrue() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade and QS collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -489,7 +494,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_qsDraggedDown_expandingTrue() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade and QS collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -507,7 +512,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // WHEN shade starts collapsed then partially expanded shadeRepository.setLegacyShadeExpansion(0f) Loading @@ -532,7 +537,7 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN anyExpanding is still true assertThat(actual).isTrue() // WHEN shade fully shadeExpanded // WHEN shade fully expanded shadeRepository.setLegacyShadeExpansion(1f) runCurrent() Loading @@ -550,7 +555,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun expanding_shadeDraggedDownThenUp_expandingFalse() = testScope.runTest() { val actual by collectLastValue(underTest.anyExpanding) val actual by collectLastValue(underTest.isAnyExpanding) // GIVEN shade starts collapsed shadeRepository.setLegacyShadeExpansion(0f) Loading Loading @@ -708,4 +713,227 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN expansion is still 0 assertThat(expansionAmount).isEqualTo(0f) } @Test fun userInteractingWithShade_shadeDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged down halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully expanded but tracking is not stopped shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully collapsed but tracking is not stopped shadeRepository.setLegacyShadeExpansion(0f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged halfway and tracking is stopped shadeRepository.setLegacyShadeExpansion(.6f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade completes expansion stopped shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadeExpanded() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged down halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully expanded and tracking is stopped shadeRepository.setLegacyShadeExpansion(1f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadePartiallyExpanded() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade partially expanded shadeRepository.setLegacyShadeExpansion(.4f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN tracking is stopped shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade goes back to collapsed shadeRepository.setLegacyShadeExpansion(0f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithShade_shadeCollapsed() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade expanded and not tracking input shadeRepository.setLegacyShadeExpansion(1f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN shade tracking starts shadeRepository.setLegacyShadeTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade dragged up halfway shadeRepository.setLegacyShadeExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN shade fully collapsed and tracking is stopped shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setLegacyShadeTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteractingWithQs_qsDraggedUpAndDown() = testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithQs) // GIVEN qs collapsed and not tracking input shadeRepository.setQsExpansion(0f) shadeRepository.setLegacyQsTracking(false) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() // WHEN qs tracking starts shadeRepository.setLegacyQsTracking(true) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs dragged down halfway shadeRepository.setQsExpansion(.5f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs fully expanded but tracking is not stopped shadeRepository.setQsExpansion(1f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs fully collapsed but tracking is not stopped shadeRepository.setQsExpansion(0f) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs dragged halfway and tracking is stopped shadeRepository.setQsExpansion(.6f) shadeRepository.setLegacyQsTracking(false) runCurrent() // THEN user is interacting assertThat(actual).isTrue() // WHEN qs completes expansion stopped shadeRepository.setQsExpansion(1f) runCurrent() // THEN user is not interacting assertThat(actual).isFalse() } }