diff options
| author | 2022-04-20 14:09:52 +0200 | |
|---|---|---|
| committer | 2022-04-28 09:07:43 +0000 | |
| commit | 352c4e44dbcf5e7e1ce2a0c547ce6fb35ebba942 (patch) | |
| tree | 349163b6c7dfb82af034661d5996d600712c9afe | |
| parent | 8731db334905c0ec62d26410b9ce288185057c52 (diff) | |
[Motion] Split-shade expansion: implement over scroll effect [v1]
Before, there was code to add over scroll on shade expansion, but only for the notification side of the panel.
This CL adds specific split-shade logic for over scroll, which adds over scroll to the entire panel.
Note that this first version still doesn't take into account fling velocity for the overscroll amount.
Also, this isn't a regression from the current state.
Test: Manually
Test: NotificationStackScrollLayoutTest.java
Test: NotificationPanelViewControllerTest.java
Test: ShadeTransitionControllerTest.kt
Test: SplitShadeOverScrollerTest.kt
Fixes: 228022047
Change-Id: Ic6de84d831a4a6e65139c1cdc04a2612b72dcb49
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) + } +} |