summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
authorSelim Cinek <cinek@google.com>2014-05-13 15:45:22 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-05-13 15:45:22 +0000
commit2f675acdb2f9b8476017cc8bb21b6c4d34577059 (patch)
tree3c368cf38b34f2cb575459652ad4eb246dbf4547 /packages/SystemUI/src
parentd2cd3bcff4c7d24b847e9202e47c41ce44c3b22c (diff)
parent8d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dc (diff)
downloadframeworks_base-2f675acdb2f9b8476017cc8bb21b6c4d34577059.zip
frameworks_base-2f675acdb2f9b8476017cc8bb21b6c4d34577059.tar.gz
frameworks_base-2f675acdb2f9b8476017cc8bb21b6c4d34577059.tar.bz2
Merge "Introduced overscrolling for the new notifications"
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java275
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java36
4 files changed, 273 insertions, 63 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 4121a40..deab757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -28,6 +28,8 @@ public class AmbientState {
private int mScrollY;
private boolean mDimmed;
private View mActivatedChild;
+ private float mOverScrollTopAmount;
+ private float mOverScrollBottomAmount;
public int getScrollY() {
return mScrollY;
@@ -72,4 +74,16 @@ public class AmbientState {
public View getActivatedChild() {
return mActivatedChild;
}
+
+ public void setOverScrollAmount(float amount, boolean onTop) {
+ if (onTop) {
+ mOverScrollTopAmount = amount;
+ } else {
+ mOverScrollBottomAmount = amount;
+ }
+ }
+
+ public float getOverScrollAmount(boolean top) {
+ return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 5849afb..109a2d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -53,6 +53,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
+ private static final float RUBBER_BAND_FACTOR = 0.35f;
/**
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
@@ -70,8 +71,8 @@ public class NotificationStackScrollLayout extends ViewGroup
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
- private int mOverscrollDistance;
private int mOverflingDistance;
+ private float mMaxOverScroll;
private boolean mIsBeingDragged;
private int mLastMotionY;
private int mActivePointerId;
@@ -107,6 +108,16 @@ public class NotificationStackScrollLayout extends ViewGroup
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
+ /**
+ * The raw amount of the overScroll on the top, which is not rubber-banded.
+ */
+ private float mOverScrolledTopPixels;
+
+ /**
+ * The raw amount of the overScroll on the bottom, which is not rubber-banded.
+ */
+ private float mOverScrolledBottomPixels;
+
private OnChildLocationsChangedListener mListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private boolean mNeedsAnimation;
@@ -175,7 +186,6 @@ public class NotificationStackScrollLayout extends ViewGroup
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
@@ -544,7 +554,7 @@ public class NotificationStackScrollLayout extends ViewGroup
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ mScroller.forceFinished(true);
}
// Remember where the motion event started
@@ -572,40 +582,23 @@ public class NotificationStackScrollLayout extends ViewGroup
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y;
-
- final int oldX = mScrollX;
- final int oldY = mOwnScrollY;
final int range = getScrollRange();
- final int overscrollMode = getOverScrollMode();
- final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+
+ float scrollAmount;
+ if (deltaY < 0) {
+ scrollAmount = overScrollDown(deltaY);
+ } else {
+ scrollAmount = overScrollUp(deltaY, range);
+ }
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
- if (overScrollBy(0, deltaY, 0, mOwnScrollY,
- 0, range, 0, mOverscrollDistance, true)) {
- // Break our velocity if we hit a scroll barrier.
- mVelocityTracker.clear();
+ if (scrollAmount != 0.0f) {
+ // The scrolling motion could not be compensated with the
+ // existing overScroll, we have to scroll the view
+ overScrollBy(0, (int) scrollAmount, 0, mOwnScrollY,
+ 0, range, 0, getHeight() / 2, true);
}
- // TODO: Overscroll
-// if (canOverscroll) {
-// final int pulledToY = oldY + deltaY;
-// if (pulledToY < 0) {
-// mEdgeGlowTop.onPull((float) deltaY / getHeight());
-// if (!mEdgeGlowBottom.isFinished()) {
-// mEdgeGlowBottom.onRelease();
-// }
-// } else if (pulledToY > range) {
-// mEdgeGlowBottom.onPull((float) deltaY / getHeight());
-// if (!mEdgeGlowTop.isFinished()) {
-// mEdgeGlowTop.onRelease();
-// }
-// }
-// if (mEdgeGlowTop != null
-// && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())){
-// postInvalidateOnAnimation();
-// }
-// }
}
break;
case MotionEvent.ACTION_UP:
@@ -652,6 +645,68 @@ public class NotificationStackScrollLayout extends ViewGroup
return true;
}
+ /**
+ * Perform a scroll upwards and adapt the overscroll amounts accordingly
+ *
+ * @param deltaY The amount to scroll upwards, has to be positive.
+ * @return The amount of scrolling to be performed by the scroller,
+ * not handled by the overScroll amount.
+ */
+ private float overScrollUp(int deltaY, int range) {
+ deltaY = Math.max(deltaY, 0);
+ float currentTopAmount = getCurrentOverScrollAmount(true);
+ float newTopAmount = currentTopAmount - deltaY;
+ if (currentTopAmount > 0) {
+ setOverScrollAmount(newTopAmount, true /* onTop */,
+ false /* animate */);
+ }
+ // Top overScroll might not grab all scrolling motion,
+ // we have to scroll as well.
+ float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
+ float newScrollY = mOwnScrollY + scrollAmount;
+ if (newScrollY > range) {
+ float currentBottomPixels = getCurrentOverScrolledPixels(false);
+ // We overScroll on the top
+ setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+ false /* onTop */,
+ false /* animate */);
+ mOwnScrollY = range;
+ scrollAmount = 0.0f;
+ }
+ return scrollAmount;
+ }
+
+ /**
+ * Perform a scroll downward and adapt the overscroll amounts accordingly
+ *
+ * @param deltaY The amount to scroll downwards, has to be negative.
+ * @return The amount of scrolling to be performed by the scroller,
+ * not handled by the overScroll amount.
+ */
+ private float overScrollDown(int deltaY) {
+ deltaY = Math.min(deltaY, 0);
+ float currentBottomAmount = getCurrentOverScrollAmount(false);
+ float newBottomAmount = currentBottomAmount + deltaY;
+ if (currentBottomAmount > 0) {
+ setOverScrollAmount(newBottomAmount, false /* onTop */,
+ false /* animate */);
+ }
+ // Bottom overScroll might not grab all scrolling motion,
+ // we have to scroll as well.
+ float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
+ float newScrollY = mOwnScrollY + scrollAmount;
+ if (newScrollY < 0) {
+ float currentTopPixels = getCurrentOverScrolledPixels(true);
+ // We overScroll on the top
+ setOverScrolledPixels(currentTopPixels - newScrollY,
+ true /* onTop */,
+ false /* animate */);
+ mOwnScrollY = 0;
+ scrollAmount = 0.0f;
+ }
+ return scrollAmount;
+ }
+
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -701,23 +756,16 @@ public class NotificationStackScrollLayout extends ViewGroup
if (oldX != x || oldY != y) {
final int range = getScrollRange();
- final int overscrollMode = getOverScrollMode();
- final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
+ float currVelocity = mScroller.getCurrVelocity();
+ if (currVelocity >= mMinimumVelocity * 20) {
+ mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
+ }
+ }
overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
- 0, mOverflingDistance, false);
+ 0, (int) (mMaxOverScroll), false);
onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
-
- if (canOverscroll) {
- // TODO: Overscroll
-// if (y < 0 && oldY >= 0) {
-// mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-// } else if (y > range && oldY <= range) {
-// mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-// }
- }
- updateChildren();
}
// Keep on drawing until the animation has finished.
@@ -725,6 +773,81 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ @Override
+ protected int computeVerticalScrollRange() {
+ // needed for the overScroller
+ return mContentHeight;
+ }
+
+ /**
+ * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
+ * overscroll effect based on numPixels. By default this will also cancel animations on the
+ * same overScroll edge.
+ *
+ * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
+ * the rubber-banding logic.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ */
+ public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+ setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+ }
+
+ /**
+ * Set the effective overScroll amount which will be directly reflected in the layout.
+ * By default this will also cancel animations on the same overScroll edge.
+ *
+ * @param amount The amount to overScroll by.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ */
+ public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+ setOverScrollAmount(amount, onTop, animate, true);
+ }
+
+ /**
+ * Set the effective overScroll amount which will be directly reflected in the layout.
+ *
+ * @param amount The amount to overScroll by.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ * @param cancelAnimators Should running animations be cancelled.
+ */
+ public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+ boolean cancelAnimators) {
+ if (cancelAnimators) {
+ mStateAnimator.cancelOverScrollAnimators(onTop);
+ }
+ setOverScrollAmountInternal(amount, onTop, animate);
+ }
+
+ private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+ amount = Math.max(0, amount);
+ if (animate) {
+ mStateAnimator.animateOverScrollToAmount(amount, onTop);
+ } else {
+ setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+ mAmbientState.setOverScrollAmount(amount, onTop);
+ requestChildrenUpdate();
+ }
+ }
+
+ public float getCurrentOverScrollAmount(boolean top) {
+ return mAmbientState.getOverScrollAmount(top);
+ }
+
+ public float getCurrentOverScrolledPixels(boolean top) {
+ return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
+ }
+
+ private void setOverScrolledPixels(float amount, boolean onTop) {
+ if (onTop) {
+ mOverScrolledTopPixels = amount;
+ } else {
+ mOverScrolledBottomPixels = amount;
+ }
+ }
+
private void customScrollTo(int y) {
mOwnScrollY = y;
updateChildren();
@@ -738,18 +861,41 @@ public class NotificationStackScrollLayout extends ViewGroup
final int oldY = mOwnScrollY;
mScrollX = scrollX;
mOwnScrollY = scrollY;
- invalidateParentIfNeeded();
- onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
if (clampedY) {
- mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange());
+ springBack();
+ } else {
+ onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
+ invalidateParentIfNeeded();
+ updateChildren();
}
- updateChildren();
} else {
customScrollTo(scrollY);
scrollTo(scrollX, mScrollY);
}
}
+ private void springBack() {
+ int scrollRange = getScrollRange();
+ boolean overScrolledTop = mOwnScrollY <= 0;
+ boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+ if (overScrolledTop || overScrolledBottom) {
+ boolean onTop;
+ float newAmount;
+ if (overScrolledTop) {
+ onTop = true;
+ newAmount = -mOwnScrollY;
+ mOwnScrollY = 0;
+ } else {
+ onTop = false;
+ newAmount = mOwnScrollY - scrollRange;
+ mOwnScrollY = scrollRange;
+ }
+ setOverScrollAmount(newAmount, onTop, false);
+ setOverScrollAmount(0.0f, onTop, true);
+ mScroller.forceFinished(true);
+ }
+ }
+
private int getScrollRange() {
int scrollRange = 0;
ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
@@ -841,7 +987,23 @@ public class NotificationStackScrollLayout extends ViewGroup
int height = (int) getLayoutHeight();
int bottom = getContentHeight();
- mScroller.fling(mScrollX, mOwnScrollY, 0, velocityY, 0, 0, 0,
+ float topAmount = getCurrentOverScrollAmount(true);
+ float bottomAmount = getCurrentOverScrollAmount(false);
+ if (velocityY < 0 && topAmount > 0) {
+ mOwnScrollY -= (int) topAmount;
+ setOverScrollAmount(0, true, false);
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ * mOverflingDistance + topAmount;
+ } else if (velocityY > 0 && bottomAmount > 0) {
+ mOwnScrollY += bottomAmount;
+ setOverScrollAmount(0, false, false);
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ * mOverflingDistance + bottomAmount;
+ } else {
+ // it will be set once we reach the boundary
+ mMaxOverScroll = 0.0f;
+ }
+ mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);
postInvalidateOnAnimation();
@@ -853,11 +1015,12 @@ public class NotificationStackScrollLayout extends ViewGroup
recycleVelocityTracker();
- // TODO: Overscroll
-// if (mEdgeGlowTop != null) {
-// mEdgeGlowTop.onRelease();
-// mEdgeGlowBottom.onRelease();
-// }
+ if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+ setOverScrollAmount(0, true /* onTop */, true /* animate */);
+ }
+ if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+ setOverScrollAmount(0, false /* onTop */, true /* animate */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index bd9de82..d572ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -128,7 +128,10 @@ public class StackScrollAlgorithm {
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
- algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
+ float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
+ float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+ algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
+ + bottomOverScroll - topOverScroll);
updateVisibleChildren(resultState, algorithmState);
@@ -215,7 +218,6 @@ public class StackScrollAlgorithm {
*
* @param resultState The result state to update if a change to the properties of a child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * and which will be updated
*/
private void updatePositionsForState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
@@ -295,7 +297,7 @@ public class StackScrollAlgorithm {
// The first card is always rendered.
if (i == 0) {
childViewState.alpha = 1.0f;
- childViewState.yTranslation = 0;
+ childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
}
if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
@@ -454,7 +456,6 @@ public class StackScrollAlgorithm {
*
* @param resultState The result state to update if a height change of an child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * and which will be updated
*/
private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
@@ -472,7 +473,7 @@ public class StackScrollAlgorithm {
+ childHeight
+ mPaddingBetweenElements;
if (yPositionInScrollView < algorithmState.scrollY) {
- if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
+ if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
// The starting position of the bottom stack peek
int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index ca383aa..5ac51f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.stack;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
@@ -73,6 +72,9 @@ public class StackStateAnimator {
private AnimationFilter mAnimationFilter = new AnimationFilter();
private long mCurrentLength;
+ private ValueAnimator mTopOverScrollAnimator;
+ private ValueAnimator mBottomOverScrollAnimator;
+
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
@@ -510,7 +512,7 @@ public class StackStateAnimator {
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
StackScrollState finalState) {
mNewEvents.clear();
- for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
+ for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
View changingView = event.changingView;
if (!mHandledEvents.contains(event)) {
if (event.animationType == NotificationStackScrollLayout.AnimationEvent
@@ -532,4 +534,34 @@ public class StackStateAnimator {
}
}
}
+
+ public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+ final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+ cancelOverScrollAnimators(onTop);
+ ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
+ targetAmount);
+ overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+ overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float currentOverScroll = (float) animation.getAnimatedValue();
+ mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
+ false /* cancelAnimators */);
+ }
+ });
+ overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ overScrollAnimator.start();
+ if (onTop) {
+ mTopOverScrollAnimator = overScrollAnimator;
+ } else {
+ mBottomOverScrollAnimator = overScrollAnimator;
+ }
+ }
+
+ public void cancelOverScrollAnimators(boolean onTop) {
+ ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
+ if (currentAnimator != null) {
+ currentAnimator.cancel();
+ }
+ }
}