Merge "[multi-shade] Launcher touch integration" into udc-dev
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 e352c61..1894bc4 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
@@ -20,12 +20,16 @@
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.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 @@
* 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 @@
started = SharingStarted.Eagerly,
initialValue = false,
)
+
private val isBouncerShowing: StateFlow<Boolean> =
keyguardTransitionInteractor
.transitionValue(state = KeyguardState.PRIMARY_BOUNCER)
@@ -102,23 +124,7 @@
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 @@
}
true
} else {
- false
+ onMove(event)
+ isDraggingShade()
}
}
?: false
@@ -225,6 +232,32 @@
}
}
+ /**
+ * 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,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d2cb762..b7243ae9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -158,6 +158,7 @@
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 @@
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 @@
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 @@
}
};
+ private final Consumer<Boolean> mMultiShadeExpansionConsumer =
+ (Boolean expanded) -> mIsAnyMultiShadeExpanded = expanded;
+
private final Consumer<TransitionStep> mDreamingToLockscreenTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
@@ -761,6 +768,7 @@
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
+ Provider<MultiShadeInteractor> multiShadeInteractorProvider,
DumpManager dumpManager,
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor) {
@@ -861,6 +869,8 @@
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 @@
mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
controller.setup(mNotificationContainerParent));
+ if (mDualShadeEnabled) {
+ collectFlow(mView, mMultiShadeInteractor.isAnyShadeExpanded(),
+ mMultiShadeExpansionConsumer, mMainDispatcher);
+ }
+
// Dreaming->Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
mDreamingToLockscreenTransition, mMainDispatcher);
@@ -4617,7 +4632,9 @@
mQsController.setExpandImmediate(false);
// Close the status bar in the next frame so we can show the end of the
// animation.
- mView.post(mMaybeHideExpandedRunnable);
+ if (!mIsAnyMultiShadeExpanded) {
+ mView.post(mMaybeHideExpandedRunnable);
+ }
}
mCurrentPanelState = state;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index ad5a68e4d..e08bc33 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -64,6 +64,11 @@
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
* method makes distinction between shade being truly open and plain keyguard state:
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 826b3ee..c71467b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -154,6 +154,11 @@
}
@Override
+ public boolean isKeyguard() {
+ return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ }
+
+ @Override
public boolean isShadeFullyOpen() {
return mNotificationPanelViewController.isShadeFullyExpanded();
}
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 acde887..19f9960 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
@@ -22,6 +22,8 @@
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.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.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 @@
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 @@
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,
)
}
@@ -96,6 +111,39 @@
}
@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 {
assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 1bd13aa..7b37ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -103,6 +103,7 @@
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.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
@@ -286,6 +287,7 @@
@Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock protected MultiShadeInteractor mMultiShadeInteractor;
@Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
@Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock protected MotionEvent mDownMotionEvent;
@@ -570,6 +572,7 @@
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,
+ () -> mMultiShadeInteractor,
mDumpManager,
mKeyuardLongPressViewModel,
mKeyguardInteractor);
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 2a10823..bc8ab1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -86,6 +86,7 @@
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@@ -173,11 +174,13 @@
applicationContext = context,
applicationScope = testScope.backgroundScope,
multiShadeInteractor = multiShadeInteractor,
+ featureFlags = featureFlags,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
),
falsingManager = FalsingManagerFake(),
+ shadeController = shadeController,
)
},
)
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 86660a4..56385b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -185,11 +185,13 @@
applicationContext = context,
applicationScope = testScope.backgroundScope,
multiShadeInteractor = multiShadeInteractor,
+ featureFlags = featureFlags,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
),
falsingManager = FalsingManagerFake(),
+ shadeController = shadeController,
)
},
)