summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt107
13 files changed, 503 insertions, 10 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4a8fd1b00dde..0a858af31964 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1179,6 +1179,10 @@
<!-- Maximum over scroll amount for the shade when transition to the full shade. -->
<dimen name="lockscreen_shade_max_over_scroll_amount">24dp</dimen>
+ <!-- Maximum over scroll amount for the shade when transition to the full shade.
+ Only used for split-shade. -->
+ <dimen name="shade_max_over_scroll_amount">@dimen/lockscreen_shade_max_over_scroll_amount</dimen>
+
<!-- Maximum overshoot for the pulse expansion -->
<dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c2750c2d2a6f..2493ccbe5a48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -117,6 +117,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -178,6 +179,7 @@ public class NotificationStackScrollLayoutController {
private final CentralSurfaces mCentralSurfaces;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final ShadeTransitionController mShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateLogger mStackStateLogger;
@@ -647,6 +649,7 @@ public class NotificationStackScrollLayoutController {
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
+ ShadeTransitionController shadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
LayoutInflater layoutInflater,
@@ -675,6 +678,7 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ mShadeTransitionController = shadeTransitionController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mResources = resources;
@@ -769,6 +773,7 @@ public class NotificationStackScrollLayoutController {
mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
mLockscreenShadeTransitionController.setStackScroller(this);
+ mShadeTransitionController.setNotificationStackScrollLayoutController(this);
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 0b46f07f8e8b..a01071222199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -179,6 +179,7 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.panelstate.PanelState;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -310,6 +311,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final NotificationRemoteInputManager mRemoteInputManager;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final ShadeTransitionController mShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final RecordingController mRecordingController;
@@ -745,7 +747,8 @@ public class NotificationPanelViewController extends PanelViewController {
NotificationListContainer notificationListContainer,
PanelEventsEmitter panelEventsEmitter,
NotificationStackSizeCalculator notificationStackSizeCalculator,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ ShadeTransitionController shadeTransitionController) {
super(view,
falsingManager,
dozeLog,
@@ -826,7 +829,9 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ mShadeTransitionController = shadeTransitionController;
lockscreenShadeTransitionController.setNotificationPanelController(this);
+ shadeTransitionController.setNotificationPanelViewController(this);
DynamicPrivacyControlListener
dynamicPrivacyControlListener =
new DynamicPrivacyControlListener();
@@ -3613,6 +3618,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
});
mLockscreenShadeTransitionController.setQS(mQs);
+ mShadeTransitionController.setQs(mQs);
mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
mQs.setScrollListener(mScrollListener);
updateQsExpansion();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 9e707644782c..6637394e2b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -108,6 +108,8 @@ public abstract class PanelViewController {
*/
private boolean mIsSpringBackAnimation;
+ private boolean mInSplitShade;
+
private void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
@@ -303,8 +305,9 @@ public abstract class PanelViewController {
mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
mPanelFlingOvershootAmount = mResources.getDimension(R.dimen.panel_overshoot_amount);
- mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
- R.dimen.unlock_falsing_threshold);
+ mUnlockFalsingThreshold =
+ mResources.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
+ mInSplitShade = mResources.getBoolean(R.bool.config_use_split_notification_shade);
}
protected float getTouchSlop(MotionEvent event) {
@@ -600,10 +603,12 @@ public abstract class PanelViewController {
}
mIsFlinging = true;
// we want to perform an overshoot animation when flinging open
- final boolean addOverscroll = expand
- && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
- && mOverExpansion == 0.0f
- && vel >= 0;
+ final boolean addOverscroll =
+ expand
+ && !mInSplitShade // Split shade has its own overscroll logic
+ && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
+ && mOverExpansion == 0.0f
+ && vel >= 0;
final boolean shouldSpringBack = addOverscroll || (mOverExpansion != 0.0f && expand);
float overshootAmount = 0.0f;
if (addOverscroll) {
@@ -777,7 +782,8 @@ public abstract class PanelViewController {
}
float maxPanelHeight = getMaxPanelHeight();
if (mHeightAnimator == null) {
- if (mTracking) {
+ // Split shade has its own overscroll logic
+ if (mTracking && !mInSplitShade) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
index e29959290355..ca667dddbe8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.phone.panelstate
/** A listener interface to be notified of state change events for the notification panel. */
-interface PanelStateListener {
+fun interface PanelStateListener {
/** Called when the panel's expansion state has changed. */
fun onPanelStateChanged(@PanelState state: Int)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt
new file mode 100644
index 000000000000..2789db874249
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import javax.inject.Inject
+
+/**
+ * An implementation on [ShadeOverScroller] that does nothing.
+ *
+ * At the moment there is only a concrete implementation [ShadeOverScroller] for split-shade, so
+ * this one is used when we are not in split-shade.
+ */
+class NoOpOverScroller @Inject constructor() : ShadeOverScroller {
+ override fun onPanelStateChanged(newPanelState: Int) {}
+ override fun onDragDownAmountChanged(newDragDownAmount: Float) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt
new file mode 100644
index 000000000000..f1cedeb21e0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+
+/** Represents an over scroller for the non-lockscreen shade. */
+interface ShadeOverScroller {
+
+ fun onPanelStateChanged(@PanelState newPanelState: Int)
+
+ fun onDragDownAmountChanged(newDragDownAmount: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
new file mode 100644
index 000000000000..2762b9a38e92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
@@ -0,0 +1,73 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/** Controls the shade expansion transition on non-lockscreen. */
+@SysUISingleton
+class ShadeTransitionController
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ panelExpansionStateManager: PanelExpansionStateManager,
+ private val context: Context,
+ private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
+ private val noOpOverScroller: NoOpOverScroller
+) {
+
+ lateinit var notificationPanelViewController: NotificationPanelViewController
+ lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController
+ lateinit var qs: QS
+
+ private var inSplitShade = false
+
+ private val splitShadeOverScroller by lazy {
+ splitShadeOverScrollerFactory.create(qs, notificationStackScrollLayoutController)
+ }
+ private val shadeOverScroller: ShadeOverScroller
+ get() =
+ if (inSplitShade && propertiesInitialized()) {
+ splitShadeOverScroller
+ } else {
+ noOpOverScroller
+ }
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ panelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
+ panelExpansionStateManager.addStateListener(this::onPanelStateChanged)
+ }
+
+ private fun updateResources() {
+ inSplitShade = context.resources.getBoolean(R.bool.config_use_split_notification_shade)
+ }
+
+ private fun onPanelStateChanged(@PanelState state: Int) {
+ shadeOverScroller.onPanelStateChanged(state)
+ }
+
+ private fun onPanelExpansionChanged(event: PanelExpansionChangeEvent) {
+ shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
+ }
+
+ private fun propertiesInitialized() =
+ this::qs.isInitialized &&
+ this::notificationPanelViewController.isInitialized &&
+ this::notificationStackScrollLayoutController.isInitialized
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
new file mode 100644
index 000000000000..71050f2e7c67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
@@ -0,0 +1,142 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
+import android.util.MathUtils
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.phone.panelstate.STATE_CLOSED
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.io.PrintWriter
+
+class SplitShadeOverScroller
+@AssistedInject
+constructor(
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ private val context: Context,
+ private val scrimController: ScrimController,
+ @Assisted private val qS: QS,
+ @Assisted private val nsslController: NotificationStackScrollLayoutController
+) : ShadeOverScroller {
+
+ private var releaseOverScrollDuration = 0L
+ private var maxOverScrollAmount = 0
+ private var previousOverscrollAmount = 0
+ private var dragDownAmount: Float = 0f
+ @PanelState private var panelState: Int = STATE_CLOSED
+ private var releaseOverScrollAnimator: Animator? = null
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ dumpManager.registerDumpable(this::dump)
+ }
+
+ private fun updateResources() {
+ val resources = context.resources
+ maxOverScrollAmount = resources.getDimensionPixelSize(R.dimen.shade_max_over_scroll_amount)
+ releaseOverScrollDuration =
+ resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
+ }
+
+ override fun onPanelStateChanged(@PanelState newPanelState: Int) {
+ if (shouldReleaseOverscroll(previousState = panelState, newState = newPanelState)) {
+ releaseOverScroll()
+ }
+ panelState = newPanelState
+ }
+
+ override fun onDragDownAmountChanged(newDragDownAmount: Float) {
+ if (dragDownAmount == newDragDownAmount) {
+ return
+ }
+ dragDownAmount = newDragDownAmount
+ if (shouldOverscroll()) {
+ overScroll(newDragDownAmount)
+ }
+ }
+
+ private fun shouldOverscroll() = panelState == STATE_OPENING
+
+ private fun shouldReleaseOverscroll(@PanelState previousState: Int, @PanelState newState: Int) =
+ previousState == STATE_OPENING && newState != STATE_OPENING
+
+ private fun overScroll(dragDownAmount: Float) {
+ val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
+ applyOverscroll(overscrollAmount)
+ previousOverscrollAmount = overscrollAmount
+ }
+
+ private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
+ val fullHeight: Int = nsslController.height
+ val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
+ return (fullHeightProgress * maxOverScrollAmount).toInt()
+ }
+
+ private fun applyOverscroll(overscrollAmount: Int) {
+ qS.setOverScrollAmount(overscrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overscrollAmount)
+ nsslController.setOverScrollAmount(overscrollAmount)
+ }
+
+ private fun releaseOverScroll() {
+ val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
+ animator.addUpdateListener {
+ val overScrollAmount = it.animatedValue as Int
+ qS.setOverScrollAmount(overScrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overScrollAmount)
+ nsslController.setOverScrollAmount(overScrollAmount)
+ }
+ animator.interpolator = Interpolators.STANDARD
+ animator.duration = releaseOverScrollDuration
+ animator.start()
+ releaseOverScrollAnimator = animator
+ previousOverscrollAmount = 0
+ }
+
+ @VisibleForTesting
+ internal fun finishAnimations() {
+ releaseOverScrollAnimator?.end()
+ releaseOverScrollAnimator = null
+ }
+
+ private fun dump(pw: PrintWriter, strings: Array<String>) {
+ pw.println(
+ """
+ SplitShadeOverScroller:
+ Resources:
+ releaseOverScrollDuration: $releaseOverScrollDuration
+ maxOverScrollAmount: $maxOverScrollAmount
+ State:
+ previousOverscrollAmount: $previousOverscrollAmount
+ dragDownAmount: $dragDownAmount
+ panelState: $panelState
+ """.trimIndent())
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(
+ qS: QS,
+ nsslController: NotificationStackScrollLayoutController
+ ): SplitShadeOverScroller
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6409967aca9b..94a93ad6cf33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -135,6 +136,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
+ @Mock private ShadeTransitionController mShadeTransitionController;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -179,6 +181,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mNotifCollection,
mEntryManager,
mLockscreenShadeTransitionController,
+ mShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
mLayoutInflater,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index cad603c85bbc..00ac005845f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -128,6 +128,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -337,6 +338,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
+ private ShadeTransitionController mShadeTransitionController;
+ @Mock
private QS mQs;
@Mock
private View mQsHeader;
@@ -527,7 +530,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationListContainer,
mPanelEventsEmitter,
mNotificationStackSizeCalculator,
- mUnlockedScreenOffAnimationController);
+ mUnlockedScreenOffAnimationController,
+ mShadeTransitionController);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
new file mode 100644
index 000000000000..39d33e86925e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
@@ -0,0 +1,118 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShadeTransitionControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var npvc: NotificationPanelViewController
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+ @Mock private lateinit var qs: QS
+ @Mock private lateinit var noOpOverScroller: NoOpOverScroller
+ @Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
+
+ private lateinit var controller: ShadeTransitionController
+
+ private val configurationController = FakeConfigurationController()
+ private val panelExpansionStateManager = PanelExpansionStateManager()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ controller =
+ ShadeTransitionController(
+ configurationController,
+ panelExpansionStateManager,
+ context,
+ splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
+ noOpOverScroller)
+
+ // Resetting as they are notified upon initialization.
+ reset(noOpOverScroller, splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_forwardsToSplitShadeOverScroller() {
+ initLateProperties()
+ enableSplitShade()
+
+ startPanelExpansion()
+
+ verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(noOpOverScroller)
+ }
+
+ @Test
+ fun onPanelStateChanged_inSplitShade_propertiesNotInitialized_forwardsToNoOpOverScroller() {
+ enableSplitShade()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() {
+ initLateProperties()
+ disableSplitShade()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ private fun initLateProperties() {
+ controller.qs = qs
+ controller.notificationStackScrollLayoutController = nsslController
+ controller.notificationPanelViewController = npvc
+ }
+
+ private fun disableSplitShade() {
+ setSplitShadeEnabled(false)
+ }
+
+ private fun enableSplitShade() {
+ setSplitShadeEnabled(true)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+
+ private fun startPanelExpansion() {
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f,
+ expanded = true,
+ tracking = true,
+ dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
+ }
+
+ companion object {
+ private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
new file mode 100644
index 000000000000..219737d1dfb4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
@@ -0,0 +1,107 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import org.mockito.Mockito.`when` as whenever
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.STATE_CLOSED
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPEN
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class SplitShadeOverScrollerTest : SysuiTestCase() {
+
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var scrimController: ScrimController
+ @Mock private lateinit var qs: QS
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+
+ private val configurationController = FakeConfigurationController()
+ private lateinit var overScroller: SplitShadeOverScroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(nsslController.height).thenReturn(1000)
+ overScroller =
+ SplitShadeOverScroller(
+ configurationController, dumpManager, context, scrimController, qs, nsslController)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelOpening_overScrolls_basedOnHeightAndMaxAmount() {
+ val maxOverScrollAmount = 50
+ val dragDownAmount = 100f
+ overrideResource(R.dimen.shade_max_over_scroll_amount, maxOverScrollAmount)
+ configurationController.notifyConfigurationChanged()
+
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(dragDownAmount)
+
+ val expectedOverScrollAmount =
+ (dragDownAmount / nsslController.height * maxOverScrollAmount).toInt()
+ verify(qs).setOverScrollAmount(expectedOverScrollAmount)
+ verify(nsslController).setOverScrollAmount(expectedOverScrollAmount)
+ verify(scrimController).setNotificationsOverScrollAmount(expectedOverScrollAmount)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelClosed_doesNotOverScroll() {
+ overScroller.onPanelStateChanged(STATE_CLOSED)
+ overScroller.onDragDownAmountChanged(100f)
+
+ verifyZeroInteractions(qs, scrimController, nsslController)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelOpen_doesNotOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPEN)
+ overScroller.onDragDownAmountChanged(100f)
+
+ verifyZeroInteractions(qs, scrimController, nsslController)
+ }
+
+ @Test
+ fun onPanelStateChanged_opening_thenOpen_releasesOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(100f)
+
+ overScroller.onPanelStateChanged(STATE_OPEN)
+ overScroller.finishAnimations()
+
+ verify(qs, atLeastOnce()).setOverScrollAmount(0)
+ verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
+ verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
+ }
+
+ @Test
+ fun onPanelStateChanged_opening_thenClosed_releasesOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(100f)
+
+ overScroller.onPanelStateChanged(STATE_CLOSED)
+ overScroller.finishAnimations()
+
+ verify(qs, atLeastOnce()).setOverScrollAmount(0)
+ verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
+ verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
+ }
+}