summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Justin Weir <justinweir@google.com> 2023-09-18 21:08:37 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-09-18 21:08:37 +0000
commit1290bd665e6badb6093d19c99b0354db04a6b74d (patch)
tree8b27ee6cdbf815e202b6c0957dd9faae42203800
parentaef7b742ede29e9c5b83616cbc43ccfdf80a13f5 (diff)
parente4d7972071692563393cb566c94158f369be33dc (diff)
Merge "Add flows for whether user is interacting with the shade" into main
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt240
3 files changed, 291 insertions, 9 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 97b06170e770..054bd082edb7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -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 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 95a072c0d4c9..b77b9e488d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -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)
@@ -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(
@@ -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)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 9275ccb6d473..d4b69fad6097 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -402,6 +402,7 @@ class ShadeInteractorTest : SysuiTestCase() {
assertThat(actual).isEqualTo(0.8f)
}
+ @Test
fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.shadeExpansion)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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()
@@ -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)
@@ -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()
+ }
}