diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
index 176c620fa1193..798250de89d0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
@@ -311,6 +311,13 @@ public class FlingAnimationUtils {
return mMinVelocityPxPerSecond;
}
+ /**
+ * @return a velocity considered fast
+ */
+ public float getHighVelocityPxPerSecond() {
+ return mHighVelocityPxPerSecond;
+ }
+
/**
* An interpolator which interpolates two interpolators with an interpolator.
*/
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 457e8e6e9a5fa..659b9fee86564 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -93,6 +93,16 @@ public class Interpolators {
(float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
}
+ /**
+ * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
+ * starts immediately here, instead of first having a section of non-overshooting
+ *
+ * @param progress a progress value going from 0 to 1
+ */
+ public static float getOvershootInterpolation(float progress) {
+ return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
+ }
+
/**
* Interpolate alpha for notifications background scrim during shade expansion.
* @param fraction Shade expansion fraction
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 05f2f6727a73f..ad2a1aceabcc5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -771,6 +771,9 @@
75dp
+
+ 16dp
+
48dp
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a18917789ba19..0c86262d9037f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -83,7 +83,7 @@ public class AmbientState {
private ExpandableNotificationRow mTrackedHeadsUpRow;
private float mAppearFraction;
private boolean mIsShadeOpening;
- private float mSectionPadding;
+ private float mOverExpansion;
/** Distance of top of notifications panel from top of screen. */
private float mStackY = 0;
@@ -182,12 +182,12 @@ public class AmbientState {
return mIsShadeOpening;
}
- void setSectionPadding(float padding) {
- mSectionPadding = padding;
+ void setOverExpansion(float overExpansion) {
+ mOverExpansion = overExpansion;
}
- float getSectionPadding() {
- return mSectionPadding;
+ float getOverExpansion() {
+ return mOverExpansion;
}
private static int getZDistanceBetweenElements(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 64f228f41df9d..9390c81a847bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -119,6 +119,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
@@ -470,6 +471,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
+ private Consumer mScrollListener;
private final ScrollAdapter mScrollAdapter = new ScrollAdapter() {
@Override
public boolean isScrolledToTop() {
@@ -552,8 +554,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- void setSectionPadding(float margin) {
- mAmbientState.setSectionPadding(margin);
+ /**
+ * Set the overexpansion of the panel to be applied to the view.
+ */
+ void setOverExpansion(float margin) {
+ mAmbientState.setOverExpansion(margin);
+ updateStackPosition();
requestChildrenUpdate();
}
@@ -1136,7 +1142,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private void updateStackPosition() {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
- float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition;
+ float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ + mAmbientState.getOverExpansion();
final float fraction = mAmbientState.getExpansionFraction();
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
@@ -1144,7 +1151,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mOnStackYChanged.run();
}
if (mQsExpansionFraction <= 0) {
- final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
final float stackEndHeight = Math.max(0f,
getHeight() - getEmptyBottomMargin() - mTopPadding);
mAmbientState.setStackEndHeight(stackEndHeight);
@@ -1166,7 +1172,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setExpandedHeight(float height) {
final float shadeBottom = getHeight() - getEmptyBottomMargin();
- final float expansionFraction = MathUtils.constrain(height / shadeBottom, 0f, 1f);
+ final float expansionFraction = MathUtils.saturate(height / shadeBottom);
mAmbientState.setExpansionFraction(expansionFraction);
updateStackPosition();
@@ -2395,8 +2401,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
float topOverScroll = getCurrentOverScrollAmount(true);
return mScrolledToTopOnFirstDown
&& !mExpandedInThisMotion
- && topOverScroll > mMinTopOverScrollToEscape
- && initialVelocity > 0;
+ && (initialVelocity > mMinimumVelocity
+ || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0));
}
/**
@@ -4552,6 +4558,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void updateOnScrollChange() {
+ if (mScrollListener != null) {
+ mScrollListener.accept(mOwnScrollY);
+ }
updateForwardAndBackwardScrollability();
requestChildrenUpdate();
}
@@ -5162,6 +5171,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
requestChildrenUpdate();
}
+ /**
+ * Set a listener to when scrolling changes.
+ */
+ public void setOnScrollListener(Consumer listener) {
+ mScrollListener = listener;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
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 4432f5463802e..a6bba93d98162 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
@@ -124,6 +124,7 @@ import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
@@ -302,8 +303,11 @@ public class NotificationStackScrollLayoutController {
}
};
- public void setSectionPadding(float padding) {
- mView.setSectionPadding(padding);
+ /**
+ * Set the overexpansion of the panel to be applied to the view.
+ */
+ public void setOverExpansion(float overExpansion) {
+ mView.setOverExpansion(overExpansion);
}
private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
@@ -944,10 +948,6 @@ public class NotificationStackScrollLayoutController {
mView.setOverScrollAmount(amount, onTop, animate);
}
- public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
- mView.setOverScrolledPixels(numPixels, onTop, animate);
- }
-
public void resetScrollPosition() {
mView.resetScrollPosition();
}
@@ -1054,10 +1054,6 @@ public class NotificationStackScrollLayoutController {
return mView.getCurrentOverScrollAmount(top);
}
- public float getCurrentOverScrolledPixels(boolean top) {
- return mView.getCurrentOverScrolledPixels(top);
- }
-
public float calculateAppearFraction(float height) {
return mView.calculateAppearFraction(height);
}
@@ -1430,6 +1426,13 @@ public class NotificationStackScrollLayoutController {
mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
}
+ /**
+ * Set a listener to when scrolling changes.
+ */
+ public void setOnScrollListener(Consumer listener) {
+ mView.setOnScrollListener(listener);
+ }
+
/**
* Enum for UiEvent logged from this class
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 63c5c80ff3814..a02ebbfa3521e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -403,10 +403,6 @@ public class StackScrollAlgorithm {
}
viewState.yTranslation = algorithmState.mCurrentYPosition;
- if (view instanceof SectionHeaderView) {
- // Add padding before sections for overscroll effect.
- viewState.yTranslation += expansionFraction * ambientState.getSectionPadding();
- }
if (view instanceof FooterView) {
final boolean shadeClosed = !ambientState.isShadeExpanded();
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 dc71c10ed87cf..336cbdc8a63dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -510,7 +510,6 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mShowingKeyguardHeadsUp;
private boolean mAllowExpandForSmallExpansion;
private Runnable mExpandAfterLayoutRunnable;
- private float mSectionPadding;
/**
* The padding between the start of notifications and the qs boundary on the lockscreen.
@@ -589,7 +588,6 @@ public class NotificationPanelViewController extends PanelViewController {
private NotificationShelfController mNotificationShelfController;
private int mScrimCornerRadius;
private int mScreenCornerRadius;
- private int mNotificationScrimPadding;
private boolean mQSAnimatingHiddenFromCollapsed;
private final Executor mUiExecutor;
@@ -824,6 +822,7 @@ public class NotificationPanelViewController extends PanelViewController {
mOnHeightChangedListener);
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
mOnOverscrollTopChangedListener);
+ mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -908,8 +907,6 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimCornerRadius = mResources.getDimensionPixelSize(
R.dimen.notification_scrim_corner_radius);
mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mResources);
- mNotificationScrimPadding = mResources.getDimensionPixelSize(
- R.dimen.notification_side_paddings);
mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
R.dimen.notification_side_paddings);
}
@@ -2190,6 +2187,22 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
+ private void onNotificationScrolled(int newScrollPosition) {
+ // Since this is an overscroller, sometimes the scrollY can be temporarily negative
+ // (when overscrollng on the top and flinging). Let's
+ updateQSExpansionEnabled();
+ }
+
+ @Override
+ public void setIsShadeOpening(boolean opening) {
+ mAmbientState.setIsShadeOpening(opening);
+ updateQSExpansionEnabled();
+ }
+
+ private void updateQSExpansionEnabled() {
+ setQsExpansionEnabled(mAmbientState.getScrollY() <= 0 && !mAmbientState.isShadeOpening());
+ }
+
/**
* Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade
* and QS state.
@@ -2203,7 +2216,6 @@ public class NotificationPanelViewController extends PanelViewController {
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- setQsExpansionEnabled(mAmbientState.getScrollY() == 0 && !mAmbientState.isShadeOpening());
if (!mShouldUseSplitNotificationShade) {
if (mTransitioningToFullShadeProgress > 0.0f) {
@@ -2315,8 +2327,6 @@ public class NotificationPanelViewController extends PanelViewController {
qsBottomY = (int) MathUtils.lerp(
qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
}
- // to account for shade overshooting animation, see setSectionPadding method
- if (mSectionPadding > 0) qsBottomY += mSectionPadding;
return qsBottomY;
}
}
@@ -2632,7 +2642,7 @@ public class NotificationPanelViewController extends PanelViewController {
int min = mStatusBarMinHeight;
if (!(mBarState == KEYGUARD)
&& mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
- int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
+ int minHeight = mQsMinExpansionHeight;
min = Math.max(min, minHeight);
}
int maxHeight;
@@ -2644,8 +2654,8 @@ public class NotificationPanelViewController extends PanelViewController {
}
maxHeight = Math.max(min, maxHeight);
if (maxHeight == 0 || isNaN(maxHeight)) {
- Log.wtf(TAG, "maxPanelHeight is invalid. getOverExpansionAmount(): "
- + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
+ Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
+ + mOverExpansion + ", calculatePanelHeightQsExpanded: "
+ calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
+ calculatePanelHeightShade() + ", mStatusBarMinHeight = "
+ mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
@@ -2807,23 +2817,6 @@ public class NotificationPanelViewController extends PanelViewController {
return alpha;
}
- @Override
- protected float getOverExpansionAmount() {
- float result = mNotificationStackScrollLayoutController
- .getCurrentOverScrollAmount(true /* top */);
- if (isNaN(result)) {
- Log.wtf(TAG, "OverExpansionAmount is NaN!");
- }
-
- return result;
- }
-
- @Override
- protected float getOverExpansionPixels() {
- return mNotificationStackScrollLayoutController
- .getCurrentOverScrolledPixels(true /* top */);
- }
-
/**
* Hides the header when notifications are colliding with it.
*/
@@ -3018,31 +3011,15 @@ public class NotificationPanelViewController extends PanelViewController {
}
@Override
- public void setSectionPadding(float padding) {
- if (padding == mSectionPadding) {
+ public void setOverExpansion(float overExpansion) {
+ if (overExpansion == mOverExpansion) {
return;
}
- mSectionPadding = padding;
- // TODO(b/172289889) update overscroll to spec
- }
-
- @Override
- protected void setOverExpansion(float overExpansion, boolean isPixels) {
- if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
- return;
- }
- if (mBarState != KEYGUARD) {
- mNotificationStackScrollLayoutController.setOnHeightChangedListener(null);
- if (isPixels) {
- mNotificationStackScrollLayoutController.setOverScrolledPixels(
- overExpansion, true /* onTop */, false /* animate */);
- } else {
- mNotificationStackScrollLayoutController.setOverScrollAmount(
- overExpansion, true /* onTop */, false /* animate */);
- }
- mNotificationStackScrollLayoutController
- .setOnHeightChangedListener(mOnHeightChangedListener);
- }
+ super.setOverExpansion(overExpansion);
+ // Translating the quick settings by half the overexpansion to center it in the background
+ // frame
+ mQsFrame.setTranslationY(overExpansion / 2f);
+ mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
}
@Override
@@ -3066,7 +3043,7 @@ public class NotificationPanelViewController extends PanelViewController {
mFalsingCollector.onTrackingStopped();
super.onTrackingStopped(expand);
if (expand) {
- mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */,
+ mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */,
true /* animate */);
}
mNotificationStackScrollLayoutController.onPanelTrackingStopped();
@@ -3111,18 +3088,6 @@ public class NotificationPanelViewController extends PanelViewController {
|| !isTracking());
}
- @Override
- protected boolean fullyExpandedClearAllVisible() {
- return mNotificationStackScrollLayoutController.isFooterViewNotGone()
- && mNotificationStackScrollLayoutController.isScrolledToBottom()
- && !mQsExpandImmediate;
- }
-
- @Override
- protected boolean isClearAllVisible() {
- return mNotificationStackScrollLayoutController.isFooterViewContentVisible();
- }
-
@Override
protected boolean isTrackingBlocked() {
return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
@@ -3973,10 +3938,15 @@ public class NotificationPanelViewController extends PanelViewController {
}
mLastOverscroll = 0f;
mQsExpansionFromOverscroll = false;
+ if (open) {
+ // During overscrolling, qsExpansion doesn't actually change that the qs is
+ // becoming expanded. Any layout could therefore reset the position again. Let's
+ // make sure we can expand
+ setOverScrolling(false);
+ }
setQsExpansion(mQsExpansionHeight);
flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> {
- mStackScrollerOverscrolling = false;
setOverScrolling(false);
updateQsState();
}, false /* isClick */);
@@ -4392,7 +4362,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (mQsMaxExpansionHeight != oldMaxHeight) {
startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
}
- } else if (!mQsExpanded) {
+ } else if (!mQsExpanded && mQsExpansionAnimator == null) {
setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
}
updateExpandedHeight(getExpandedHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 64e2c1c5d268b..f1b6c7c3ad193 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -53,7 +53,7 @@ public abstract class PanelBar extends FrameLayout {
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
if (mPanel != null) {
- mPanel.getAmbientState().setIsShadeOpening(state == STATE_OPENING);
+ mPanel.setIsShadeOpening(state == STATE_OPENING);
}
}
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 798e8953b1704..323a1128d3bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -68,12 +68,14 @@ public abstract class PanelViewController {
public static final String TAG = PanelView.class.getSimpleName();
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
- private static final long SHADE_OPEN_SPRING_BACK_DURATION = 200L;
- private static final float MIN_OVERSCROLL = -50;
- private static final float MAX_OVERSCROLL = 30;
+ private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
+
+ /**
+ * The factor of the usual high velocity that is needed in order to reach the maximum overshoot
+ * when flinging. A low value will make it that most flings will reach the maximum overshoot.
+ */
+ private static final float FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT = 0.5f;
- private float mFlingTarget;
- private float mFlingVelocity;
protected long mDownTime;
protected boolean mTouchSlopExceededBeforeDown;
private float mMinExpandHeight;
@@ -83,6 +85,22 @@ public abstract class PanelViewController {
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
protected ArrayList mExpansionListeners = new ArrayList<>();
+ protected float mOverExpansion;
+
+ /**
+ * The overshoot amount when the panel flings open
+ */
+ private float mPanelFlingOvershootAmount;
+
+ /**
+ * The amount of pixels that we have overexpanded the last time with a gesture
+ */
+ private float mLastGesturedOverExpansion = -1;
+
+ /**
+ * Is the current animator the spring back animation?
+ */
+ private boolean mIsSpringBackAnimation;
private void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -254,6 +272,7 @@ public abstract class PanelViewController {
mTouchSlop = configuration.getScaledTouchSlop();
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);
}
@@ -529,21 +548,38 @@ public abstract class PanelViewController {
protected void flingToHeight(float vel, boolean expand, float target,
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
- if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ if (target == mExpandedHeight && mOverExpansion == 0.0f) {
+ // We're at the target and didn't fling and there's no overshoot
endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
mKeyguardStateController.notifyPanelFlingEnd();
notifyExpandingFinished();
return;
}
mIsFlinging = true;
- mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
- ValueAnimator animator = createHeightAnimator(target);
- mFlingTarget = target;
+ // 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 shouldSpringBack = addOverscroll || (mOverExpansion != 0.0f && expand);
+ float overshootAmount = 0.0f;
+ if (addOverscroll) {
+ // Let's overshoot depending on the amount of velocity
+ overshootAmount = MathUtils.lerp(
+ 0.2f,
+ 1.0f,
+ MathUtils.saturate(vel
+ / (mFlingAnimationUtils.getHighVelocityPxPerSecond()
+ * FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT)));
+ overshootAmount += mOverExpansion / mPanelFlingOvershootAmount;
+ }
+ ValueAnimator animator = createHeightAnimator(target, overshootAmount);
if (expand) {
if (expandBecauseOfFalsing && vel < 0) {
vel = 0;
}
- mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, mView.getHeight());
+ mFlingAnimationUtils.apply(animator, mExpandedHeight,
+ target + overshootAmount * mPanelFlingOvershootAmount, vel, mView.getHeight());
if (vel == 0) {
animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
}
@@ -570,7 +606,6 @@ public abstract class PanelViewController {
animator.setDuration(mFixedDuration);
}
}
- mFlingVelocity = vel;
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -586,7 +621,7 @@ public abstract class PanelViewController {
@Override
public void onAnimationEnd(Animator animation) {
- if (expand && mFlingVelocity > 0) {
+ if (shouldSpringBack && !mCancelled) {
// After the shade is flinged open to an overscrolled state, spring back
// the shade by reducing section padding to 0.
springBack();
@@ -600,14 +635,19 @@ public abstract class PanelViewController {
}
private void springBack() {
- ValueAnimator animator = ValueAnimator.ofFloat(MAX_OVERSCROLL, 0);
+ if (mOverExpansion == 0) {
+ onFlingEnd(false /* cancelled */);
+ return;
+ }
+ mIsSpringBackAnimation = true;
+ ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
animator.addUpdateListener(
animation -> {
- setSectionPadding((float) animation.getAnimatedValue());
- setExpandedHeightInternal(mFlingTarget);
+ setOverExpansionInternal((float) animation.getAnimatedValue(),
+ false /* isFromGesture */);
});
animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
- animator.setInterpolator(Interpolators.LINEAR);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
@@ -616,6 +656,7 @@ public abstract class PanelViewController {
}
@Override
public void onAnimationEnd(Animator animation) {
+ mIsSpringBackAnimation = false;
onFlingEnd(mCancelled);
}
});
@@ -625,6 +666,8 @@ public abstract class PanelViewController {
private void onFlingEnd(boolean cancelled) {
mIsFlinging = false;
+ // No overshoot when the animation ends
+ setOverExpansionInternal(0, false /* isFromGesture */);
setAnimator(null);
mKeyguardStateController.notifyPanelFlingEnd();
if (!cancelled) {
@@ -644,7 +687,7 @@ public abstract class PanelViewController {
public void setExpandedHeight(float height) {
if (DEBUG) logf("setExpandedHeight(%.1f)", height);
- setExpandedHeightInternal(height + getOverExpansionPixels());
+ setExpandedHeightInternal(height);
}
protected void requestPanelHeightUpdate() {
@@ -662,7 +705,7 @@ public abstract class PanelViewController {
return;
}
- if (mHeightAnimator != null) {
+ if (mHeightAnimator != null && !mIsSpringBackAnimation) {
mPanelUpdateWhenAnimatorEnds = true;
return;
}
@@ -677,38 +720,24 @@ public abstract class PanelViewController {
return stackHeightFraction;
}
- // When the shade is flinged open, add space before sections for overscroll effect.
- private void maybeOverScrollForShadeFlingOpen(float height) {
- if (!mBar.isShadeOpening() || mFlingVelocity <= 0) {
- return;
- }
- final float padding = MathUtils.lerp(
- MIN_OVERSCROLL, MAX_OVERSCROLL, getStackHeightFraction(height));
- setSectionPadding(padding);
- }
-
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
- maybeOverScrollForShadeFlingOpen(h);
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
- float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
+ float maxPanelHeight = getMaxPanelHeight();
if (mHeightAnimator == null) {
- float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
- if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
- setOverExpansion(overExpansionPixels, true /* isPixels */);
+ if (mTracking) {
+ float overExpansionPixels = Math.max(0, h - maxPanelHeight);
+ setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
}
- mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
+ mExpandedHeight = Math.min(h, maxPanelHeight);
} else {
mExpandedHeight = h;
- if (mOverExpandedBeforeFling) {
- setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
- }
}
// If we are closing the panel and we are almost there due to a slow decelerating
@@ -720,7 +749,7 @@ public abstract class PanelViewController {
}
}
mExpandedFraction = Math.min(1f,
- fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
+ maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
onHeightUpdated(mExpandedHeight);
notifyBarPanelExpansionChanged();
}
@@ -731,16 +760,31 @@ public abstract class PanelViewController {
*/
protected abstract boolean isTrackingBlocked();
- protected abstract void setSectionPadding(float padding);
+ protected void setOverExpansion(float overExpansion) {
+ mOverExpansion = overExpansion;
+ }
- protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
+ /**
+ * Set the current overexpansion
+ *
+ * @param overExpansion the amount of overexpansion to apply
+ * @param isFromGesture is this amount from a gesture and needs to be rubberBanded?
+ */
+ private void setOverExpansionInternal(float overExpansion, boolean isFromGesture) {
+ if (!isFromGesture) {
+ mLastGesturedOverExpansion = -1;
+ setOverExpansion(overExpansion);
+ } else if (mLastGesturedOverExpansion != overExpansion) {
+ mLastGesturedOverExpansion = overExpansion;
+ final float heightForFullOvershoot = mView.getHeight() / 3.0f;
+ float newExpansion = MathUtils.saturate(overExpansion / heightForFullOvershoot);
+ newExpansion = Interpolators.getOvershootInterpolation(newExpansion);
+ setOverExpansion(newExpansion * mPanelFlingOvershootAmount * 2.0f);
+ }
+ }
protected abstract void onHeightUpdated(float expandedHeight);
- protected abstract float getOverExpansionAmount();
-
- protected abstract float getOverExpansionPixels();
-
/**
* This returns the maximum height of the panel. Children should override this if their
* desired height is not the full height.
@@ -977,9 +1021,32 @@ public abstract class PanelViewController {
}
private ValueAnimator createHeightAnimator(float targetHeight) {
+ return createHeightAnimator(targetHeight, 0.0f /* performOvershoot */);
+ }
+
+ /**
+ * Create an animator that can also overshoot
+ *
+ * @param targetHeight the target height
+ * @param overshootAmount the amount of overshoot desired
+ */
+ private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
+ float startExpansion = mOverExpansion;
ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
animator.addUpdateListener(
- animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
+ animation -> {
+ if (overshootAmount > 0.0f
+ // Also remove the overExpansion when collapsing
+ || (targetHeight == 0.0f && startExpansion != 0)) {
+ final float expansion = MathUtils.lerp(
+ startExpansion,
+ mPanelFlingOvershootAmount * overshootAmount,
+ Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ animator.getAnimatedFraction()));
+ setOverExpansionInternal(expansion, false /* isFromGesture */);
+ }
+ setExpandedHeightInternal((float) animation.getAnimatedValue());
+ });
return animator;
}
@@ -989,7 +1056,7 @@ public abstract class PanelViewController {
mExpandedFraction,
mExpandedFraction > 0f || mInstantExpanding
|| isPanelVisibleBecauseOfHeadsUp() || mTracking
- || mHeightAnimator != null);
+ || mHeightAnimator != null && !mIsSpringBackAnimation);
}
for (int i = 0; i < mExpansionListeners.size(); i++) {
mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
@@ -1037,14 +1104,6 @@ public abstract class PanelViewController {
public abstract void resetViews(boolean animate);
-
- /**
- * @return whether "Clear all" button will be visible when the panel is fully expanded
- */
- protected abstract boolean fullyExpandedClearAllVisible();
-
- protected abstract boolean isClearAllVisible();
-
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
}
@@ -1080,6 +1139,11 @@ public abstract class PanelViewController {
return new OnConfigurationChangedListener();
}
+ /**
+ * Set that the panel is currently opening and not fully opened or closed.
+ */
+ public abstract void setIsShadeOpening(boolean opening);
+
public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
@@ -1108,7 +1172,7 @@ public abstract class PanelViewController {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mStatusBar.userActivity();
- mAnimatingOnDown = mHeightAnimator != null;
+ mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
mMinExpandHeight = 0.0f;
mDownTime = SystemClock.uptimeMillis();
if (mAnimatingOnDown && mClosing && !mHintAnimationRunning) {
@@ -1227,10 +1291,10 @@ public abstract class PanelViewController {
mCollapsedAndHeadsUpOnDown =
isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
addMovement(event);
- if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
- && !mHintAnimationRunning)) {
- mTouchSlopExceeded =
- (mHeightAnimator != null && !mHintAnimationRunning)
+ boolean regularHeightAnimationRunning = mHeightAnimator != null
+ && !mHintAnimationRunning && !mIsSpringBackAnimation;
+ if (!mGestureWaitForTouchSlop || regularHeightAnimationRunning) {
+ mTouchSlopExceeded = regularHeightAnimationRunning
|| mTouchSlopExceededBeforeDown;
cancelHeightAnimator();
onTrackingStarted();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index ffb53a8b2e11c..4b5657afe02fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -638,8 +638,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
public void testCancelSwipeWhileLocked_notifiesKeyguardState() {
mStatusBarStateController.setState(KEYGUARD);
- mNotificationPanelViewController.setOverExpansion(100f, true);
-
// Fling expanded (cancelling the keyguard exit swipe). We should notify keyguard state that
// the fling occurred and did not dismiss the keyguard.
mNotificationPanelViewController.flingToHeight(