Loading packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt +51 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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. Loading @@ -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( Loading @@ -56,6 +77,7 @@ constructor( started = SharingStarted.Eagerly, initialValue = false, ) private val isBouncerShowing: StateFlow<Boolean> = keyguardTransitionInteractor .transitionValue(state = KeyguardState.PRIMARY_BOUNCER) Loading Loading @@ -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() Loading Loading @@ -162,7 +168,8 @@ constructor( } true } else { false onMove(event) isDraggingShade() } } ?: false Loading Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +18 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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; Loading @@ -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 = Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; } Loading packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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() Loading @@ -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, ) } Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt +51 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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. Loading @@ -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( Loading @@ -56,6 +77,7 @@ constructor( started = SharingStarted.Eagerly, initialValue = false, ) private val isBouncerShowing: StateFlow<Boolean> = keyguardTransitionInteractor .transitionValue(state = KeyguardState.PRIMARY_BOUNCER) Loading Loading @@ -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() Loading Loading @@ -162,7 +168,8 @@ constructor( } true } else { false onMove(event) isDraggingShade() } } ?: false Loading Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +18 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; /** Loading Loading @@ -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; Loading @@ -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 = Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; } Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt +48 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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() Loading @@ -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, ) } Loading @@ -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