summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author burakov <burakov@google.com> 2024-09-09 14:28:58 +0000
committer burakov <burakov@google.com> 2024-09-12 18:21:27 +0000
commit60985a397aaad3554a3da436b6029f5fbcefb3b8 (patch)
treeb2e86e999d5967e635bc35b5539656a5976f31da
parent5394369d517609a99bf0e6966deb04757bb75d31 (diff)
[bc25] Refactor ShadeInteractor for Dual Shade Overlays.
This also adds expandNotificationShade() and expandQuickSettingsShade(), which make it easier to ensure the shade is opened correctly in the different shade modes. Bug: 356596436 Flag: com.android.systemui.scene_container Flag: com.android.systemui.dual_shade Test: Manually tested by opening the Wallpaper Picker and navigating to the "Lock screen" tab and "Shortcuts" section. The lockscreen preview renders correctly, and the shortcuts change and render correctly too. Test: Added new unit tests for the two new functions introduced. Test: Existing unit tests still pass. Change-Id: I92368206c6849c8fefba881049db391d8ca9b11f
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt215
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt168
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt2
12 files changed, 395 insertions, 132 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index 2a2817b9af73..ad2b23e49536 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -88,6 +88,26 @@ class ShadeModeInteractorImplTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun isDualShade_flagEnabled_true() =
+ testScope.runTest {
+ // Initiate collection.
+ val shadeMode by collectLastValue(underTest.shadeMode)
+
+ assertThat(underTest.isDualShade).isTrue()
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun isDualShade_flagDisabled_false() =
+ testScope.runTest {
+ // Initiate collection.
+ val shadeMode by collectLastValue(underTest.shadeMode)
+
+ assertThat(underTest.isDualShade).isFalse()
+ }
+
+ @Test
fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
testScope.runTest {
// Ensure isShadeLayoutWide is collected.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a402a9d5ae7c..ac49e91c777c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,10 +101,10 @@ import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.shared.model.SceneFamilies;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -158,6 +158,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final ScreenPinningRequest mScreenPinningRequest;
private final NotificationShadeWindowController mStatusBarWinController;
private final Provider<SceneInteractor> mSceneInteractor;
+ private final Provider<ShadeInteractor> mShadeInteractor;
private final KeyboardTouchpadEduStatsInteractor mKeyboardTouchpadEduStatsInteractor;
@@ -246,11 +247,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
} else if (action == ACTION_UP) {
// Gesture was too short to be picked up by scene container touch
- // handling; programmatically start the transition to shade scene.
- mSceneInteractor.get().changeScene(
- SceneFamilies.NotifShade,
- "short launcher swipe"
- );
+ // handling; programmatically start the transition to the shade.
+ mShadeInteractor.get().expandNotificationShade("short launcher swipe");
}
}
event.recycle();
@@ -267,10 +265,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mSceneInteractor.get().onRemoteUserInputStarted(
"trackpad swipe");
} else if (action == ACTION_UP) {
- mSceneInteractor.get().changeScene(
- SceneFamilies.NotifShade,
- "short trackpad swipe"
- );
+ mShadeInteractor.get().expandNotificationShade("short trackpad swipe");
}
mStatusBarWinController.getWindowRootView().dispatchTouchEvent(event);
} else {
@@ -652,6 +647,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
NotificationShadeWindowController statusBarWinController,
SysUiState sysUiState,
Provider<SceneInteractor> sceneInteractor,
+ Provider<ShadeInteractor> shadeInteractor,
UserTracker userTracker,
UserManager userManager,
WakefulnessLifecycle wakefulnessLifecycle,
@@ -688,6 +684,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mScreenPinningRequest = screenPinningRequest;
mStatusBarWinController = statusBarWinController;
mSceneInteractor = sceneInteractor;
+ mShadeInteractor = shadeInteractor;
mUserTracker = userTracker;
mConnectionBackoffAttempts = 0;
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 5d03a28e7f09..361226a4df18 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -91,17 +91,14 @@ constructor(
}
override fun instantCollapseShade() {
- sceneInteractor.snapToScene(
- SceneFamilies.Home,
- "hide shade",
- )
+ sceneInteractor.snapToScene(SceneFamilies.Home, "hide shade")
}
override fun animateCollapseShade(
flags: Int,
force: Boolean,
delayed: Boolean,
- speedUpFactor: Float
+ speedUpFactor: Float,
) {
if (!force && !shadeInteractor.isAnyExpanded.value) {
runPostCollapseActions()
@@ -147,7 +144,7 @@ constructor(
if (shadeInteractor.isAnyExpanded.value) {
commandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */
+ true, /* force */
)
assistManagerLazy.get().hideAssist()
}
@@ -172,17 +169,11 @@ constructor(
}
override fun expandToNotifications() {
- sceneInteractor.changeScene(
- SceneFamilies.NotifShade,
- "ShadeController.animateExpandShade",
- )
+ shadeInteractor.expandNotificationShade("ShadeController.animateExpandShade")
}
override fun expandToQs() {
- sceneInteractor.changeScene(
- SceneFamilies.QuickSettings,
- "ShadeController.animateExpandQs",
- )
+ shadeInteractor.expandQuickSettingsShade("ShadeController.animateExpandQs")
}
override fun setVisibilityListener(listener: ShadeVisibilityListener) {
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 6fb96da2c186..b046c50b05d3 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
@@ -140,6 +140,18 @@ interface BaseShadeInteractor {
* animating.
*/
val isUserInteractingWithQs: Flow<Boolean>
+
+ /**
+ * Triggers the expansion (opening) of the notification shade. If the notification shade is
+ * already open, this has no effect.
+ */
+ fun expandNotificationShade(loggingReason: String)
+
+ /**
+ * Triggers the expansion (opening) of the quick settings shade. If the quick settings shade is
+ * already open, this has no effect.
+ */
+ fun expandQuickSettingsShade(loggingReason: String)
}
fun createAnyExpansionFlow(
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
index 6c0b55a5dd57..fb1482890b87 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -49,4 +49,8 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor {
override val isShadeLayoutWide: StateFlow<Boolean> = inactiveFlowBoolean
override fun getTopEdgeSplitFraction(): Float = 0.5f
+
+ override fun expandNotificationShade(loggingReason: String) {}
+
+ override fun expandQuickSettingsShade(loggingReason: String) {}
}
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
index f48e31e1d7eb..df094864a71b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -61,7 +61,7 @@ constructor(
keyguardRepository.statusBarState,
repository.legacyShadeExpansion,
repository.qsExpansion,
- sharedNotificationContainerInteractor.isSplitShadeEnabled
+ sharedNotificationContainerInteractor.isSplitShadeEnabled,
) {
lockscreenShadeExpansion,
statusBarState,
@@ -97,13 +97,13 @@ constructor(
repository.legacyExpandedOrAwaitingInputTransfer.stateIn(
scope,
SharingStarted.Eagerly,
- false
+ false,
)
override val isUserInteractingWithShade: Flow<Boolean> =
combine(
userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion),
- repository.legacyLockscreenShadeTracking
+ repository.legacyLockscreenShadeTracking,
) { legacyShadeTracking, legacyLockscreenShadeTracking ->
legacyShadeTracking || legacyLockscreenShadeTracking
}
@@ -111,6 +111,18 @@ constructor(
override val isUserInteractingWithQs: Flow<Boolean> =
userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
+ override fun expandNotificationShade(loggingReason: String) {
+ throw UnsupportedOperationException(
+ "expandNotificationShade() is not supported in legacy shade"
+ )
+ }
+
+ override fun expandQuickSettingsShade(loggingReason: String) {
+ throw UnsupportedOperationException(
+ "expandQuickSettingsShade() is not supported in legacy shade"
+ )
+ }
+
/**
* 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
@@ -118,7 +130,7 @@ constructor(
*/
private fun userInteractingFlow(
tracking: Flow<Boolean>,
- expansion: StateFlow<Float>
+ expansion: StateFlow<Float>,
): Flow<Boolean> {
return flow {
// initial value is 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
index e84cfa51dd67..81bf712f21e5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -17,81 +17,70 @@
package com.android.systemui.shade.domain.interactor
import com.android.app.tracing.FlowTracing.traceAsCounter
+import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
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.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
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,
- shadeRepository: ShadeRepository,
+ private val sceneInteractor: SceneInteractor,
+ private val shadeModeInteractor: ShadeModeInteractor,
) : BaseShadeInteractor {
init {
SceneContainerFlag.assertInNewMode()
}
override val shadeExpansion: StateFlow<Float> =
- sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade)
+ shadeModeInteractor.shadeMode
+ .flatMapLatest { shadeMode ->
+ transitionProgressExpansion(shadeMode.notificationsContentKey)
+ }
.traceAsCounter("panel_expansion") { (it * 100f).toInt() }
.stateIn(scope, SharingStarted.Eagerly, 0f)
- private val sceneBasedQsExpansion =
- sceneBasedExpansion(sceneInteractor, SceneFamilies.QuickSettings)
-
override val qsExpansion: StateFlow<Float> =
- combine(
- shadeRepository.isShadeLayoutWide,
- shadeExpansion,
- sceneBasedQsExpansion,
- ) { isSplitShadeEnabled, shadeExpansion, qsExpansion ->
- if (isSplitShadeEnabled) {
- shadeExpansion
- } else {
- qsExpansion
- }
- }
+ shadeModeInteractor.shadeMode
+ .flatMapLatest { shadeMode -> transitionProgressExpansion(shadeMode.qsContentKey) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
override val isQsExpanded: StateFlow<Boolean> =
- qsExpansion
- .map { it > 0 }
- .distinctUntilChanged()
- .stateIn(scope, SharingStarted.Eagerly, false)
+ qsExpansion.map { it > 0 }.stateIn(scope, SharingStarted.Eagerly, false)
override val isQsBypassingShade: Flow<Boolean> =
- combine(
- sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
- sceneInteractor.resolveSceneFamily(SceneFamilies.NotifShade),
- ::Pair
- )
- .flatMapLatestConflated { (quickSettingsScene, notificationsScene) ->
+ shadeModeInteractor.shadeMode
+ .flatMapLatestConflated { shadeMode ->
sceneInteractor.transitionState
.map { state ->
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.toContent == quickSettingsScene &&
- state.fromContent != notificationsScene
+ state.toContent == shadeMode.qsContentKey &&
+ state.fromContent != shadeMode.notificationsContentKey
}
}
.distinctUntilChanged()
@@ -99,21 +88,22 @@ constructor(
.distinctUntilChanged()
override val isQsFullscreen: Flow<Boolean> =
- combine(
- shadeRepository.isShadeLayoutWide,
- sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
- ::Pair
- )
- .flatMapLatestConflated { (isShadeLayoutWide, quickSettingsScene) ->
- sceneInteractor.transitionState
- .map { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- !isShadeLayoutWide && state.currentScene == quickSettingsScene
- is ObservableTransitionState.Transition -> false
- }
- }
- .distinctUntilChanged()
+ shadeModeInteractor.shadeMode
+ .flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ ShadeMode.Single ->
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ state.currentScene == Scenes.QuickSettings
+ is ObservableTransitionState.Transition -> false
+ }
+ }
+ .distinctUntilChanged()
+ ShadeMode.Split,
+ ShadeMode.Dual -> flowOf(false)
+ }
}
.distinctUntilChanged()
@@ -121,16 +111,79 @@ constructor(
createAnyExpansionFlow(scope, shadeExpansion, qsExpansion)
override val isAnyExpanded =
- anyExpansion
- .map { it > 0f }
- .distinctUntilChanged()
- .stateIn(scope, SharingStarted.Eagerly, false)
+ anyExpansion.map { it > 0f }.stateIn(scope, SharingStarted.Eagerly, false)
override val isUserInteractingWithShade: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, SceneFamilies.NotifShade)
+ shadeModeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ ShadeMode.Single,
+ ShadeMode.Split -> sceneBasedInteracting(sceneInteractor, Scenes.Shade)
+ ShadeMode.Dual ->
+ overlayBasedInteracting(sceneInteractor, Overlays.NotificationsShade)
+ }
+ }
override val isUserInteractingWithQs: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, SceneFamilies.QuickSettings)
+ shadeModeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ ShadeMode.Single -> sceneBasedInteracting(sceneInteractor, Scenes.QuickSettings)
+ ShadeMode.Split -> sceneBasedInteracting(sceneInteractor, Scenes.Shade)
+ ShadeMode.Dual ->
+ overlayBasedInteracting(sceneInteractor, Overlays.QuickSettingsShade)
+ }
+ }
+
+ override fun expandNotificationShade(loggingReason: String) {
+ if (shadeModeInteractor.isDualShade) {
+ if (Overlays.QuickSettingsShade in sceneInteractor.currentOverlays.value) {
+ sceneInteractor.replaceOverlay(
+ from = Overlays.QuickSettingsShade,
+ to = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.showOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ )
+ }
+ } else {
+ sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = loggingReason)
+ }
+ }
+
+ override fun expandQuickSettingsShade(loggingReason: String) {
+ if (shadeModeInteractor.isDualShade) {
+ if (Overlays.NotificationsShade in sceneInteractor.currentOverlays.value) {
+ sceneInteractor.replaceOverlay(
+ from = Overlays.NotificationsShade,
+ to = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.showOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ )
+ }
+ } else {
+ sceneInteractor.changeScene(
+ toScene = Scenes.QuickSettings,
+ loggingReason = loggingReason,
+ )
+ }
+ }
+
+ /**
+ * Returns a flow that uses scene transition progress to and from a content to a 0-1 expansion
+ * amount float.
+ */
+ private fun transitionProgressExpansion(contentKey: ContentKey): Flow<Float> {
+ return when (contentKey) {
+ is SceneKey -> sceneBasedExpansion(sceneInteractor, contentKey)
+ is OverlayKey -> overlayBasedExpansion(sceneInteractor, contentKey)
+ }
+ }
/**
* Returns a flow that uses scene transition progress to and from a scene that is pulled down
@@ -181,4 +234,60 @@ constructor(
}
}
.distinctUntilChanged()
+
+ /**
+ * Returns a flow that uses scene transition progress to and from [overlay] to a 0-1 expansion
+ * amount float.
+ */
+ private fun overlayBasedExpansion(sceneInteractor: SceneInteractor, overlay: OverlayKey) =
+ sceneInteractor.transitionState
+ .flatMapLatestConflated { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ flowOf(if (overlay in state.currentOverlays) 1f else 0f)
+ is ObservableTransitionState.Transition ->
+ if (state.toContent == overlay) {
+ state.progress
+ } else if (state.fromContent == overlay) {
+ 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 [overlay].
+ */
+ private fun overlayBasedInteracting(sceneInteractor: SceneInteractor, overlay: OverlayKey) =
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.isInitiatedByUserInput &&
+ (state.toContent == overlay || state.fromContent == overlay)
+ }
+ }
+ .distinctUntilChanged()
+
+ private val ShadeMode.notificationsContentKey: ContentKey
+ get() {
+ return when (this) {
+ ShadeMode.Single,
+ ShadeMode.Split -> Scenes.Shade
+ ShadeMode.Dual -> Overlays.NotificationsShade
+ }
+ }
+
+ private val ShadeMode.qsContentKey: ContentKey
+ get() {
+ return when (this) {
+ ShadeMode.Single -> Scenes.QuickSettings
+ ShadeMode.Split -> Scenes.Shade
+ ShadeMode.Dual -> Overlays.QuickSettingsShade
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index e525b86a2f9e..0fb379017be9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -21,9 +21,10 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -45,10 +46,14 @@ constructor(
override val udfpsTransitionToFullShadeProgress =
shadeRepository.udfpsTransitionToFullShadeProgress
+ @Deprecated("Use ShadeInteractor instead")
override fun expandToNotifications() {
- changeToShadeScene()
+ shadeInteractor.expandNotificationShade(
+ loggingReason = "ShadeLockscreenInteractorImpl.expandToNotifications"
+ )
}
+ @Deprecated("Use ShadeInteractor instead")
override val isExpanded
get() = shadeInteractor.isAnyExpanded.value
@@ -60,15 +65,26 @@ constructor(
lockIconViewController.dozeTimeTick()
}
+ @Deprecated("Not supported by scenes")
override fun blockExpansionForCurrentTouch() {
// TODO("b/324280998") Implement replacement or delete
}
override fun resetViews(animate: Boolean) {
+ val loggingReason = "ShadeLockscreenInteractorImpl.resetViews"
// The existing comment to the only call to this claims it only calls it to collapse QS
- changeToShadeScene()
+ if (shadeInteractor.shadeMode.value == ShadeMode.Dual) {
+ // TODO(b/356596436): Hide without animation if !animate.
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ shadeInteractor.expandNotificationShade(loggingReason)
+ }
}
+ @Deprecated("Not supported by scenes")
override fun setPulsing(pulsing: Boolean) {
// Now handled elsewhere. Do nothing.
}
@@ -76,22 +92,30 @@ constructor(
override fun transitionToExpandedShade(delay: Long) {
backgroundScope.launch {
delay(delay)
- withContext(mainDispatcher) { changeToShadeScene() }
+ withContext(mainDispatcher) {
+ shadeInteractor.expandNotificationShade(
+ "ShadeLockscreenInteractorImpl.transitionToExpandedShade"
+ )
+ }
}
}
+ @Deprecated("Not supported by scenes")
override fun resetViewGroupFade() {
// Now handled elsewhere. Do nothing.
}
+ @Deprecated("Not supported by scenes")
override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {
// Now handled elsewhere. Do nothing.
}
+ @Deprecated("Not supported by scenes")
override fun setOverStretchAmount(amount: Float) {
// Now handled elsewhere. Do nothing.
}
+ @Deprecated("TODO(b/325072511) delete this")
override fun setKeyguardStatusBarAlpha(alpha: Float) {
// TODO(b/325072511) delete this
}
@@ -100,11 +124,4 @@ constructor(
sceneInteractor.changeScene(Scenes.Lockscreen, "showAodUi", sceneState = KeyguardState.AOD)
// TODO(b/330311871) implement transition to AOD
}
-
- private fun changeToShadeScene() {
- sceneInteractor.changeScene(
- SceneFamilies.NotifShade,
- "ShadeLockscreenInteractorImpl.expandToNotifications",
- )
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index 77ae679bf018..caa45137ed98 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -51,6 +51,10 @@ interface ShadeModeInteractor {
*/
val isShadeLayoutWide: StateFlow<Boolean>
+ /** Convenience shortcut for querying whether the current [shadeMode] is [ShadeMode.Dual]. */
+ val isDualShade: Boolean
+ get() = shadeMode.value is ShadeMode.Dual
+
/**
* The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
* between "top-left" and "top-right" for the purposes of dual-shade invocation.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index b02cccc2bb8d..4959224ead2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -146,9 +146,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
.thenReturn(mock(ResolveInfo::class.java))
- mSetFlagsRule.disableFlags(
- com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
- )
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
subject = createOverviewProxyService(context)
}
@@ -283,6 +281,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
statusBarWinController,
sysUiState,
mock(),
+ mock(),
userTracker,
userManager,
wakefulnessLifecycle,
@@ -294,7 +293,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
dumpManager,
unfoldTransitionProgressForwarder,
broadcastDispatcher,
- keyboardTouchpadEduStatsInteractor
+ keyboardTouchpadEduStatsInteractor,
)
}
}
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
index b65a90200837..a1750cdd0c84 100644
--- 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
@@ -16,6 +16,8 @@
package com.android.systemui.shade.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
@@ -27,16 +29,18 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
+import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.testKosmos
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,7 +57,12 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
private val sceneInteractor = kosmos.sceneInteractor
private val shadeTestUtil = kosmos.shadeTestUtil
- private val underTest = kosmos.shadeInteractorSceneContainerImpl
+ private lateinit var underTest: ShadeInteractorSceneContainerImpl
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.shadeInteractorSceneContainerImpl
+ }
@Test
fun qsExpansionWhenInSplitShadeAndQsExpanded() =
@@ -80,7 +89,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
// THEN legacy shade expansion is passed through
- Truth.assertThat(actual).isEqualTo(.3f)
+ assertThat(actual).isEqualTo(.3f)
}
@Test
@@ -109,7 +118,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN shade expansion is zero
- Truth.assertThat(actual).isEqualTo(.7f)
+ assertThat(actual).isEqualTo(.7f)
}
@Test
@@ -134,7 +143,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN QS is not fullscreen
- Truth.assertThat(actual).isFalse()
+ assertThat(actual).isFalse()
}
@Test
@@ -152,7 +161,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN QS is not fullscreen
- Truth.assertThat(actual).isFalse()
+ assertThat(actual).isFalse()
}
@Test
@@ -171,7 +180,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN QS is not fullscreen
- Truth.assertThat(actual).isFalse()
+ assertThat(actual).isFalse()
}
@Test
@@ -189,7 +198,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN QS is fullscreen
- Truth.assertThat(actual).isTrue()
+ assertThat(actual).isTrue()
}
@Test
@@ -206,7 +215,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN expansion is 1
- Truth.assertThat(expansionAmount).isEqualTo(1f)
+ assertThat(expansionAmount).isEqualTo(1f)
}
@Test
@@ -224,7 +233,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN expansion is 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ assertThat(expansionAmount).isEqualTo(0f)
}
@Test
@@ -251,19 +260,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN expansion is 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ 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)
+ assertThat(expansionAmount).isEqualTo(.4f)
// WHEN transition completes
progress.value = 1f
// THEN expansion is 1
- Truth.assertThat(expansionAmount).isEqualTo(1f)
+ assertThat(expansionAmount).isEqualTo(1f)
}
@Test
@@ -290,19 +299,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN expansion is 1
- Truth.assertThat(expansionAmount).isEqualTo(1f)
+ 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)
+ assertThat(expansionAmount).isEqualTo(.6f)
// WHEN transition completes
progress.value = 1f
// THEN expansion is 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ assertThat(expansionAmount).isEqualTo(0f)
}
fun isQsBypassingShade_goneToQs() =
@@ -326,7 +335,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN qs is bypassing shade
- Truth.assertThat(actual).isTrue()
+ assertThat(actual).isTrue()
}
fun isQsBypassingShade_shadeToQs() =
@@ -350,7 +359,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
runCurrent()
// THEN qs is not bypassing shade
- Truth.assertThat(actual).isFalse()
+ assertThat(actual).isFalse()
}
@Test
@@ -376,19 +385,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN expansion is 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ assertThat(expansionAmount).isEqualTo(0f)
// WHEN transition state is partially complete
progress.value = .4f
// THEN expansion is still 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ assertThat(expansionAmount).isEqualTo(0f)
// WHEN transition completes
progress.value = 1f
// THEN expansion is still 0
- Truth.assertThat(expansionAmount).isEqualTo(0f)
+ assertThat(expansionAmount).isEqualTo(0f)
}
@Test
@@ -405,7 +414,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
}
@Test
@@ -432,19 +441,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
// WHEN transition state is partially to the scene
progress.value = .4f
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
// WHEN transition completes
progress.value = 1f
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
}
@Test
@@ -471,19 +480,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
// WHEN transition state is partially to the scene
progress.value = .4f
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
// WHEN transition completes
progress.value = 1f
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
}
@Test
@@ -510,19 +519,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
// WHEN transition state is partially to the scene
progress.value = .4f
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
// WHEN transition completes
progress.value = 1f
// THEN interacting is false
- Truth.assertThat(interacting).isFalse()
+ assertThat(interacting).isFalse()
}
@Test
@@ -549,19 +558,19 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
sceneInteractor.setTransitionState(transitionState)
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
// WHEN transition state is partially to the scene
progress.value = .4f
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
// WHEN transition completes
progress.value = 1f
// THEN interacting is true
- Truth.assertThat(interacting).isTrue()
+ assertThat(interacting).isTrue()
}
@Test
@@ -572,7 +581,6 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val interacting by collectLastValue(interactingFlow)
// WHEN transition state is starting to between different scenes
- val progress = MutableStateFlow(0f)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
@@ -589,4 +597,94 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
// THEN interacting is false
assertThat(interacting).isFalse()
}
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun expandNotificationShade_dualShadeEnabled_opensOverlay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+
+ underTest.expandNotificationShade("reason")
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun expandNotificationShade_dualShadeDisabled_switchesToShadeScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+
+ underTest.expandNotificationShade("reason")
+
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun expandNotificationShade_dualShadeEnabledAndQuickSettingsOpen_replacesOverlay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ underTest.expandQuickSettingsShade("reason")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
+
+ underTest.expandNotificationShade("reason")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun expandQuickSettingsShade_dualShadeEnabled_opensOverlay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+
+ underTest.expandQuickSettingsShade("reason")
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun expandQuickSettingsShade_dualShadeDisabled_switchesToQuickSettingsScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+
+ underTest.expandQuickSettingsShade("reason")
+
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun expandQuickSettingsShade_dualShadeEnabledAndNotificationsOpen_replacesOverlay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ underTest.expandNotificationShade("reason")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
+
+ underTest.expandQuickSettingsShade("reason")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 04d930c72792..92075ea75c4a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -44,7 +44,7 @@ val Kosmos.shadeInteractorSceneContainerImpl by
ShadeInteractorSceneContainerImpl(
scope = applicationCoroutineScope,
sceneInteractor = sceneInteractor,
- shadeRepository = shadeRepository,
+ shadeModeInteractor = shadeModeInteractor,
)
}
val Kosmos.shadeInteractorLegacyImpl by