Merge "[Motion] Split-shade expansion: implement over scroll effect [v1]" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
fa9d138be0
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -3625,6 +3630,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
}
|
||||
});
|
||||
mLockscreenShadeTransitionController.setQS(mQs);
|
||||
mShadeTransitionController.setQs(mQs);
|
||||
mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
|
||||
mQs.setScrollListener(mScrollListener);
|
||||
updateQsExpansion();
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -529,7 +532,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
|
||||
mNotificationListContainer,
|
||||
mPanelEventsEmitter,
|
||||
mNotificationStackSizeCalculator,
|
||||
mUnlockedScreenOffAnimationController);
|
||||
mUnlockedScreenOffAnimationController,
|
||||
mShadeTransitionController);
|
||||
mNotificationPanelViewController.initDependencies(
|
||||
mCentralSurfaces,
|
||||
() -> {},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user