diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index 8ad200951eb47..75d95e6d85e30 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -122,6 +122,11 @@ public interface QS extends FragmentBase { */ default void setScrollListener(ScrollListener scrollListener) {} + /** + * Sets the amount of vertical over scroll that should be performed on QS. + */ + default void setOverScrollAmount(int overScrollAmount) {} + /** * Callback for when QSPanel container is scrolled */ diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 9f14d2f236b5c..76780ffd2f80e 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -38,6 +38,8 @@ 42dp + 32dp + 200dp diff --git a/packages/SystemUI/res/values-sw600dp-land/integers.xml b/packages/SystemUI/res/values-sw600dp-land/integers.xml new file mode 100644 index 0000000000000..919d6055667bc --- /dev/null +++ b/packages/SystemUI/res/values-sw600dp-land/integers.xml @@ -0,0 +1,20 @@ + + + + 500 + \ No newline at end of file diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml index cff660d26cb1e..0512d3c2c3a48 100644 --- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -26,4 +26,6 @@ 56dp 251dp + + 42dp diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a309cc4ff1f5b..f39b5efdca24e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1174,9 +1174,8 @@ the shade --> 120dp - - 24dp + + 24dp 32dp diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml index f0f7a196d6940..3164ed1e67513 100644 --- a/packages/SystemUI/res/values/integers.xml +++ b/packages/SystemUI/res/values/integers.xml @@ -25,4 +25,7 @@ See com.android.systemui.volume.VolumeDialogImpl. Value 21 corresponds to RIGHT|CENTER_VERTICAL. --> 21 + + + 0 \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 4e631046063bf..f3d71d1d76298 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -141,6 +141,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca */ private float mFullShadeProgress; + private boolean mOverScrolling; + @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, QSTileHost qsTileHost, @@ -497,6 +499,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca isTransitioningToFullShade ? progress : mSquishinessFraction); } + @Override + public void setOverScrollAmount(int overScrollAmount) { + mOverScrolling = overScrollAmount != 0; + getView().setTranslationY(overScrollAmount); + } + @Override public int getHeightDiff() { return mQSPanelScrollView.getBottom() - mHeader.getBottom() @@ -515,7 +523,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); boolean onKeyguard = isKeyguardState(); boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard; - if (!mHeaderAnimating && !headerWillBeAnimating()) { + if (!mHeaderAnimating && !headerWillBeAnimating() && !mOverScrolling) { getView().setTranslationY( onKeyguardAndExpanded ? translationScaleY * mHeader.getHeight() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt new file mode 100644 index 0000000000000..de37a38e969e2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt @@ -0,0 +1,8 @@ +package com.android.systemui.statusbar + +/** Represents an over scroller for the transition to full shade on lock screen. */ +interface LockScreenShadeOverScroller { + + /** The amount in pixels that the user has dragged to expand the shade. */ + var expansionDragDownAmount: Float +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 310eb4f246525..8f78feb979b89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -64,6 +64,8 @@ class LockscreenShadeTransitionController @Inject constructor( private val scrimController: ScrimController, private val depthController: NotificationShadeDepthController, private val context: Context, + private val splitShadeOverScrollerFactory: SplitShadeLockScreenOverScroller.Factory, + private val singleShadeOverScrollerFactory: SingleShadeLockScreenOverScroller.Factory, wakefulnessLifecycle: WakefulnessLifecycle, configurationController: ConfigurationController, falsingManager: FalsingManager, @@ -194,6 +196,27 @@ class LockscreenShadeTransitionController @Inject constructor( */ val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context) + private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy { + splitShadeOverScrollerFactory.create(qS, nsslController) + } + + private val phoneShadeOverScroller: SingleShadeLockScreenOverScroller by lazy { + singleShadeOverScrollerFactory.create(nsslController) + } + + /** + * [LockScreenShadeOverScroller] property that delegates to either + * [SingleShadeLockScreenOverScroller] or [SplitShadeLockScreenOverScroller]. + * + * There are currently two different implementations, as the over scroll behavior is different + * on single shade and split shade. + * + * On single shade, only notifications are over scrolled, whereas on split shade, everything is + * over scrolled. + */ + private val shadeOverScroller: LockScreenShadeOverScroller + get() = if (useSplitShade) splitShadeOverScroller else phoneShadeOverScroller + init { updateResources() configurationController.addCallback(object : ConfigurationController.ConfigurationListener { @@ -410,7 +433,7 @@ class LockscreenShadeTransitionController @Inject constructor( if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) { val notificationShelfProgress = MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance) - nsslController.setTransitionToFullShadeAmount(field, notificationShelfProgress) + nsslController.setTransitionToFullShadeAmount(notificationShelfProgress) qSDragProgress = MathUtils.saturate(dragDownAmount / qsTransitionDistance) qS.setTransitionToFullShadeAmount(field, qSDragProgress) @@ -422,6 +445,7 @@ class LockscreenShadeTransitionController @Inject constructor( transitionToShadeAmountScrim(field) transitionToShadeAmountCommon(field) transitionToShadeAmountKeyguard(field) + shadeOverScroller.expansionDragDownAmount = dragDownAmount } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt new file mode 100644 index 0000000000000..575f354c66201 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt @@ -0,0 +1,74 @@ +package com.android.systemui.statusbar + +import android.content.Context +import android.content.res.Configuration +import android.util.MathUtils +import com.android.systemui.R +import com.android.systemui.animation.Interpolators +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.policy.ConfigurationController +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class SingleShadeLockScreenOverScroller +@AssistedInject +constructor( + configurationController: ConfigurationController, + private val context: Context, + private val statusBarStateController: SysuiStatusBarStateController, + @Assisted private val nsslController: NotificationStackScrollLayoutController +) : LockScreenShadeOverScroller { + + private var maxOverScrollAmount = 0 + private var totalDistanceForFullShadeTransition = 0 + + init { + updateResources() + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + updateResources() + } + }) + } + + private fun updateResources() { + val resources = context.resources + totalDistanceForFullShadeTransition = + resources.getDimensionPixelSize(R.dimen.lockscreen_shade_qs_transition_distance) + maxOverScrollAmount = + resources.getDimensionPixelSize(R.dimen.lockscreen_shade_max_over_scroll_amount) + } + + override var expansionDragDownAmount: Float = 0f + set(value) { + if (value == field) { + return + } + field = value + overScroll() + } + + private fun overScroll() { + var extraTopInset = 0.0f + if (statusBarStateController.state == StatusBarState.KEYGUARD) { + val viewHeight = nsslController.height + val overallProgress = MathUtils.saturate(expansionDragDownAmount / viewHeight) + val transitionProgress = + Interpolators.getOvershootInterpolation( + overallProgress, + 0.6f, + totalDistanceForFullShadeTransition.toFloat() / viewHeight.toFloat()) + extraTopInset = transitionProgress * maxOverScrollAmount + } + nsslController.setOverScrollAmount(extraTopInset.toInt()) + } + + @AssistedFactory + fun interface Factory { + fun create( + nsslController: NotificationStackScrollLayoutController + ): SingleShadeLockScreenOverScroller + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt new file mode 100644 index 0000000000000..96ce6b45dc71d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt @@ -0,0 +1,129 @@ +package com.android.systemui.statusbar + +import android.animation.Animator +import android.animation.ValueAnimator +import android.content.Context +import android.content.res.Configuration +import android.util.MathUtils +import android.view.animation.PathInterpolator +import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.R +import com.android.systemui.animation.Interpolators +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.policy.ConfigurationController +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class SplitShadeLockScreenOverScroller +@AssistedInject +constructor( + configurationController: ConfigurationController, + private val context: Context, + private val scrimController: ScrimController, + private val statusBarStateController: SysuiStatusBarStateController, + @Assisted private val qS: QS, + @Assisted private val nsslController: NotificationStackScrollLayoutController +) : LockScreenShadeOverScroller { + + private var releaseOverScrollAnimator: Animator? = null + private var transitionToFullShadeDistance = 0 + private var releaseOverScrollDuration = 0L + private var maxOverScrollAmount = 0 + private var previousOverscrollAmount = 0 + + init { + updateResources() + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + updateResources() + } + }) + } + + private fun updateResources() { + val resources = context.resources + transitionToFullShadeDistance = + resources.getDimensionPixelSize(R.dimen.lockscreen_shade_full_transition_distance) + maxOverScrollAmount = + resources.getDimensionPixelSize(R.dimen.lockscreen_shade_max_over_scroll_amount) + releaseOverScrollDuration = + resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong() + } + + override var expansionDragDownAmount: Float = 0f + set(dragDownAmount) { + if (field == dragDownAmount) { + return + } + field = dragDownAmount + if (shouldOverscroll()) { + overScroll(dragDownAmount) + } else if (shouldReleaseOverscroll()) { + releaseOverScroll() + } + } + + private fun shouldOverscroll() = statusBarStateController.state == StatusBarState.KEYGUARD + + private fun shouldReleaseOverscroll() = !shouldOverscroll() && previousOverscrollAmount != 0 + + private fun overScroll(dragDownAmount: Float) { + val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount) + applyOverscroll(overscrollAmount) + previousOverscrollAmount = overscrollAmount + } + + private fun applyOverscroll(overscrollAmount: Int) { + qS.setOverScrollAmount(overscrollAmount) + scrimController.setNotificationsOverScrollAmount(overscrollAmount) + nsslController.setOverScrollAmount(overscrollAmount) + } + + private fun calculateOverscrollAmount(dragDownAmount: Float): Int { + val fullHeight: Int = nsslController.height + val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight) + val overshootStart: Float = transitionToFullShadeDistance / fullHeight.toFloat() + val overShootTransitionProgress: Float = + Interpolators.getOvershootInterpolation( + fullHeightProgress, OVER_SHOOT_AMOUNT, overshootStart) + return (overShootTransitionProgress * maxOverScrollAmount).toInt() + } + + 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 = RELEASE_OVER_SCROLL_INTERPOLATOR + animator.duration = releaseOverScrollDuration + animator.start() + releaseOverScrollAnimator = animator + previousOverscrollAmount = 0 + } + + @VisibleForTesting + internal fun finishAnimations() { + releaseOverScrollAnimator?.end() + releaseOverScrollAnimator = null + } + + @AssistedFactory + fun interface Factory { + fun create( + qS: QS, + nsslController: NotificationStackScrollLayoutController + ): SplitShadeLockScreenOverScroller + } + + companion object { + private const val OVER_SHOOT_AMOUNT = 0.6f + private val RELEASE_OVER_SCROLL_INTERPOLATOR = PathInterpolator(0.17f, 0f, 0f, 1f) + } +} 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 d98f8a77cc8d8..5bc50ae11fb74 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 @@ -41,7 +41,6 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; -import android.util.MathUtils; import android.util.Pair; import android.view.Display; import android.view.LayoutInflater; @@ -65,7 +64,6 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -205,18 +203,6 @@ public class NotificationStackScrollLayoutController { private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; - /** - * The total distance in pixels that the full shade transition takes to transition entirely to - * the full shade. - */ - private int mTotalDistanceForFullShadeTransition; - - /** - * The amount of movement the notifications do when transitioning to the full shade before - * reaching the overstrech - */ - private int mNotificationDragDownMovement; - @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -304,10 +290,6 @@ public class NotificationStackScrollLayoutController { private NotifStats mNotifStats = NotifStats.getEmpty(); private void updateResources() { - mNotificationDragDownMovement = mResources.getDimensionPixelSize( - R.dimen.lockscreen_shade_notification_movement); - mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( - R.dimen.lockscreen_shade_qs_transition_distance); mNotificationStackSizeCalculator.updateResources(); } @@ -1537,8 +1519,6 @@ public class NotificationStackScrollLayoutController { } /** - * @param amount The amount of pixels we have currently dragged down - * for the lockscreen to shade transition. 0f for all other states. * @param fraction The fraction of lockscreen to shade transition. * 0f for all other states. * @@ -1546,18 +1526,15 @@ public class NotificationStackScrollLayoutController { * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain * until the next lockscreen-to-shade transition. */ - public void setTransitionToFullShadeAmount(float amount, float fraction) { + public void setTransitionToFullShadeAmount(float fraction) { mView.setFractionToShade(fraction); + } - float extraTopInset = 0.0f; - if (mStatusBarStateController.getState() == KEYGUARD) { - float overallProgress = MathUtils.saturate(amount / mView.getHeight()); - float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress, - 0.6f, - (float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight()); - extraTopInset = transitionProgress * mNotificationDragDownMovement; - } - mView.setExtraTopInsetForFullShadeTransition(extraTopInset); + /** + * Sets the amount of vertical over scroll that should be performed on NSSL. + */ + public void setOverScrollAmount(int overScrollAmount) { + mView.setExtraTopInsetForFullShadeTransition(overScrollAmount); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 7a69dad92bfb9..7e22510c99afe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -644,6 +644,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } + /** + * Sets the amount of vertical over scroll that should be performed on the notifications scrim. + */ + public void setNotificationsOverScrollAmount(int overScrollAmount) { + mNotificationsScrim.setTranslationY(overScrollAmount); + } + /** * Current state of the QuickSettings when pulling it from the top. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 1d2a0ca3777a1..52c8a559b7b35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -44,6 +44,7 @@ import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions import org.mockito.junit.MockitoJUnit private fun anyObject(): T { @@ -75,6 +76,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var expandHelperCallback: ExpandHelper.Callback @Mock lateinit var mCentralSurfaces: CentralSurfaces @Mock lateinit var qS: QS + @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller + @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller @JvmField @Rule val mockito = MockitoJUnit.rule() private val configurationController = FakeConfigurationController() @@ -104,7 +107,9 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { context = context, configurationController = configurationController, falsingManager = falsingManager, - dumpManager = dumpManager + dumpManager = dumpManager, + splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller }, + singleShadeOverScrollerFactory = { singleShadeOverScroller } ) whenever(nsslController.view).thenReturn(stackscroller) whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback) @@ -229,7 +234,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { fun testDragDownAmountDoesntCallOutInLockedDownShade() { whenever(nsslController.isInLockedDownShade).thenReturn(true) transitionController.dragDownAmount = 10f - verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) + verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat()) verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat()) verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(), @@ -240,7 +245,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Test fun testDragDownAmountCallsOut() { transitionController.dragDownAmount = 10f - verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) + verify(nsslController).setTransitionToFullShadeAmount(anyFloat()) verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat()) verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(), @@ -388,6 +393,26 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f) } + @Test + fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() { + disableSplitShade() + + transitionController.dragDownAmount = 10f + + verify(singleShadeOverScroller).expansionDragDownAmount = 10f + verifyZeroInteractions(splitShadeOverScroller) + } + + @Test + fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() { + enableSplitShade() + + transitionController.dragDownAmount = 10f + + verify(splitShadeOverScroller).expansionDragDownAmount = 10f + verifyZeroInteractions(singleShadeOverScroller) + } + private fun enableSplitShade() { setSplitShadeEnabled(true) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt new file mode 100644 index 0000000000000..2606be5fabad2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt @@ -0,0 +1,56 @@ +package com.android.systemui.statusbar + +import org.mockito.Mockito.`when` as whenever +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +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.intThat +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class SingleShadeLockScreenOverScrollerTest : SysuiTestCase() { + + @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock private lateinit var nsslController: NotificationStackScrollLayoutController + + private lateinit var overScroller: SingleShadeLockScreenOverScroller + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(nsslController.height).thenReturn(1800) + overScroller = + SingleShadeLockScreenOverScroller( + FakeConfigurationController(), + context, + statusBarStateController, + nsslController + ) + } + + @Test + fun setDragDownAmount_onKeyguard_overScrolls() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + overScroller.expansionDragDownAmount = 10f + + verify(nsslController).setOverScrollAmount(intThat { it > 0 }) + } + + @Test + fun setDragDownAmount_notOnKeyguard_doesNotOverScroll() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + + overScroller.expansionDragDownAmount = 10f + + verify(nsslController).setOverScrollAmount(0) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt new file mode 100644 index 0000000000000..9d5099cfafd0e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt @@ -0,0 +1,124 @@ +package com.android.systemui.statusbar + +import org.mockito.Mockito.`when` as whenever +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +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.ScrimController +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.atLeast +import org.mockito.Mockito.intThat +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@TestableLooper.RunWithLooper +class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() { + + private val configurationController = FakeConfigurationController() + + @Mock private lateinit var scrimController: ScrimController + @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock private lateinit var qS: QS + @Mock private lateinit var nsslController: NotificationStackScrollLayoutController + + private lateinit var overScroller: SplitShadeLockScreenOverScroller + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(nsslController.height).thenReturn(1800) + + overScroller = + SplitShadeLockScreenOverScroller( + configurationController, + context, + scrimController, + statusBarStateController, + qS, + nsslController) + } + + @Test + fun setDragDownAmount_onKeyguard_appliesOverScroll() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + setDragAmount(1000f) + + verifyOverScrollPerformed() + } + + @Test + fun setDragDownAmount_notOnKeyguard_doesNotApplyOverScroll() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + + setDragAmount(1000f) + + verifyZeroInteractions(qS) + verifyZeroInteractions(scrimController) + verifyZeroInteractions(nsslController) + } + + @Test + fun setDragAmount_onKeyguard_thenNotOnKeyguard_resetsOverScrollToZero() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + setDragAmount(1000f) + verifyOverScrollPerformed() + reset(qS, scrimController, nsslController) + + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + setDragAmount(999f) + verifyOverScrollResetToZero() + } + + @Test + fun setDragAmount_onKeyguard_thenNotOnKeyguard_multipleTimes_resetsOverScrollToZeroOnlyOnce() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + setDragAmount(1000f) + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + setDragAmount(999f) + reset(qS, scrimController, nsslController) + + setDragAmount(998f) + setDragAmount(997f) + setDragAmount(996f) + verifyNoMoreOverScrollChanges() + } + + private fun verifyOverScrollPerformed() { + verify(qS).setOverScrollAmount(intThat { it > 0 }) + verify(scrimController).setNotificationsOverScrollAmount(intThat { it > 0 }) + verify(nsslController).setOverScrollAmount(intThat { it > 0 }) + } + + private fun verifyOverScrollResetToZero() { + // Might be more than once as the animator might have multiple values close to zero that + // round down to zero. + verify(qS, atLeast(1)).setOverScrollAmount(0) + verify(scrimController, atLeast(1)).setNotificationsOverScrollAmount(0) + verify(nsslController, atLeast(1)).setOverScrollAmount(0) + } + + private fun verifyNoMoreOverScrollChanges() { + verifyNoMoreInteractions(qS) + verifyNoMoreInteractions(scrimController) + verifyNoMoreInteractions(nsslController) + } + + private fun setDragAmount(dragDownAmount: Float) { + overScroller.expansionDragDownAmount = dragDownAmount + overScroller.finishAnimations() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 92c0e1a1ba672..134ad4b9d0fbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1296,6 +1296,33 @@ public class ScrimControllerTest extends SysuiTestCase { assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(notifProgress); } + @Test + public void setNotificationsOverScrollAmount_setsTranslationYOnNotificationsScrim() { + int overScrollAmount = 10; + + mScrimController.setNotificationsOverScrollAmount(overScrollAmount); + + assertThat(mNotificationsScrim.getTranslationY()).isEqualTo(overScrollAmount); + } + + @Test + public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnBehindScrim() { + int overScrollAmount = 10; + + mScrimController.setNotificationsOverScrollAmount(overScrollAmount); + + assertThat(mScrimBehind.getTranslationY()).isEqualTo(0); + } + + @Test + public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnFrontScrim() { + int overScrollAmount = 10; + + mScrimController.setNotificationsOverScrollAmount(overScrollAmount); + + assertThat(mScrimInFront.getTranslationY()).isEqualTo(0); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately();