Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +24 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ constructor( @Application scope: CoroutineScope, disableFlagsRepository: DisableFlagsRepository, sceneContainerFlags: SceneContainerFlags, // TODO(b/300258424) convert to direct reference instead of provider sceneInteractorProvider: Provider<SceneInteractor>, keyguardRepository: KeyguardRepository, userSetupRepository: UserSetupRepository, Loading Loading @@ -141,15 +142,22 @@ constructor( * if the user's input gesture has ended but a transition they initiated is animating. */ val isUserInteractingWithShade: Flow<Boolean> = if (sceneContainerFlags.isEnabled()) { sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade) } else { 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> = if (sceneContainerFlags.isEnabled()) { sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings) } else { userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion) } /** * Whether the user is expanding or collapsing either the shade or quick settings with user Loading @@ -158,6 +166,7 @@ constructor( */ val isUserInteracting: Flow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } .distinctUntilChanged() /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = Loading Loading @@ -198,6 +207,18 @@ constructor( } .distinctUntilChanged() fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState .map { state -> when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> state.isUserInputDriven && (state.toScene == sceneKey || state.fromScene == sceneKey) } } .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 Loading packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +188 −0 Original line number Diff line number Diff line Loading @@ -939,4 +939,192 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteracting_idle() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.Shade val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is idle val transitionState = MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_toScene_programmatic() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition completes progress.value = 1f // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_toScene_userInputDriven() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition completes progress.value = 1f // THEN interacting is true assertThat(interacting).isTrue() } @Test fun userInteracting_transitioning_fromScene_programmatic() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition completes progress.value = 1f // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_fromScene_userInputDriven() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition completes progress.value = 1f // THEN interacting is true assertThat(interacting).isTrue() } @Test fun userInteracting_transitioning_toAndFromDifferentScenes() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to between different scenes val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() } } Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +24 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ constructor( @Application scope: CoroutineScope, disableFlagsRepository: DisableFlagsRepository, sceneContainerFlags: SceneContainerFlags, // TODO(b/300258424) convert to direct reference instead of provider sceneInteractorProvider: Provider<SceneInteractor>, keyguardRepository: KeyguardRepository, userSetupRepository: UserSetupRepository, Loading Loading @@ -141,15 +142,22 @@ constructor( * if the user's input gesture has ended but a transition they initiated is animating. */ val isUserInteractingWithShade: Flow<Boolean> = if (sceneContainerFlags.isEnabled()) { sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade) } else { 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> = if (sceneContainerFlags.isEnabled()) { sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.QuickSettings) } else { userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion) } /** * Whether the user is expanding or collapsing either the shade or quick settings with user Loading @@ -158,6 +166,7 @@ constructor( */ val isUserInteracting: Flow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } .distinctUntilChanged() /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = Loading Loading @@ -198,6 +207,18 @@ constructor( } .distinctUntilChanged() fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState .map { state -> when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> state.isUserInputDriven && (state.toScene == sceneKey || state.fromScene == sceneKey) } } .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 Loading
packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +188 −0 Original line number Diff line number Diff line Loading @@ -939,4 +939,192 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN user is not interacting assertThat(actual).isFalse() } @Test fun userInteracting_idle() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.Shade val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is idle val transitionState = MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_toScene_programmatic() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition completes progress.value = 1f // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_toScene_userInputDriven() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition completes progress.value = 1f // THEN interacting is true assertThat(interacting).isTrue() } @Test fun userInteracting_transitioning_fromScene_programmatic() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is false assertThat(interacting).isFalse() // WHEN transition completes progress.value = 1f // THEN interacting is false assertThat(interacting).isFalse() } @Test fun userInteracting_transitioning_fromScene_userInputDriven() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to move to the scene val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition state is partially to the scene progress.value = .4f // THEN interacting is true assertThat(interacting).isTrue() // WHEN transition completes progress.value = 1f // THEN interacting is true assertThat(interacting).isTrue() } @Test fun userInteracting_transitioning_toAndFromDifferentScenes() = testScope.runTest() { // GIVEN an interacting flow based on transitions to and from a scene val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade) val interacting by collectLastValue(interactingFlow) // WHEN transition state is starting to between different scenes val progress = MutableStateFlow(0f) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, progress = progress, isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) // THEN interacting is false assertThat(interacting).isFalse() } }