diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 91c6a9c892e70..28868e10dfd1f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -449,6 +449,8 @@ public final class NotificationPanelViewController extends PanelViewController { private boolean mQsTouchAboveFalsingThreshold; private int mQsFalsingThreshold; + /** Indicates drag starting height when swiping down or up on heads-up notifications */ + private int mHeadsUpStartHeight; private HeadsUpTouchHelper mHeadsUpTouchHelper; private boolean mListenForHeadsUp; private int mNavigationBarBottomHeight; @@ -651,6 +653,8 @@ public final class NotificationPanelViewController extends PanelViewController { /** The drag distance required to fully expand the split shade. */ private int mSplitShadeFullTransitionDistance; + /** The drag distance required to fully transition scrims. */ + private int mSplitShadeScrimTransitionDistance; private final NotificationListContainer mNotificationListContainer; private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; @@ -1064,6 +1068,8 @@ public final class NotificationPanelViewController extends PanelViewController { mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( R.dimen.notification_side_paddings); mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); + mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize( + R.dimen.split_shade_scrim_transition_distance); } private void updateViewControllers(KeyguardStatusView keyguardStatusView, @@ -1354,7 +1360,7 @@ public final class NotificationPanelViewController extends PanelViewController { mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mQsSizeChangeAnimator.addUpdateListener(animation -> { requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); mQs.setHeightOverride(height); }); @@ -2134,7 +2140,7 @@ public final class NotificationPanelViewController extends PanelViewController { mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); setQsExpandImmediate(true); setShowShelfOnly(true); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); // Normally, we start listening when the panel is expanded, but here we need to start // earlier so the state is already up to date when dragging down. @@ -2346,7 +2352,7 @@ public final class NotificationPanelViewController extends PanelViewController { // Reset scroll position and apply that position to the expanded height. float height = mQsExpansionHeight; setQsExpansion(height); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); // When expanding QS, let's authenticate the user if possible, @@ -2362,7 +2368,7 @@ public final class NotificationPanelViewController extends PanelViewController { if (changed) { mQsExpanded = expanded; updateQsState(); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); mFalsingCollector.setQsExpanded(expanded); mCentralSurfaces.setQsExpanded(expanded); mNotificationsQSContainerController.setQsExpanded(expanded); @@ -3086,16 +3092,7 @@ public final class NotificationPanelViewController extends PanelViewController { int maxHeight; if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted || mPulsing || mSplitShadeEnabled) { - if (mSplitShadeEnabled && mBarState == SHADE) { - // Max panel height is used to calculate the fraction of the shade expansion. - // Traditionally the value is based on the number of notifications. - // On split-shade, we want the required distance to be a specific and constant - // value, to make sure the expansion motion has the expected speed. - // We also only want this on non-lockscreen for now. - maxHeight = mSplitShadeFullTransitionDistance; - } else { - maxHeight = calculatePanelHeightQsExpanded(); - } + maxHeight = calculatePanelHeightQsExpanded(); } else { maxHeight = calculatePanelHeightShade(); } @@ -3454,6 +3451,31 @@ public final class NotificationPanelViewController extends PanelViewController { || !isTracking()); } + @Override + public int getMaxPanelTransitionDistance() { + // Traditionally the value is based on the number of notifications. On split-shade, we want + // the required distance to be a specific and constant value, to make sure the expansion + // motion has the expected speed. We also only want this on non-lockscreen for now. + if (mSplitShadeEnabled && mBarState == SHADE) { + boolean transitionFromHeadsUp = + mHeadsUpManager.isTrackingHeadsUp() || mExpandingFromHeadsUp; + // heads-up starting height is too close to mSplitShadeFullTransitionDistance and + // when dragging HUN transition is already 90% complete. It makes shade become + // immediately visible when starting to drag. We want to set distance so that + // nothing is immediately visible when dragging (important for HUN swipe up motion) - + // 0.4 expansion fraction is a good starting point. + if (transitionFromHeadsUp) { + double maxDistance = Math.max(mSplitShadeFullTransitionDistance, + mHeadsUpStartHeight * 2.5); + return (int) Math.min(getMaxPanelHeight(), maxDistance); + } else { + return mSplitShadeFullTransitionDistance; + } + } else { + return getMaxPanelHeight(); + } + } + @Override protected boolean isTrackingBlocked() { return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; @@ -3653,11 +3675,36 @@ public final class NotificationPanelViewController extends PanelViewController { return mBarState == KEYGUARD; } + /** + * Called when heads-up notification is being dragged up or down to indicate what's the starting + * height for shade motion + */ + public void setHeadsUpDraggingStartingHeight(int startHeight) { + mHeadsUpStartHeight = startHeight; + float scrimMinFraction; + if (mSplitShadeEnabled) { + boolean highHun = mHeadsUpStartHeight * 2.5 > mSplitShadeScrimTransitionDistance; + // if HUN height is higher than 40% of predefined transition distance, it means HUN + // is too high for regular transition. In that case we need to calculate transition + // distance - here we take scrim transition distance as equal to shade transition + // distance. It doesn't result in perfect motion - usually scrim transition distance + // should be longer - but it's good enough for HUN case. + float transitionDistance = + highHun ? getMaxPanelTransitionDistance() : mSplitShadeFullTransitionDistance; + scrimMinFraction = mHeadsUpStartHeight / transitionDistance; + } else { + int transitionDistance = getMaxPanelHeight(); + scrimMinFraction = transitionDistance > 0f + ? (float) mHeadsUpStartHeight / transitionDistance : 0f; + } + setPanelScrimMinFraction(scrimMinFraction); + } + /** * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain * cases, such as if there's a heads-up notification. */ - public void setPanelScrimMinFraction(float minFraction) { + private void setPanelScrimMinFraction(float minFraction) { mMinFraction = minFraction; mDepthController.setPanelPullDownMinFraction(mMinFraction); mScrimController.setPanelScrimMinFraction(mMinFraction); @@ -4391,7 +4438,7 @@ public final class NotificationPanelViewController extends PanelViewController { if (mKeyguardShowing) { updateMaxDisplayedNotifications(true); } - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); } @Override @@ -4524,7 +4571,7 @@ public final class NotificationPanelViewController extends PanelViewController { if (mQsExpanded && mQsFullyExpanded) { mQsExpansionHeight = mQsMaxExpansionHeight; requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); } if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); @@ -4794,7 +4841,7 @@ public final class NotificationPanelViewController extends PanelViewController { if (mQsExpanded && mQsFullyExpanded) { mQsExpansionHeight = mQsMaxExpansionHeight; requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); // Size has changed, start an animation. if (mQsMaxExpansionHeight != oldMaxHeight) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java index a007693749385..c3f1e571ab872 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java @@ -248,7 +248,7 @@ public abstract class PanelViewController { keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardFadingAwayChanged() { - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); } }); mAmbientState = ambientState; @@ -730,7 +730,7 @@ public abstract class PanelViewController { setExpandedHeightInternal(height); } - protected void requestPanelHeightUpdate() { + void updateExpandedHeightToMaxHeight() { float currentMaxPanelHeight = getMaxPanelHeight(); if (isFullyCollapsed()) { @@ -753,6 +753,13 @@ public abstract class PanelViewController { setExpandedHeight(currentMaxPanelHeight); } + /** + * Returns drag down distance after which panel should be fully expanded. Usually it's the + * same as max panel height but for large screen devices (especially split shade) we might + * want to return different value to shorten drag distance + */ + public abstract int getMaxPanelTransitionDistance(); + public void setExpandedHeightInternal(float h) { if (isNaN(h)) { Log.wtf(TAG, "ExpandedHeight set to NaN"); @@ -763,18 +770,15 @@ public abstract class PanelViewController { () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL)); mExpandLatencyTracking = false; } - float maxPanelHeight = getMaxPanelHeight(); + float maxPanelHeight = getMaxPanelTransitionDistance(); if (mHeightAnimator == null) { // Split shade has its own overscroll logic if (mTracking && !mInSplitShade) { float overExpansionPixels = Math.max(0, h - maxPanelHeight); setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */); } - mExpandedHeight = Math.min(h, maxPanelHeight); - } else { - mExpandedHeight = h; } - + mExpandedHeight = Math.min(h, maxPanelHeight); // If we are closing the panel and we are almost there due to a slow decelerating // interpolator, abort the animation. if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) { @@ -832,7 +836,7 @@ public abstract class PanelViewController { protected abstract int getMaxPanelHeight(); public void setExpandedFraction(float frac) { - setExpandedHeight(getMaxPanelHeight() * frac); + setExpandedHeight(getMaxPanelTransitionDistance() * frac); } public float getExpandedHeight() { @@ -1029,7 +1033,7 @@ public abstract class PanelViewController { mHeightAnimator = animator; if (animator == null && mPanelUpdateWhenAnimatorEnds) { mPanelUpdateWhenAnimatorEnds = false; - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); } } @@ -1421,7 +1425,7 @@ public abstract class PanelViewController { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - requestPanelHeightUpdate(); + updateExpandedHeightToMaxHeight(); mHasLayoutedSinceDown = true; if (mUpdateFlingOnLayout) { abortAnimations(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt index afd57daca10bf..618c8924cd211 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt @@ -14,6 +14,7 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent import com.android.systemui.statusbar.phone.panelstate.PanelState import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.LargeScreenUtils import java.io.PrintWriter import javax.inject.Inject @@ -28,6 +29,7 @@ constructor( private val scrimController: ScrimController, @Main private val resources: Resources, private val statusBarStateController: SysuiStatusBarStateController, + private val headsUpManager: HeadsUpManager ) { private var inSplitShade = false @@ -84,7 +86,11 @@ constructor( } private fun canUseCustomFraction(panelState: Int?) = - inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING + inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING && + // in case of HUN we can't always use predefined distances to manage scrim + // transition because dragDownPxAmount can start from value bigger than + // splitShadeScrimTransitionDistance + !headsUpManager.isTrackingHeadsUp private fun isScreenUnlocked() = statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 6bfb0dad28f79..90d0b697337a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -115,9 +115,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mInitialTouchY = y; int startHeight = (int) (mPickedChild.getActualHeight() + mPickedChild.getTranslationY()); - float maxPanelHeight = mPanel.getMaxPanelHeight(); - mPanel.setPanelScrimMinFraction(maxPanelHeight > 0f - ? (float) startHeight / maxPanelHeight : 0f); + mPanel.setHeadsUpDraggingStartingHeight(startHeight); mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight); // This call needs to be after the expansion start otherwise we will get a // flicker of one frame as it's not expanded yet. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt index a6160aaf7756c..6b7c42e3884a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt @@ -114,8 +114,8 @@ class PanelExpansionStateManager @Inject constructor() { "end state=${state.panelStateToString()} " + "f=$fraction " + "expanded=$expanded " + - "tracking=$tracking" + - "drawDownPxAmount=$dragDownPxAmount " + + "tracking=$tracking " + + "dragDownPxAmount=$dragDownPxAmount " + "${if (fullyOpened) " fullyOpened" else ""} " + if (fullyClosed) " fullyClosed" else "" ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index f2670131081ff..eb6a6b4586a38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -184,6 +184,7 @@ import java.util.Optional; @TestableLooper.RunWithLooper public class NotificationPanelViewControllerTest extends SysuiTestCase { + private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400; private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; @Mock @@ -413,6 +414,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)) .thenReturn(10); + when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) + .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); when(mView.getContext()).thenReturn(getContext()); when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); @@ -751,8 +754,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testSetPanelScrimMinFraction() { - mNotificationPanelViewController.setPanelScrimMinFraction(0.5f); + public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() { + mNotificationPanelViewController.setHeadsUpDraggingStartingHeight( + mNotificationPanelViewController.getMaxPanelHeight() / 2); verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f)); } @@ -1448,40 +1452,47 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void getMaxPanelHeight_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() { - int splitShadeFullTransitionDistance = 123456; + public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() { enableSplitShade(true); - setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance); mNotificationPanelViewController.expandWithQs(); - int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight(); + int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); - assertThat(maxPanelHeight).isEqualTo(splitShadeFullTransitionDistance); + assertThat(maxDistance).isEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); } @Test - public void getMaxPanelHeight_expandingSplitShade_keyguard_returnsNonSplitShadeValue() { + public void getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue() { + enableSplitShade(true); + mNotificationPanelViewController.expandWithQs(); + when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true); + mNotificationPanelViewController.setHeadsUpDraggingStartingHeight( + SPLIT_SHADE_FULL_TRANSITION_DISTANCE); + + int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); + + assertThat(maxDistance).isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); + } + + @Test + public void getMaxPanelTransitionDistance_expandingSplitShade_keyguard_returnsNonSplitShadeValue() { mStatusBarStateController.setState(KEYGUARD); - int splitShadeFullTransitionDistance = 123456; enableSplitShade(true); - setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance); mNotificationPanelViewController.expandWithQs(); - int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight(); + int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); - assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance); + assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); } @Test - public void getMaxPanelHeight_expanding_notSplitShade_returnsNonSplitShadeValue() { - int splitShadeFullTransitionDistance = 123456; + public void getMaxPanelTransitionDistance_expanding_notSplitShade_returnsNonSplitShadeValue() { enableSplitShade(false); - setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance); mNotificationPanelViewController.expandWithQs(); - int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight(); + int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); - assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance); + assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); } private static MotionEvent createMotionEvent(int x, int y, int action) { @@ -1507,12 +1518,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } } - private void givenViewDetached() { - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewDetachedFromWindow(mView); - } - } - private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); @@ -1544,12 +1549,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mTouchHandler.onTouch(mView, ev); } - private void setSplitShadeFullTransitionDistance(int splitShadeFullTransitionDistance) { - when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) - .thenReturn(splitShadeFullTransitionDistance); - mNotificationPanelViewController.updateResources(); - } - private void setDozing(boolean dozing, boolean dozingAlwaysOn) { when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn); mNotificationPanelViewController.setDozing( diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt index baaa447d53a37..6be76a6ac9690 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt @@ -13,6 +13,7 @@ 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 com.android.systemui.statusbar.policy.HeadsUpManager import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -28,6 +29,7 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { @Mock private lateinit var scrimController: ScrimController @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock private lateinit var headsUpManager: HeadsUpManager private val configurationController = FakeConfigurationController() private lateinit var controller: ScrimShadeTransitionController @@ -42,7 +44,8 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { dumpManager, scrimController, context.resources, - statusBarStateController) + statusBarStateController, + headsUpManager) controller.onPanelStateChanged(STATE_OPENING) }