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

Commit ca9ca830 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "[multi-shade] Launcher touch integration" into udc-dev am: 86060227 am: 8bc9b232

parents 7ada1bfa 8bc9b232
Loading
Loading
Loading
Loading
+51 −18
Original line number Diff line number Diff line
@@ -20,12 +20,16 @@ import android.content.Context
import android.view.MotionEvent
import android.view.ViewConfiguration
import com.android.systemui.classifier.Classifier
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
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 com.android.systemui.shade.ShadeController
import javax.inject.Inject
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
@@ -33,6 +37,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

/**
 * Encapsulates business logic to handle [MotionEvent]-based user input.
@@ -40,15 +45,31 @@ import kotlinx.coroutines.flow.stateIn
 * This class is meant purely for the legacy `View`-based system to be able to pass `MotionEvent`s
 * into the newer multi-shade framework for processing.
 */
@SysUISingleton
class MultiShadeMotionEventInteractor
@Inject
constructor(
    @Application private val applicationContext: Context,
    @Application private val applicationScope: CoroutineScope,
    private val multiShadeInteractor: MultiShadeInteractor,
    featureFlags: FeatureFlags,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    private val falsingManager: FalsingManager,
    private val shadeController: ShadeController,
) {
    init {
        if (featureFlags.isEnabled(Flags.DUAL_SHADE)) {
            applicationScope.launch {
                multiShadeInteractor.isAnyShadeExpanded.collect {
                    if (!it && !shadeController.isKeyguard) {
                        shadeController.makeExpandedInvisible()
                    } else {
                        shadeController.makeExpandedVisible(false)
                    }
                }
            }
        }
    }

    private val isAnyShadeExpanded: StateFlow<Boolean> =
        multiShadeInteractor.isAnyShadeExpanded.stateIn(
@@ -56,6 +77,7 @@ constructor(
            started = SharingStarted.Eagerly,
            initialValue = false,
        )

    private val isBouncerShowing: StateFlow<Boolean> =
        keyguardTransitionInteractor
            .transitionValue(state = KeyguardState.PRIMARY_BOUNCER)
@@ -102,23 +124,7 @@ constructor(
                false
            }
            MotionEvent.ACTION_MOVE -> {
                interactionState?.let {
                    val pointerIndex = event.findPointerIndex(it.pointerId)
                    val currentX = event.getX(pointerIndex)
                    val currentY = event.getY(pointerIndex)
                    if (!it.isDraggingHorizontally && !it.isDraggingShade) {
                        val xDistanceTravelled = currentX - it.initialX
                        val yDistanceTravelled = currentY - it.initialY
                        val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop
                        interactionState =
                            when {
                                yDistanceTravelled > touchSlop -> it.copy(isDraggingShade = true)
                                abs(xDistanceTravelled) > touchSlop ->
                                    it.copy(isDraggingHorizontally = true)
                                else -> interactionState
                            }
                    }
                }
                onMove(event)

                // We want to intercept the rest of the gesture if we're dragging the shade.
                isDraggingShade()
@@ -162,7 +168,8 @@ constructor(
                        }
                        true
                    } else {
                        false
                        onMove(event)
                        isDraggingShade()
                    }
                }
                    ?: false
@@ -225,6 +232,32 @@ constructor(
        }
    }

    /**
     * Handles [MotionEvent.ACTION_MOVE] and sets whether or not we are dragging shade in our
     * current interaction
     *
     * @param event The [MotionEvent] to handle.
     */
    private fun onMove(event: MotionEvent) {
        interactionState?.let {
            val pointerIndex = event.findPointerIndex(it.pointerId)
            val currentX = event.getX(pointerIndex)
            val currentY = event.getY(pointerIndex)
            if (!it.isDraggingHorizontally && !it.isDraggingShade) {
                val xDistanceTravelled = currentX - it.initialX
                val yDistanceTravelled = currentY - it.initialY
                val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop
                interactionState =
                    when {
                        yDistanceTravelled > touchSlop -> it.copy(isDraggingShade = true)
                        abs(xDistanceTravelled) > touchSlop ->
                            it.copy(isDraggingHorizontally = true)
                        else -> interactionState
                    }
            }
        }
    }

    private data class InteractionState(
        val initialX: Float,
        val initialY: Float,
+18 −1
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -392,6 +393,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
    private KeyguardBottomAreaView mKeyguardBottomArea;
    private boolean mExpanding;
    private boolean mSplitShadeEnabled;
    private boolean mDualShadeEnabled;
    /** The bottom padding reserved for elements of the keyguard measuring notifications. */
    private float mKeyguardNotificationBottomPadding;
    /**
@@ -623,7 +625,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump

    private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
    private final KeyguardInteractor mKeyguardInteractor;
    private final @Nullable MultiShadeInteractor mMultiShadeInteractor;
    private final CoroutineDispatcher mMainDispatcher;
    private boolean mIsAnyMultiShadeExpanded;
    private boolean mIsOcclusionTransitionRunning = false;
    private int mDreamingToLockscreenTransitionTranslationY;
    private int mOccludedToLockscreenTransitionTranslationY;
@@ -645,6 +649,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
        }
    };

    private final Consumer<Boolean> mMultiShadeExpansionConsumer =
            (Boolean expanded) -> mIsAnyMultiShadeExpanded = expanded;

    private final Consumer<TransitionStep> mDreamingToLockscreenTransition =
            (TransitionStep step) -> {
                mIsOcclusionTransitionRunning =
@@ -761,6 +768,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
            LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
            @Main CoroutineDispatcher mainDispatcher,
            KeyguardTransitionInteractor keyguardTransitionInteractor,
            Provider<MultiShadeInteractor> multiShadeInteractorProvider,
            DumpManager dumpManager,
            KeyguardLongPressViewModel keyguardLongPressViewModel,
            KeyguardInteractor keyguardInteractor) {
@@ -861,6 +869,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
        mFeatureFlags = featureFlags;
        mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
        mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
        mDualShadeEnabled = mFeatureFlags.isEnabled(Flags.DUAL_SHADE);
        mMultiShadeInteractor = mDualShadeEnabled ? multiShadeInteractorProvider.get() : null;
        mFalsingCollector = falsingCollector;
        mPowerManager = powerManager;
        mWakeUpCoordinator = coordinator;
@@ -1097,6 +1107,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
        mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
                controller.setup(mNotificationContainerParent));

        if (mDualShadeEnabled) {
            collectFlow(mView, mMultiShadeInteractor.isAnyShadeExpanded(),
                    mMultiShadeExpansionConsumer, mMainDispatcher);
        }

        // Dreaming->Lockscreen
        collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
                mDreamingToLockscreenTransition, mMainDispatcher);
@@ -4617,8 +4632,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
            mQsController.setExpandImmediate(false);
            // Close the status bar in the next frame so we can show the end of the
            // animation.
            if (!mIsAnyMultiShadeExpanded) {
                mView.post(mMaybeHideExpandedRunnable);
            }
        }
        mCurrentPanelState = state;
    }

+5 −0
Original line number Diff line number Diff line
@@ -63,6 +63,11 @@ public interface ShadeController {
     */
    boolean closeShadeIfOpen();

    /**
     * Returns whether the shade state is the keyguard or not.
     */
    boolean isKeyguard();

    /**
     * Returns whether the shade is currently open.
     * Even though in the current implementation shade is in expanded state on keyguard, this
+5 −0
Original line number Diff line number Diff line
@@ -153,6 +153,11 @@ public final class ShadeControllerImpl implements ShadeController {
        return false;
    }

    @Override
    public boolean isKeyguard() {
        return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
    }

    @Override
    public boolean isShadeFullyOpen() {
        return mNotificationPanelViewController.isShadeFullyExpanded();
+48 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ 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.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,6 +33,8 @@ import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
import com.android.systemui.multishade.data.repository.MultiShadeRepository
import com.android.systemui.multishade.shared.model.ProxiedInputModel
import com.android.systemui.multishade.shared.model.ShadeId
import com.android.systemui.shade.ShadeController
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -42,6 +46,10 @@ 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.anyBoolean
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -57,9 +65,11 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
    private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
    private lateinit var falsingManager: FalsingManagerFake
    @Mock private lateinit var shadeController: ShadeController

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        testScope = TestScope()
        motionEvents = mutableSetOf()

@@ -75,18 +85,23 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
                repository = repository,
                inputProxy = inputProxy,
            )
        val featureFlags = FakeFeatureFlags()
        featureFlags.set(Flags.DUAL_SHADE, true)
        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
        falsingManager = FalsingManagerFake()

        underTest =
            MultiShadeMotionEventInteractor(
                applicationContext = context,
                applicationScope = testScope.backgroundScope,
                multiShadeInteractor = interactor,
                featureFlags = featureFlags,
                keyguardTransitionInteractor =
                    KeyguardTransitionInteractor(
                        repository = keyguardTransitionRepository,
                    ),
                falsingManager = falsingManager,
                shadeController = shadeController,
            )
    }

@@ -95,6 +110,39 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
        motionEvents.forEach { motionEvent -> motionEvent.recycle() }
    }

    @Test
    fun listenForIsAnyShadeExpanded_expanded_makesWindowViewVisible() =
        testScope.runTest {
            whenever(shadeController.isKeyguard).thenReturn(false)
            repository.setExpansion(ShadeId.LEFT, 0.1f)
            val expanded by collectLastValue(interactor.isAnyShadeExpanded)
            assertThat(expanded).isTrue()

            verify(shadeController).makeExpandedVisible(anyBoolean())
        }

    @Test
    fun listenForIsAnyShadeExpanded_collapsed_makesWindowViewInvisible() =
        testScope.runTest {
            whenever(shadeController.isKeyguard).thenReturn(false)
            repository.setForceCollapseAll(true)
            val expanded by collectLastValue(interactor.isAnyShadeExpanded)
            assertThat(expanded).isFalse()

            verify(shadeController).makeExpandedInvisible()
        }

    @Test
    fun listenForIsAnyShadeExpanded_collapsedOnKeyguard_makesWindowViewVisible() =
        testScope.runTest {
            whenever(shadeController.isKeyguard).thenReturn(true)
            repository.setForceCollapseAll(true)
            val expanded by collectLastValue(interactor.isAnyShadeExpanded)
            assertThat(expanded).isFalse()

            verify(shadeController).makeExpandedVisible(anyBoolean())
        }

    @Test
    fun shouldIntercept_initialDown_returnsFalse() =
        testScope.runTest {
Loading