Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d830c75 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge changes I81118cb2,Iab6a7568,I9f7ff736 into udc-dev

* changes:
  [multi-shade] Bouncer touch integration.
  KeyguardTransitionInteractor#transitionValue.
  Adds Flow extension for collecting values into a list.
parents 9d56ebbc 58cd6126
Loading
Loading
Loading
Loading
+25 −9
Original line number Diff line number Diff line
@@ -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
                }
            }
    }
}
+95 −27
Original line number Diff line number Diff line
@@ -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,11 +46,23 @@ 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,
@@ -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,
                            )

                        when (it.draggedVertically) {
                            Dragged.SHADE -> {
                                val yDragAmountPx = currentY - previousY

                                if (yDragAmountPx != 0f) {
                            interactor.sendProxiedInput(
                                    multiShadeInteractor.sendProxiedInput(
                                        ProxiedInputModel.OnDrag(
                                            xFraction = event.x / viewWidthPx,
                                            yDragAmountPx = yDragAmountPx,
                                        )
                                    )
                                }
                                true
                            }
                            Dragged.BOUNCER -> {
                                bouncerInteractor.show(isScrimmed = true)
                                false
                            }

                true
                            else -> false
                        }
                    } else {
                        false
                    }
                }
                    ?: 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
    }
}
+9 −3
Original line number Diff line number Diff line
@@ -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() {
        if (!mMultiShadeEnabled) {
            mShadeExpansionStateManager.onPanelExpansionChanged(
                    mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
        }

        updateVisibility();
    }

+134 −122
Original line number Diff line number Diff line
@@ -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,19 +52,9 @@ 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)
    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))
@@ -77,20 +66,18 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
        steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
        steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))

            steps.forEach { repository.sendTransitionStep(it) }
        steps.forEach {
            repository.sendTransitionStep(it)
            runCurrent()
        }

        assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
        assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))

            job1.cancel()
            job2.cancel()
    }

    @Test
    fun dozeAmountTransitionTest() =
        runTest(UnconfinedTestDispatcher()) {
            var dozeAmountSteps = mutableListOf<TransitionStep>()
            val job = underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
    fun dozeAmountTransitionTest() = runTest {
        val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)

        val steps = mutableListOf<TransitionStep>()

@@ -102,7 +89,10 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
        steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
        steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))

            steps.forEach { repository.sendTransitionStep(it) }
        steps.forEach {
            repository.sendTransitionStep(it)
            runCurrent()
        }

        assertThat(dozeAmountSteps.subList(0, 3))
            .isEqualTo(
@@ -113,15 +103,11 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
                )
            )
        assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))

            job.cancel()
    }

    @Test
    fun keyguardStateTests() =
        runTest(UnconfinedTestDispatcher()) {
            var finishedSteps = mutableListOf<KeyguardState>()
            val job = underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
    fun keyguardStateTests() = runTest {
        val finishedSteps by collectValues(underTest.finishedKeyguardState)

        val steps = mutableListOf<TransitionStep>()

@@ -133,19 +119,17 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
        steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
        steps.add(TransitionStep(AOD, GONE, 1f, STARTED))

            steps.forEach { repository.sendTransitionStep(it) }
        steps.forEach {
            repository.sendTransitionStep(it)
            runCurrent()
        }

        assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))

            job.cancel()
    }

    @Test
    fun finishedKeyguardTransitionStepTests() =
        runTest(UnconfinedTestDispatcher()) {
            var finishedSteps = mutableListOf<TransitionStep>()
            val job =
                underTest.finishedKeyguardTransitionStep.onEach { finishedSteps.add(it) }.launchIn(this)
    fun finishedKeyguardTransitionStepTests() = runTest {
        val finishedSteps by collectValues(underTest.finishedKeyguardTransitionStep)

        val steps = mutableListOf<TransitionStep>()

@@ -157,19 +141,17 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
        steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
        steps.add(TransitionStep(AOD, GONE, 1f, STARTED))

            steps.forEach { repository.sendTransitionStep(it) }
        steps.forEach {
            repository.sendTransitionStep(it)
            runCurrent()
        }

        assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5]))

            job.cancel()
    }

    @Test
    fun startedKeyguardTransitionStepTests() =
        runTest(UnconfinedTestDispatcher()) {
            var startedSteps = mutableListOf<TransitionStep>()
            val job =
                underTest.startedKeyguardTransitionStep.onEach { startedSteps.add(it) }.launchIn(this)
    fun startedKeyguardTransitionStepTests() = runTest {
        val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)

        val steps = mutableListOf<TransitionStep>()

@@ -181,10 +163,40 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
        steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
        steps.add(TransitionStep(AOD, GONE, 1f, STARTED))

            steps.forEach { repository.sendTransitionStep(it) }
        steps.forEach {
            repository.sendTransitionStep(it)
            runCurrent()
        }

        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()
        }

        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()
        }

            job.cancel()
        assertThat(startedSteps).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
    }
}
+163 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading