summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt300
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt124
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/SysUITestModule.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt)355
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt415
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt522
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java19
17 files changed, 1508 insertions, 663 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index 7a803867d4a4..53eccfdf70d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractorEmptyImpl
import dagger.Binds
import dagger.Module
@@ -30,4 +32,8 @@ abstract class ShadeEmptyImplModule {
@Binds
@SysUISingleton
abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeInteractor(si: ShadeInteractorEmptyImpl): ShadeInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 89aaaafbcdf3..54467cffa401 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -17,13 +17,40 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
-
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
import dagger.Binds
import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
/** Module for classes related to the notification shade. */
@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
abstract class ShadeModule {
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideBaseShadeInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
+ sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+ ): BaseShadeInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
+ }
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeInteractor(si: ShadeInteractorImpl): ShadeInteractor
+
@Binds
@SysUISingleton
abstract fun bindsShadeViewController(
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 d687ef64aec4..5f7b07760a5b 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
@@ -16,149 +16,39 @@
package com.android.systemui.shade.domain.interactor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
-import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
-import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
-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.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.isActive
/** Business logic for shade interactions. */
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-class ShadeInteractor
-@Inject
-constructor(
- @Application scope: CoroutineScope,
- deviceProvisioningRepository: DeviceProvisioningRepository,
- disableFlagsRepository: DisableFlagsRepository,
- dozeParams: DozeParameters,
- sceneContainerFlags: SceneContainerFlags,
- // TODO(b/300258424) convert to direct reference instead of provider
- sceneInteractorProvider: Provider<SceneInteractor>,
- keyguardRepository: KeyguardRepository,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
- powerInteractor: PowerInteractor,
- userSetupRepository: UserSetupRepository,
- userSwitcherInteractor: UserSwitcherInteractor,
- sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
- private val repository: ShadeRepository,
-) {
+interface ShadeInteractor : BaseShadeInteractor {
/** Emits true if the shade is currently allowed and false otherwise. */
- val isShadeEnabled: StateFlow<Boolean> =
- disableFlagsRepository.disableFlags
- .map { it.isShadeEnabled() }
- .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ val isShadeEnabled: StateFlow<Boolean>
- /**
- * Whether split shade, the combined notifications and quick settings shade used for large
- * screens, is enabled.
- */
- val isSplitShadeEnabled: Flow<Boolean> =
- sharedNotificationContainerInteractor.configurationBasedDimensions
- .map { dimens -> dimens.useSplitShade }
- .distinctUntilChanged()
-
- /** The amount [0-1] that the shade has been opened */
- val shadeExpansion: Flow<Float> =
- if (sceneContainerFlags.isEnabled()) {
- sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.Shade)
- } else {
- combine(
- repository.lockscreenShadeExpansion,
- keyguardRepository.statusBarState,
- repository.legacyShadeExpansion,
- repository.qsExpansion,
- isSplitShadeEnabled
- ) {
- lockscreenShadeExpansion,
- statusBarState,
- legacyShadeExpansion,
- qsExpansion,
- splitShadeEnabled ->
- when (statusBarState) {
- // legacyShadeExpansion is 1 instead of 0 when QS is expanded
- StatusBarState.SHADE ->
- if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
- StatusBarState.KEYGUARD -> lockscreenShadeExpansion
- // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
- // the pointer is lifted and the lockscreen shade is fully expanded
- StatusBarState.SHADE_LOCKED -> 1f
- }
- }
- .distinctUntilChanged()
- }
+ /** Whether either the shade or QS is fully expanded. */
+ val isAnyFullyExpanded: Flow<Boolean>
/**
- * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
- * report 0f. If split shade is enabled, value matches shadeExpansion.
+ * 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 qsExpansion: StateFlow<Float> =
- if (sceneContainerFlags.isEnabled()) {
- val qsExp = sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
- combine(isSplitShadeEnabled, shadeExpansion, qsExp) {
- isSplitShadeEnabled,
- shadeExp,
- qsExp ->
- if (isSplitShadeEnabled) {
- shadeExp
- } else {
- qsExp
- }
- }
- .stateIn(scope, SharingStarted.Eagerly, 0f)
- } else {
- repository.qsExpansion
- }
+ val isUserInteracting: Flow<Boolean>
- /** Whether Quick Settings is expanded a non-zero amount. */
- val isQsExpanded: StateFlow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- qsExpansion
- .map { it > 0 }
- .distinctUntilChanged()
- .stateIn(scope, SharingStarted.Eagerly, false)
- } else {
- repository.legacyIsQsExpanded
- }
+ /** Are touches allowed on the notification panel? */
+ val isShadeTouchable: Flow<Boolean>
- /** 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)
+ /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
+ val isExpandToQsEnabled: Flow<Boolean>
+}
- /** Whether either the shade or QS is fully expanded. */
- val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
+/** ShadeInteractor methods with implementations that differ between non-empty impls. */
+interface BaseShadeInteractor {
+ /** The amount [0-1] either QS or the shade has been opened. */
+ val anyExpansion: StateFlow<Float>
/**
* Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
@@ -169,149 +59,39 @@ constructor(
*
* TODO(b/300258424) remove all but the first sentence of this comment
*/
- val isAnyExpanded: StateFlow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- anyExpansion.map { it > 0f }.distinctUntilChanged()
- } else {
- repository.legacyExpandedOrAwaitingInputTransfer
- }
- .stateIn(scope, SharingStarted.Eagerly, false)
+ val isAnyExpanded: StateFlow<Boolean>
+
+ /** The amount [0-1] that the shade has been opened */
+ val shadeExpansion: Flow<Float>
+
+ /**
+ * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
+ * report 0f. If split shade is enabled, value matches shadeExpansion.
+ */
+ val qsExpansion: StateFlow<Float>
+
+ /** Whether Quick Settings is expanded a non-zero amount. */
+ val isQsExpanded: StateFlow<Boolean>
/**
* 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> =
- if (sceneContainerFlags.isEnabled()) {
- sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
- } else {
- combine(
- userInteractingFlow(
- repository.legacyShadeTracking,
- repository.legacyShadeExpansion
- ),
- repository.legacyLockscreenShadeTracking
- ) { legacyShadeTracking, legacyLockscreenShadeTracking ->
- legacyShadeTracking || legacyLockscreenShadeTracking
- }
- }
+ val isUserInteractingWithShade: Flow<Boolean>
/**
* 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
- * 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, isUserInteractingWithQs) { shade, qs -> shade || qs }
- .distinctUntilChanged()
-
- /** Are touches allowed on the notification panel? */
- val isShadeTouchable: Flow<Boolean> =
- combine(
- powerInteractor.isAsleep,
- keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
- keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
- deviceProvisioningRepository.isFactoryResetProtectionActive,
- ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
- when {
- // Touches are disabled when Factory Reset Protection is active
- isFrpActive -> false
- // If the device is going to sleep, only accept touches if we're still
- // animating
- goingToSleep -> dozeParams.shouldControlScreenOff()
- // If the device is asleep, only accept touches if there's a pulse
- isAsleep -> isPulsing
- else -> true
- }
- }
-
- /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
- val isExpandToQsEnabled: Flow<Boolean> =
- combine(
- disableFlagsRepository.disableFlags,
- isShadeEnabled,
- keyguardRepository.isDozing,
- userSetupRepository.isUserSetupFlow,
- deviceProvisioningRepository.isDeviceProvisioned,
- ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
- isDeviceProvisioned &&
- // Disallow QS during setup if it's a simple user switcher. (The user intends to
- // use the lock screen user switcher, QS is not needed.)
- (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
- isShadeEnabled &&
- disableFlags.isQuickSettingsEnabled() &&
- !isDozing
- }
-
- fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
- sceneInteractor.transitionState
- .flatMapLatest { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- if (state.scene == sceneKey) {
- flowOf(1f)
- } else {
- flowOf(0f)
- }
- is ObservableTransitionState.Transition ->
- if (state.toScene == sceneKey) {
- state.progress
- } else if (state.fromScene == sceneKey) {
- state.progress.map { progress -> 1 - progress }
- } else {
- flowOf(0f)
- }
- }
- }
- .distinctUntilChanged()
-
- fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
- sceneInteractor.transitionState
- .map { state ->
- when (state) {
- is ObservableTransitionState.Idle -> false
- is ObservableTransitionState.Transition ->
- state.isInitiatedByUserInput &&
- (state.toScene == sceneKey || state.fromScene == sceneKey)
- }
- }
- .distinctUntilChanged()
+ val isUserInteractingWithQs: Flow<Boolean>
+}
- /**
- * 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)
- }
- }
- }
+fun createAnyExpansionFlow(
+ scope: CoroutineScope,
+ shadeExpansion: Flow<Float>,
+ qsExpansion: Flow<Float>
+): StateFlow<Float> {
+ return combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
+ .stateIn(scope, SharingStarted.Eagerly, 0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
new file mode 100644
index 000000000000..e36897c94964
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Empty implementation of ShadeInteractor for System UI variants with no shade. */
+@SysUISingleton
+class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor {
+ private val inactiveFlowBoolean = MutableStateFlow(false)
+ private val inactiveFlowFloat = MutableStateFlow(0f)
+ override val isShadeEnabled: StateFlow<Boolean> = inactiveFlowBoolean
+ override val shadeExpansion: Flow<Float> = inactiveFlowFloat
+ override val qsExpansion: StateFlow<Float> = inactiveFlowFloat
+ override val isQsExpanded: StateFlow<Boolean> = inactiveFlowBoolean
+ override val anyExpansion: StateFlow<Float> = inactiveFlowFloat
+ override val isAnyFullyExpanded: Flow<Boolean> = inactiveFlowBoolean
+ override val isAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean
+ override val isUserInteractingWithShade: Flow<Boolean> = inactiveFlowBoolean
+ override val isUserInteractingWithQs: Flow<Boolean> = inactiveFlowBoolean
+ override val isUserInteracting: Flow<Boolean> = inactiveFlowBoolean
+ override val isShadeTouchable: Flow<Boolean> = inactiveFlowBoolean
+ override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
new file mode 100644
index 000000000000..fb21aee3a036
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+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.map
+import kotlinx.coroutines.flow.stateIn
+
+/** The non-empty SceneInteractor implementation. */
+@SysUISingleton
+class ShadeInteractorImpl
+@Inject
+constructor(
+ @Application val scope: CoroutineScope,
+ deviceProvisioningRepository: DeviceProvisioningRepository,
+ disableFlagsRepository: DisableFlagsRepository,
+ dozeParams: DozeParameters,
+ keyguardRepository: KeyguardRepository,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ powerInteractor: PowerInteractor,
+ userSetupRepository: UserSetupRepository,
+ userSwitcherInteractor: UserSwitcherInteractor,
+ private val baseShadeInteractor: BaseShadeInteractor,
+) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
+ override val isShadeEnabled: StateFlow<Boolean> =
+ disableFlagsRepository.disableFlags
+ .map { it.isShadeEnabled() }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+
+ override val isAnyFullyExpanded: Flow<Boolean> =
+ anyExpansion.map { it >= 1f }.distinctUntilChanged()
+
+ override val isUserInteracting: Flow<Boolean> =
+ combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
+ .distinctUntilChanged()
+
+ override val isShadeTouchable: Flow<Boolean> =
+ combine(
+ powerInteractor.isAsleep,
+ keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
+ keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
+ deviceProvisioningRepository.isFactoryResetProtectionActive,
+ ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
+ when {
+ // Touches are disabled when Factory Reset Protection is active
+ isFrpActive -> false
+ // If the device is going to sleep, only accept touches if we're still
+ // animating
+ goingToSleep -> dozeParams.shouldControlScreenOff()
+ // If the device is asleep, only accept touches if there's a pulse
+ isAsleep -> isPulsing
+ else -> true
+ }
+ }
+
+ override val isExpandToQsEnabled: Flow<Boolean> =
+ combine(
+ disableFlagsRepository.disableFlags,
+ isShadeEnabled,
+ keyguardRepository.isDozing,
+ userSetupRepository.isUserSetupFlow,
+ deviceProvisioningRepository.isDeviceProvisioned,
+ ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
+ isDeviceProvisioned &&
+ // Disallow QS during setup if it's a simple user switcher. (The user intends to
+ // use the lock screen user switcher, QS is not needed.)
+ (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
+ isShadeEnabled &&
+ disableFlags.isQuickSettingsEnabled() &&
+ !isDozing
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
new file mode 100644
index 000000000000..5b3834b343bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+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.flow
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.isActive
+
+/** ShadeInteractor implementation for the legacy codebase, e.g. NPVC. */
+@SysUISingleton
+class ShadeInteractorLegacyImpl
+@Inject
+constructor(
+ @Application val scope: CoroutineScope,
+ keyguardRepository: KeyguardRepository,
+ sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
+ repository: ShadeRepository,
+) : BaseShadeInteractor {
+ /** The amount [0-1] that the shade has been opened */
+ override val shadeExpansion: Flow<Float> =
+ combine(
+ repository.lockscreenShadeExpansion,
+ keyguardRepository.statusBarState,
+ repository.legacyShadeExpansion,
+ repository.qsExpansion,
+ sharedNotificationContainerInteractor.isSplitShadeEnabled
+ ) {
+ lockscreenShadeExpansion,
+ statusBarState,
+ legacyShadeExpansion,
+ qsExpansion,
+ splitShadeEnabled ->
+ when (statusBarState) {
+ // legacyShadeExpansion is 1 instead of 0 when QS is expanded
+ StatusBarState.SHADE ->
+ if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
+ StatusBarState.KEYGUARD -> lockscreenShadeExpansion
+ // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
+ // the pointer is lifted and the lockscreen shade is fully expanded
+ StatusBarState.SHADE_LOCKED -> 1f
+ }
+ }
+ .distinctUntilChanged()
+
+ override val qsExpansion: StateFlow<Float> = repository.qsExpansion
+
+ override val isQsExpanded: StateFlow<Boolean> = repository.legacyIsQsExpanded
+
+ override val anyExpansion: StateFlow<Float> =
+ createAnyExpansionFlow(scope, shadeExpansion, qsExpansion)
+
+ override val isAnyExpanded =
+ repository.legacyExpandedOrAwaitingInputTransfer.stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ false
+ )
+
+ override val isUserInteractingWithShade: Flow<Boolean> =
+ combine(
+ userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion),
+ repository.legacyLockscreenShadeTracking
+ ) { legacyShadeTracking, legacyLockscreenShadeTracking ->
+ legacyShadeTracking || legacyLockscreenShadeTracking
+ }
+
+ override val isUserInteractingWithQs: Flow<Boolean> =
+ userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+
+ /**
+ * 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/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
new file mode 100644
index 000000000000..58ebbc6ac8bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** ShadeInteractor implementation for Scene Container. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class ShadeInteractorSceneContainerImpl
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ sceneInteractor: SceneInteractor,
+ sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
+) : BaseShadeInteractor {
+ override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+
+ private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+
+ override val qsExpansion: StateFlow<Float> =
+ combine(
+ sharedNotificationContainerInteractor.isSplitShadeEnabled,
+ shadeExpansion,
+ sceneBasedQsExpansion,
+ ) { isSplitShadeEnabled, shadeExpansion, qsExpansion ->
+ if (isSplitShadeEnabled) {
+ shadeExpansion
+ } else {
+ qsExpansion
+ }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, 0f)
+
+ override val isQsExpanded: StateFlow<Boolean> =
+ qsExpansion
+ .map { it > 0 }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override val anyExpansion: StateFlow<Float> =
+ createAnyExpansionFlow(scope, shadeExpansion, qsExpansion)
+
+ override val isAnyExpanded =
+ anyExpansion
+ .map { it > 0f }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ override val isUserInteractingWithShade: Flow<Boolean> =
+ sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+
+ override val isUserInteractingWithQs: Flow<Boolean> =
+ sceneBasedInteracting(sceneInteractor, SceneKey.QuickSettings)
+
+ /**
+ * Returns a flow that uses scene transition progress to and from a scene that is pulled down
+ * from the top of the screen to a 0-1 expansion amount float.
+ */
+ internal fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ if (state.scene == sceneKey) {
+ flowOf(1f)
+ } else {
+ flowOf(0f)
+ }
+ is ObservableTransitionState.Transition ->
+ if (state.toScene == sceneKey) {
+ state.progress
+ } else if (state.fromScene == sceneKey) {
+ state.progress.map { progress -> 1 - progress }
+ } else {
+ flowOf(0f)
+ }
+ }
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Returns a flow that uses scene transition data to determine whether the user is interacting
+ * with a scene that is pulled down from the top of the screen.
+ */
+ internal fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.isInitiatedByUserInput &&
+ (state.toScene == sceneKey || state.fromScene == sceneKey)
+ }
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index eb1c17aaca78..c2c5eed6f013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -73,6 +73,11 @@ constructor(
}
.distinctUntilChanged()
+ val isSplitShadeEnabled: Flow<Boolean> =
+ configurationBasedDimensions
+ .map { dimens: ConfigurationBasedDimensions -> dimens.useSplitShade }
+ .distinctUntilChanged()
+
/** Top position (without translation) of the shared container. */
fun setTopPosition(top: Float) {
_topPosition.value = top
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
index 97e43ad91f53..d8e7cd642ad9 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
@@ -26,11 +26,19 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
+import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineStart
@@ -56,6 +64,7 @@ interface SysUITestModule {
@Binds @Application fun bindAppResources(resources: Resources): Resources
@Binds @Main fun bindMainResources(resources: Resources): Resources
@Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher
+ @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor
companion object {
@Provides
@@ -72,6 +81,19 @@ interface SysUITestModule {
@Provides
fun provideFakeBroadcastDispatcher(test: SysuiTestCase): FakeBroadcastDispatcher =
test.fakeBroadcastDispatcher
+
+ @Provides
+ fun provideBaseShadeInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
+ sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+ ): BaseShadeInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index f1429b5dd7b3..36d78685edf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -123,11 +123,12 @@ import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
import com.android.systemui.scene.SceneTestUtils;
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -389,26 +390,27 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
StateFlowKt.MutableStateFlow(false));
- mShadeInteractor = new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
new FakeDeviceProvisioningRepository(),
new FakeDisableFlagsRepository(),
mDozeParameters,
- new FakeSceneContainerFlags(),
- mUtils::sceneInteractor,
mFakeKeyguardRepository,
mKeyguardTransitionInteractor,
mPowerInteractor,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
- new SharedNotificationContainerInteractor(
- new FakeConfigurationRepository(),
- mContext,
- new ResourcesSplitShadeStateController()
- ),
- mShadeRepository
+ new ShadeInteractorLegacyImpl(
+ mTestScope.getBackgroundScope(),
+ mFakeKeyguardRepository,
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()
+ ),
+ mShadeRepository
+ )
);
-
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 6aaa0a135b66..8403ac59d2f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -78,6 +78,8 @@ import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -231,25 +233,26 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mKeyguardSecurityModel,
mSelectedUserInteractor,
powerInteractor);
-
- mShadeInteractor =
- new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
+ mTestScope.getBackgroundScope(),
+ new FakeDeviceProvisioningRepository(),
+ new FakeDisableFlagsRepository(),
+ mock(DozeParameters.class),
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ new FakeUserSetupRepository(),
+ mock(UserSwitcherInteractor.class),
+ new ShadeInteractorLegacyImpl(
mTestScope.getBackgroundScope(),
- new FakeDeviceProvisioningRepository(),
- new FakeDisableFlagsRepository(),
- mock(DozeParameters.class),
- sceneContainerFlags,
- () -> sceneInteractor,
keyguardRepository,
- keyguardTransitionInteractor,
- powerInteractor,
- new FakeUserSetupRepository(),
- mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
new ResourcesSplitShadeStateController()),
- shadeRepository);
+ shadeRepository
+ )
+ );
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index ff110c5437a2..26b84e372d5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -68,6 +68,8 @@ import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -267,25 +269,26 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
ResourcesSplitShadeStateController splitShadeStateController =
new ResourcesSplitShadeStateController();
- mShadeInteractor =
- new ShadeInteractor(
+ mShadeInteractor = new ShadeInteractorImpl(
+ mTestScope.getBackgroundScope(),
+ deviceProvisioningRepository,
+ mDisableFlagsRepository,
+ mDozeParameters,
+ mKeyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ new FakeUserSetupRepository(),
+ mUserSwitcherInteractor,
+ new ShadeInteractorLegacyImpl(
mTestScope.getBackgroundScope(),
- deviceProvisioningRepository,
- mDisableFlagsRepository,
- mDozeParameters,
- sceneContainerFlags,
- () -> sceneInteractor,
mKeyguardRepository,
- keyguardTransitionInteractor,
- powerInteractor,
- new FakeUserSetupRepository(),
- mUserSwitcherInteractor,
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
splitShadeStateController),
mShadeRepository
- );
+ )
+ );
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index ff7443f10bf3..09700e186978 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.shade.data.repository
+package com.android.systemui.shade.domain.interactor
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
@@ -46,9 +46,7 @@ import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
@@ -62,14 +60,12 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
@SmallTest
-class ShadeInteractorTest : SysuiTestCase() {
+class ShadeInteractorImplTest : SysuiTestCase() {
@SysUISingleton
@Component(
@@ -79,7 +75,7 @@ class ShadeInteractorTest : SysuiTestCase() {
UserDomainLayerModule::class,
]
)
- interface TestComponent : SysUITestComponent<ShadeInteractor> {
+ interface TestComponent : SysUITestComponent<ShadeInteractorImpl> {
val configurationRepository: FakeConfigurationRepository
val deviceProvisioningRepository: FakeDeviceProvisioningRepository
@@ -105,7 +101,7 @@ class ShadeInteractorTest : SysuiTestCase() {
private val dozeParameters: DozeParameters = mock()
private val testComponent: TestComponent =
- DaggerShadeInteractorTest_TestComponent.factory()
+ DaggerShadeInteractorImplTest_TestComponent.factory()
.create(
test = this,
featureFlags =
@@ -446,154 +442,6 @@ class ShadeInteractorTest : SysuiTestCase() {
}
@Test
- fun lockscreenShadeExpansion_idle_onScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.Shade
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is idle on the scene
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
- }
-
- @Test
- fun lockscreenShadeExpansion_idle_onDifferentScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is idle on a different scene
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_toScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // 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,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN expansion matches the progress
- assertThat(expansionAmount).isEqualTo(.4f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_fromScene() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
- val expansionAmount by collectLastValue(expansion)
-
- // 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,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 1
- assertThat(expansionAmount).isEqualTo(1f)
-
- // WHEN transition state is partially to the scene
- progress.value = .4f
-
- // THEN expansion reflects the progress
- assertThat(expansionAmount).isEqualTo(.6f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
- fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
- testComponent.runTest() {
- // GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
- val expansionAmount by collectLastValue(expansion)
-
- // WHEN transition state is starting to between different scenes
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.Shade,
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN expansion is 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition state is partially complete
- progress.value = .4f
-
- // THEN expansion is still 0
- assertThat(expansionAmount).isEqualTo(0f)
-
- // WHEN transition completes
- progress.value = 1f
-
- // THEN expansion is still 0
- assertThat(expansionAmount).isEqualTo(0f)
- }
-
- @Test
fun userInteractingWithShade_shadeDraggedUpAndDown() =
testComponent.runTest() {
val actual by collectLastValue(underTest.isUserInteractingWithShade)
@@ -815,199 +663,6 @@ class ShadeInteractorTest : SysuiTestCase() {
// THEN user is not interacting
assertThat(actual).isFalse()
}
- @Test
- fun userInteracting_idle() =
- testComponent.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() =
- testComponent.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,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(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() =
- testComponent.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,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- 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() =
- testComponent.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,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(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() =
- testComponent.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,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- 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() =
- testComponent.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 = MutableStateFlow(0f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- )
- sceneInteractor.setTransitionState(transitionState)
-
- // THEN interacting is false
- assertThat(interacting).isFalse()
- }
@Test
fun isShadeTouchable_isFalse_whenFrpIsActive() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
new file mode 100644
index 000000000000..f3c875e671c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -0,0 +1,415 @@
+/*
+ * 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.shade.domain.interactor
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class ShadeInteractorLegacyImplTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ShadeInteractorLegacyImpl> {
+
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val powerRepository: FakePowerRepository
+ val sceneInteractor: SceneInteractor
+ val shadeRepository: FakeShadeRepository
+ val userRepository: FakeUserRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerShadeInteractorLegacyImplTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+
+ @Before
+ fun setUp() {
+ runBlocking {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or
+ UserInfo.FLAG_ADMIN or
+ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ )
+ testComponent.apply {
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ }
+ }
+ }
+
+ @Test
+ fun fullShadeExpansionWhenShadeLocked() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+ assertThat(actual).isEqualTo(0.5f)
+
+ shadeRepository.setLockscreenShadeExpansion(0.8f)
+ assertThat(actual).isEqualTo(0.8f)
+ }
+
+ @Test
+ fun shadeExpansionWhenInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+ shadeRepository.setQsExpansion(.5f)
+ shadeRepository.setLegacyShadeExpansion(.7f)
+ runCurrent()
+
+ // THEN legacy shade expansion is passed through
+ assertThat(actual).isEqualTo(.7f)
+ }
+
+ @Test
+ fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() =
+ testComponent.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() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLegacyShadeExpansion(.6f)
+
+ // THEN shade expansion is zero
+ assertThat(actual).isEqualTo(.6f)
+ }
+
+ @Test
+ fun userInteractingWithShade_shadeDraggedUpAndDown() =
+ testComponent.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() =
+ testComponent.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() =
+ testComponent.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() =
+ testComponent.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() =
+ testComponent.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()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
new file mode 100644
index 000000000000..3e21f15a54a9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -0,0 +1,522 @@
+/*
+ * 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.shade.domain.interactor
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+@SmallTest
+class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ShadeInteractorSceneContainerImpl> {
+
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val powerRepository: FakePowerRepository
+ val sceneInteractor: SceneInteractor
+ val shadeRepository: FakeShadeRepository
+ val userRepository: FakeUserRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerShadeInteractorSceneContainerImplTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+
+ @Before
+ fun setUp() {
+ runBlocking {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or
+ UserInfo.FLAG_ADMIN or
+ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ )
+ testComponent.apply {
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ }
+ }
+ }
+
+ @Ignore("b/309825977")
+ @Test
+ fun qsExpansionWhenInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.qsExpansion)
+
+ // WHEN split shade is enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+ val progress = MutableStateFlow(.3f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN legacy shade expansion is passed through
+ Truth.assertThat(actual).isEqualTo(.3f)
+ }
+
+ @Ignore("b/309825977")
+ @Test
+ fun qsExpansionWhenNotInSplitShadeAndQsExpanded() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.qsExpansion)
+
+ // WHEN split shade is not enabled and QS is expanded
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ val progress = MutableStateFlow(.3f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN shade expansion is zero
+ Truth.assertThat(actual).isEqualTo(.7f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.Shade
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on the scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_idle_onDifferentScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is idle on a different scene
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // 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,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion matches the progress
+ Truth.assertThat(expansionAmount).isEqualTo(.4f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_fromScene() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val key = SceneKey.QuickSettings
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+ val expansionAmount by collectLastValue(expansion)
+
+ // 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,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 1
+ Truth.assertThat(expansionAmount).isEqualTo(1f)
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN expansion reflects the progress
+ Truth.assertThat(expansionAmount).isEqualTo(.6f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
+ testComponent.runTest() {
+ // GIVEN an expansion flow based on transitions to and from a scene
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+ val expansionAmount by collectLastValue(expansion)
+
+ // WHEN transition state is starting to between different scenes
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.Shade,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN expansion is 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition state is partially complete
+ progress.value = .4f
+
+ // THEN expansion is still 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN expansion is still 0
+ Truth.assertThat(expansionAmount).isEqualTo(0f)
+ }
+
+ @Test
+ fun userInteracting_idle() =
+ testComponent.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
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_programmatic() =
+ testComponent.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,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toScene_userInputDriven() =
+ testComponent.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,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_programmatic() =
+ testComponent.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,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+
+ @Test
+ fun userInteracting_transitioning_fromScene_userInputDriven() =
+ testComponent.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,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition state is partially to the scene
+ progress.value = .4f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+
+ // WHEN transition completes
+ progress.value = 1f
+
+ // THEN interacting is true
+ Truth.assertThat(interacting).isTrue()
+ }
+
+ @Test
+ fun userInteracting_transitioning_toAndFromDifferentScenes() =
+ testComponent.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 = MutableStateFlow(0f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+
+ // THEN interacting is false
+ Truth.assertThat(interacting).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 4b79a499bf90..8fa7cd291851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -44,6 +44,8 @@ import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
@@ -153,23 +155,25 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
mock(),
mock(),
powerInteractor)
- shadeInteractor = ShadeInteractor(
+ shadeInteractor = ShadeInteractorImpl(
testScope.backgroundScope,
FakeDeviceProvisioningRepository(),
FakeDisableFlagsRepository(),
mock(),
- sceneContainerFlags,
- utils::sceneInteractor,
keyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
FakeUserSetupRepository(),
mock(),
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()),
- shadeRepository,
+ ShadeInteractorLegacyImpl(
+ testScope.backgroundScope,
+ keyguardRepository,
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ ResourcesSplitShadeStateController()),
+ shadeRepository,
+ )
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 102c3fc76a61..7e83034e513e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,8 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeWindowLogger;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl;
+import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationEntryHelper;
@@ -454,23 +456,24 @@ public class BubblesTest extends SysuiTestCase {
new ResourcesSplitShadeStateController();
mShadeInteractor =
- new ShadeInteractor(
+ new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
deviceProvisioningRepository,
new FakeDisableFlagsRepository(),
mDozeParameters,
- sceneContainerFlags,
- () -> sceneInteractor,
keyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
- new SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- splitShadeStateController),
- new FakeShadeRepository()
+ new ShadeInteractorLegacyImpl(
+ mTestScope.getBackgroundScope(), keyguardRepository,
+ new SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ splitShadeStateController),
+ shadeRepository
+ )
);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(