summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt256
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt166
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt55
8 files changed, 491 insertions, 176 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 3c0ec350c5c5..aabd212c1bd3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.merge
class KeyguardTransitionInteractor
@Inject
constructor(
- repository: KeyguardTransitionRepository,
+ private val repository: KeyguardTransitionRepository,
) {
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
@@ -62,10 +62,6 @@ constructor(
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
- /** LOCKSCREEN->PRIMARY_BOUNCER transition information. */
- val mLockscreenToPrimaryBouncerTransition: Flow<TransitionStep> =
- repository.transition(LOCKSCREEN, PRIMARY_BOUNCER)
-
/** LOCKSCREEN->DREAMING transition information. */
val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING)
@@ -92,19 +88,39 @@ constructor(
lockscreenToAodTransition,
)
- /* The last [TransitionStep] with a [TransitionState] of STARTED */
+ /** The last [TransitionStep] with a [TransitionState] of STARTED */
val startedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
- /* The last [TransitionStep] with a [TransitionState] of CANCELED */
+ /** The last [TransitionStep] with a [TransitionState] of CANCELED */
val canceledKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
- /* The last [TransitionStep] with a [TransitionState] of FINISHED */
+ /** The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
- /* The last completed [KeyguardState] transition */
+ /** The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> =
finishedKeyguardTransitionStep.map { step -> step.to }
+
+ /**
+ * The amount of transition into or out of the given [KeyguardState].
+ *
+ * The value will be `0` (or close to `0`, due to float point arithmetic) if not in this step or
+ * `1` when fully in the given state.
+ */
+ fun transitionValue(
+ state: KeyguardState,
+ ): Flow<Float> {
+ return repository.transitions
+ .filter { it.from == state || it.to == state }
+ .map {
+ if (it.from == state) {
+ 1 - it.value
+ } else {
+ it.value
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
index ff7c9015eef4..33b3087678e1 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
@@ -19,13 +19,20 @@ package com.android.systemui.multishade.domain.interactor
import android.content.Context
import android.view.MotionEvent
import android.view.ViewConfiguration
+import com.android.systemui.classifier.Classifier
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.multishade.shared.math.isZero
import com.android.systemui.multishade.shared.model.ProxiedInputModel
+import com.android.systemui.plugins.FalsingManager
import javax.inject.Inject
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/**
@@ -39,15 +46,27 @@ class MultiShadeMotionEventInteractor
constructor(
@Application private val applicationContext: Context,
@Application private val applicationScope: CoroutineScope,
- private val interactor: MultiShadeInteractor,
+ private val multiShadeInteractor: MultiShadeInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val bouncerInteractor: PrimaryBouncerInteractor,
+ private val falsingManager: FalsingManager,
) {
private val isAnyShadeExpanded: StateFlow<Boolean> =
- interactor.isAnyShadeExpanded.stateIn(
+ multiShadeInteractor.isAnyShadeExpanded.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue = false,
)
+ private val isBouncerShowing: StateFlow<Boolean> =
+ keyguardTransitionInteractor
+ .transitionValue(state = KeyguardState.PRIMARY_BOUNCER)
+ .map { !it.isZero() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
private var interactionState: InteractionState? = null
@@ -65,6 +84,10 @@ constructor(
return false
}
+ if (isBouncerShowing.value) {
+ return false
+ }
+
return when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
// Record where the pointer was placed and which pointer it was.
@@ -75,7 +98,7 @@ constructor(
currentY = event.y,
pointerId = event.getPointerId(0),
isDraggingHorizontally = false,
- isDraggingVertically = false,
+ draggedVertically = Dragged.NONE,
)
false
@@ -85,16 +108,25 @@ constructor(
val pointerIndex = event.findPointerIndex(it.pointerId)
val currentX = event.getX(pointerIndex)
val currentY = event.getY(pointerIndex)
- if (!it.isDraggingHorizontally && !it.isDraggingVertically) {
- val xDistanceTravelled = abs(currentX - it.initialX)
- val yDistanceTravelled = abs(currentY - it.initialY)
+ if (!it.isDraggingHorizontally && it.draggedVertically == Dragged.NONE) {
+ val xDistanceTravelled = currentX - it.initialX
+ val yDistanceTravelled = currentY - it.initialY
val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop
interactionState =
when {
- yDistanceTravelled > touchSlop ->
- it.copy(isDraggingVertically = true)
- xDistanceTravelled > touchSlop ->
- it.copy(isDraggingHorizontally = true)
+ abs(yDistanceTravelled) > touchSlop ->
+ it.copy(
+ draggedVertically =
+ if (yDistanceTravelled > 0) {
+ Dragged.SHADE
+ } else {
+ Dragged.BOUNCER
+ }
+ )
+ abs(xDistanceTravelled) > touchSlop ->
+ it.copy(
+ isDraggingHorizontally = true,
+ )
else -> interactionState
}
}
@@ -124,7 +156,7 @@ constructor(
return when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> {
interactionState?.let {
- if (it.isDraggingVertically) {
+ if (it.draggedVertically != Dragged.NONE) {
val pointerIndex = event.findPointerIndex(it.pointerId)
val previousY = it.currentY
val currentY = event.getY(pointerIndex)
@@ -133,32 +165,48 @@ constructor(
currentY = currentY,
)
- val yDragAmountPx = currentY - previousY
- if (yDragAmountPx != 0f) {
- interactor.sendProxiedInput(
- ProxiedInputModel.OnDrag(
- xFraction = event.x / viewWidthPx,
- yDragAmountPx = yDragAmountPx,
- )
- )
+ when (it.draggedVertically) {
+ Dragged.SHADE -> {
+ val yDragAmountPx = currentY - previousY
+
+ if (yDragAmountPx != 0f) {
+ multiShadeInteractor.sendProxiedInput(
+ ProxiedInputModel.OnDrag(
+ xFraction = event.x / viewWidthPx,
+ yDragAmountPx = yDragAmountPx,
+ )
+ )
+ }
+ true
+ }
+ Dragged.BOUNCER -> {
+ bouncerInteractor.show(isScrimmed = true)
+ false
+ }
+ else -> false
}
+ } else {
+ false
}
}
-
- true
+ ?: false
}
MotionEvent.ACTION_UP -> {
- if (interactionState.isDraggingVertically()) {
+ if (interactionState?.draggedVertically == Dragged.SHADE) {
// We finished dragging. Record that so the multi-shade framework can issue a
// fling, if the velocity reached in the drag was high enough, for example.
- interactor.sendProxiedInput(ProxiedInputModel.OnDragEnd)
+ multiShadeInteractor.sendProxiedInput(ProxiedInputModel.OnDragEnd)
+
+ if (falsingManager.isFalseTouch(Classifier.SHADE_DRAG)) {
+ multiShadeInteractor.collapseAll()
+ }
}
interactionState = null
true
}
MotionEvent.ACTION_CANCEL -> {
- if (interactionState.isDraggingVertically()) {
+ if (interactionState?.draggedVertically == Dragged.SHADE) {
// Our drag gesture was canceled by the system. This happens primarily in one of
// two occasions: (a) the parent view has decided to intercept the gesture
// itself and/or route it to a different child view or (b) the pointer has
@@ -166,7 +214,15 @@ constructor(
// we pass the cancellation event to the multi-shade framework to record it.
// Doing that allows the multi-shade framework to know that the gesture ended to
// allow new gestures to be accepted.
- interactor.sendProxiedInput(ProxiedInputModel.OnDragCancel)
+ multiShadeInteractor.sendProxiedInput(ProxiedInputModel.OnDragCancel)
+
+ if (falsingManager.isFalseTouch(Classifier.SHADE_DRAG)) {
+ multiShadeInteractor.collapseAll()
+ }
+ } else if (interactionState?.draggedVertically == Dragged.BOUNCER) {
+ if (falsingManager.isFalseTouch(Classifier.BOUNCER_UNLOCK)) {
+ bouncerInteractor.hide()
+ }
}
interactionState = null
@@ -181,11 +237,23 @@ constructor(
val initialY: Float,
val currentY: Float,
val pointerId: Int,
+ /** Whether the current gesture is dragging horizontally. */
val isDraggingHorizontally: Boolean,
- val isDraggingVertically: Boolean,
+ /** The UI component that is being dragged vertically, if any. */
+ val draggedVertically: Dragged,
)
+ /** Enumerates the UI components that can be dragged by the user. */
+ private enum class Dragged {
+ /** The bouncer is being dragged by the user. */
+ BOUNCER,
+ /** A shade is being dragged by the user. */
+ SHADE,
+ /** No UI component is being dragged by the user. */
+ NONE,
+ }
+
private fun InteractionState?.isDraggingVertically(): Boolean {
- return this?.isDraggingVertically == true
+ return this?.draggedVertically != Dragged.NONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 1cb314a5986e..636058bd9717 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -234,6 +234,8 @@ import com.android.systemui.util.Utils;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -244,7 +246,6 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
@CentralSurfacesComponent.CentralSurfacesScope
@@ -388,6 +389,7 @@ public final class NotificationPanelViewController implements Dumpable {
private KeyguardBottomAreaView mKeyguardBottomArea;
private boolean mExpanding;
private boolean mSplitShadeEnabled;
+ private final boolean mMultiShadeEnabled;
/** The bottom padding reserved for elements of the keyguard measuring notifications. */
private float mKeyguardNotificationBottomPadding;
/**
@@ -852,6 +854,7 @@ public final class NotificationPanelViewController implements Dumpable {
mFeatureFlags = featureFlags;
mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
mTrackpadGestureBack = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
+ mMultiShadeEnabled = mFeatureFlags.isEnabled(Flags.DUAL_SHADE);
mFalsingCollector = falsingCollector;
mPowerManager = powerManager;
mWakeUpCoordinator = coordinator;
@@ -4021,8 +4024,11 @@ public final class NotificationPanelViewController implements Dumpable {
* {@link #updateVisibility()}? That would allow us to make this method private.
*/
public void updatePanelExpansionAndVisibility() {
- mShadeExpansionStateManager.onPanelExpansionChanged(
- mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+ if (!mMultiShadeEnabled) {
+ mShadeExpansionStateManager.onPanelExpansionChanged(
+ mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+ }
+
updateVisibility();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 3d13d8092651..276b3e39180b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -20,9 +20,10 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
@@ -30,9 +31,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -53,138 +52,151 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
}
@Test
- fun `transition collectors receives only appropriate events`() =
- runTest(UnconfinedTestDispatcher()) {
- var lockscreenToAodSteps = mutableListOf<TransitionStep>()
- val job1 =
- underTest.lockscreenToAodTransition
- .onEach { lockscreenToAodSteps.add(it) }
- .launchIn(this)
-
- var aodToLockscreenSteps = mutableListOf<TransitionStep>()
- val job2 =
- underTest.aodToLockscreenTransition
- .onEach { aodToLockscreenSteps.add(it) }
- .launchIn(this)
-
- val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
- steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))
-
- steps.forEach { repository.sendTransitionStep(it) }
-
- assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
- assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))
-
- job1.cancel()
- job2.cancel()
+ fun `transition collectors receives only appropriate events`() = runTest {
+ val lockscreenToAodSteps by collectValues(underTest.lockscreenToAodTransition)
+ val aodToLockscreenSteps by collectValues(underTest.aodToLockscreenTransition)
+
+ val steps = mutableListOf<TransitionStep>()
+ steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+ steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
}
- @Test
- fun dozeAmountTransitionTest() =
- runTest(UnconfinedTestDispatcher()) {
- var dozeAmountSteps = mutableListOf<TransitionStep>()
- val job = underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
-
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
-
- steps.forEach { repository.sendTransitionStep(it) }
-
- assertThat(dozeAmountSteps.subList(0, 3))
- .isEqualTo(
- listOf(
- steps[0].copy(value = 1f - steps[0].value),
- steps[1].copy(value = 1f - steps[1].value),
- steps[2].copy(value = 1f - steps[2].value),
- )
- )
- assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
-
- job.cancel()
- }
+ assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
+ assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))
+ }
@Test
- fun keyguardStateTests() =
- runTest(UnconfinedTestDispatcher()) {
- var finishedSteps = mutableListOf<KeyguardState>()
- val job = underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
-
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
- steps.forEach { repository.sendTransitionStep(it) }
-
- assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
-
- job.cancel()
+ fun dozeAmountTransitionTest() = runTest {
+ val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
}
- @Test
- fun finishedKeyguardTransitionStepTests() =
- runTest(UnconfinedTestDispatcher()) {
- var finishedSteps = mutableListOf<TransitionStep>()
- val job =
- underTest.finishedKeyguardTransitionStep.onEach { finishedSteps.add(it) }.launchIn(this)
-
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
- steps.forEach { repository.sendTransitionStep(it) }
-
- assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5]))
+ assertThat(dozeAmountSteps.subList(0, 3))
+ .isEqualTo(
+ listOf(
+ steps[0].copy(value = 1f - steps[0].value),
+ steps[1].copy(value = 1f - steps[1].value),
+ steps[2].copy(value = 1f - steps[2].value),
+ )
+ )
+ assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
+ }
- job.cancel()
+ @Test
+ fun keyguardStateTests() = runTest {
+ val finishedSteps by collectValues(underTest.finishedKeyguardState)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
}
+ assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
+ }
+
@Test
- fun startedKeyguardTransitionStepTests() =
- runTest(UnconfinedTestDispatcher()) {
- var startedSteps = mutableListOf<TransitionStep>()
- val job =
- underTest.startedKeyguardTransitionStep.onEach { startedSteps.add(it) }.launchIn(this)
+ fun finishedKeyguardTransitionStepTests() = runTest {
+ val finishedSteps by collectValues(underTest.finishedKeyguardTransitionStep)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
- val steps = mutableListOf<TransitionStep>()
+ assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5]))
+ }
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+ @Test
+ fun startedKeyguardTransitionStepTests() = runTest {
+ val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
- steps.forEach { repository.sendTransitionStep(it) }
+ assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6]))
+ }
- assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6]))
+ @Test
+ fun transitionValue() = runTest {
+ val startedSteps by collectValues(underTest.transitionValue(state = DOZING))
+
+ val toSteps =
+ listOf(
+ TransitionStep(AOD, DOZING, 0f, STARTED),
+ TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+ TransitionStep(AOD, DOZING, 1f, FINISHED),
+ )
+ toSteps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
- job.cancel()
+ val fromSteps =
+ listOf(
+ TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
+ TransitionStep(DOZING, LOCKSCREEN, 0.5f, RUNNING),
+ TransitionStep(DOZING, LOCKSCREEN, 1f, FINISHED),
+ )
+ fromSteps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
}
+
+ assertThat(startedSteps).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
index f807146cdf12..c6db565d7954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -20,7 +20,14 @@ import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
import com.android.systemui.multishade.data.repository.MultiShadeRepository
import com.android.systemui.multishade.shared.model.ProxiedInputModel
@@ -36,12 +43,18 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
+ @Mock private lateinit var bouncerInteractor: PrimaryBouncerInteractor
+
private lateinit var underTest: MultiShadeMotionEventInteractor
private lateinit var testScope: TestScope
@@ -49,9 +62,13 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
private lateinit var repository: MultiShadeRepository
private lateinit var interactor: MultiShadeInteractor
private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var falsingManager: FalsingManagerFake
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
testScope = TestScope()
motionEvents = mutableSetOf()
@@ -67,11 +84,19 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
repository = repository,
inputProxy = inputProxy,
)
+ keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ falsingManager = FalsingManagerFake()
underTest =
MultiShadeMotionEventInteractor(
applicationContext = context,
applicationScope = testScope.backgroundScope,
- interactor = interactor,
+ multiShadeInteractor = interactor,
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(
+ repository = keyguardTransitionRepository,
+ ),
+ bouncerInteractor = bouncerInteractor,
+ falsingManager = falsingManager,
)
}
@@ -202,6 +227,60 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
}
@Test
+ fun shouldIntercept_moveAboveTouchSlopAndUp_butBouncerShowing_returnsFalse() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 0.1f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isFalse()
+ }
+
+ @Test
+ fun shouldIntercept_moveAboveTouchSlopAndCancel_butBouncerShowing_returnsFalse() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 0.1f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isFalse()
+ }
+
+ @Test
fun tap_doesNotSendProxiedInput() =
testScope.runTest {
val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
@@ -233,7 +312,7 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
}
@Test
- fun dragAboveTouchSlopAndUp() =
+ fun dragShadeAboveTouchSlopAndUp() =
testScope.runTest {
val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
@@ -277,7 +356,7 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
}
@Test
- fun dragAboveTouchSlopAndCancel() =
+ fun dragShadeAboveTouchSlopAndCancel() =
testScope.runTest {
val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
@@ -320,6 +399,87 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
assertThat(singleShadeProxiedInput).isNull()
}
+ @Test
+ fun dragBouncerAboveTouchSlopAndUp_showsBouncer() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_DOWN,
+ x = 100f, // left shade
+ )
+ )
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val yDragAmountPx = -(touchSlop + 1f) // dragging up
+ val moveEvent =
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ x = 100f, // left shade
+ y = yDragAmountPx,
+ )
+ assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+ underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+ verify(bouncerInteractor).show(isScrimmed = true)
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val upEvent = motionEvent(MotionEvent.ACTION_UP)
+ assertThat(underTest.shouldIntercept(upEvent)).isTrue()
+ underTest.onTouchEvent(upEvent, viewWidthPx = 1000)
+ verify(bouncerInteractor, never()).hide()
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
+ @Test
+ fun dragBouncerAboveTouchSlopAndCancel_falseTouch_showsThenHidesBouncer() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_DOWN,
+ x = 900f, // right shade
+ )
+ )
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val yDragAmountPx = -(touchSlop + 1f) // drag up
+ val moveEvent =
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ x = 900f, // right shade
+ y = yDragAmountPx,
+ )
+ assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+ underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+ verify(bouncerInteractor).show(isScrimmed = true)
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ falsingManager.setIsFalseTouch(true)
+ val cancelEvent = motionEvent(MotionEvent.ACTION_CANCEL)
+ assertThat(underTest.shouldIntercept(cancelEvent)).isTrue()
+ underTest.onTouchEvent(cancelEvent, viewWidthPx = 1000)
+ verify(bouncerInteractor).hide()
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
private fun TestScope.motionEvent(
action: Int,
downTime: Long = currentTime,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 5f34b2f0f87f..006a7c735550 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -27,10 +27,12 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dock.DockManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
@@ -67,8 +69,8 @@ import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -170,7 +172,13 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
MultiShadeMotionEventInteractor(
applicationContext = context,
applicationScope = testScope.backgroundScope,
- interactor = multiShadeInteractor,
+ multiShadeInteractor = multiShadeInteractor,
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(
+ repository = FakeKeyguardTransitionRepository(),
+ ),
+ bouncerInteractor = com.android.systemui.util.mockito.mock(),
+ falsingManager = FalsingManagerFake(),
)
},
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index b40181e24e6d..1a52067e8306 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -27,10 +27,12 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dock.DockManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -182,7 +184,13 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
MultiShadeMotionEventInteractor(
applicationContext = context,
applicationScope = testScope.backgroundScope,
- interactor = multiShadeInteractor,
+ multiShadeInteractor = multiShadeInteractor,
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(
+ repository = FakeKeyguardTransitionRepository(),
+ ),
+ bouncerInteractor = mock(),
+ falsingManager = FalsingManagerFake(),
)
},
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
index c2947b42f56d..e1ba074ac860 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("OPT_IN_USAGE")
+
package com.android.systemui.coroutines
import kotlin.coroutines.CoroutineContext
@@ -43,20 +45,55 @@ fun <T> TestScope.collectLastValue(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
): FlowValue<T?> {
- var lastValue: T? = null
- backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
- return FlowValueImpl {
+ val values by
+ collectValues(
+ flow = flow,
+ context = context,
+ start = start,
+ )
+ return FlowValueImpl { values.lastOrNull() }
+}
+
+/**
+ * Collect [flow] in a new [Job] and return a getter for the collection of values collected.
+ *
+ * ```
+ * fun myTest() = runTest {
+ * // ...
+ * val values by collectValues(underTest.flow)
+ * assertThat(values).isEqualTo(listOf(expected1, expected2, ...))
+ * }
+ * ```
+ */
+fun <T> TestScope.collectValues(
+ flow: Flow<T>,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+): FlowValues<T> {
+ val values = mutableListOf<T>()
+ backgroundScope.launch(context, start) { flow.collect(values::add) }
+ return FlowValuesImpl {
runCurrent()
- lastValue
+ values.toList()
}
}
/** @see collectLastValue */
-interface FlowValue<T> : ReadOnlyProperty<Any?, T?> {
- operator fun invoke(): T?
+interface FlowValue<T> : ReadOnlyProperty<Any?, T> {
+ operator fun invoke(): T
+}
+
+/** @see collectValues */
+interface FlowValues<T> : ReadOnlyProperty<Any?, List<T>> {
+ operator fun invoke(): List<T>
+}
+
+private class FlowValueImpl<T>(private val block: () -> T) : FlowValue<T> {
+ override operator fun invoke(): T = block()
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T = invoke()
}
-private class FlowValueImpl<T>(private val block: () -> T?) : FlowValue<T> {
- override operator fun invoke(): T? = block()
- override fun getValue(thisRef: Any?, property: KProperty<*>): T? = invoke()
+private class FlowValuesImpl<T>(private val block: () -> List<T>) : FlowValues<T> {
+ override operator fun invoke(): List<T> = block()
+ override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> = invoke()
}