summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java187
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java292
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java10
10 files changed, 438 insertions, 187 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9f12124..b6c04f7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -272,6 +272,9 @@
<!-- The padding between the individual notification cards. -->
<dimen name="notification_padding">4dp</dimen>
+ <!-- The minimum amount of top overscroll to go to the quick settings. -->
+ <dimen name="min_top_overscroll_to_qs">36dp</dimen>
+
<!-- The height of the collapsed speed bump view. -->
<dimen name="speed_bump_height_collapsed">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 4d6d815..e5e3a1a 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -42,6 +42,7 @@ public class ExpandHelper implements Gefingerpoken {
boolean canChildBeExpanded(View v);
void setUserExpandedChild(View v, boolean userExpanded);
void setUserLockedChild(View v, boolean userLocked);
+ void expansionStateChanged(boolean isExpanding);
}
private static final String TAG = "ExpandHelper";
@@ -77,7 +78,6 @@ public class ExpandHelper implements Gefingerpoken {
private boolean mWatchingForPull;
private boolean mHasPopped;
private View mEventSource;
- private View mCurrView;
private float mOldHeight;
private float mNaturalHeight;
private float mInitialTouchFocusY;
@@ -86,8 +86,7 @@ public class ExpandHelper implements Gefingerpoken {
private float mLastFocusY;
private float mLastSpanY;
private int mTouchSlop;
- private int mLastMotionY;
- private float mPopLimit;
+ private float mLastMotionY;
private int mPopDuration;
private float mPullGestureMinXSpan;
private Callback mCallback;
@@ -95,10 +94,14 @@ public class ExpandHelper implements Gefingerpoken {
private ViewScaler mScaler;
private ObjectAnimator mScaleAnimation;
private Vibrator mVibrator;
+ private boolean mEnabled = true;
+ private ExpandableView mResizedView;
+ private float mCurrentHeight;
private int mSmallSize;
private int mLargeSize;
private float mMaximumStretch;
+ private boolean mOnlyMovements;
private int mGravity;
@@ -109,17 +112,14 @@ public class ExpandHelper implements Gefingerpoken {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()");
- float focusX = detector.getFocusX();
- float focusY = detector.getFocusY();
- final ExpandableView underFocus = findView(focusX, focusY);
- startExpanding(underFocus, STRETCH);
+ startExpanding(mResizedView, STRETCH);
return mExpanding;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
- if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mCurrView);
+ if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mResizedView);
return true;
}
@@ -138,6 +138,7 @@ public class ExpandHelper implements Gefingerpoken {
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
mView.setActualHeight((int) h);
+ mCurrentHeight = h;
}
public float getHeight() {
return mView.getActualHeight();
@@ -165,7 +166,6 @@ public class ExpandHelper implements Gefingerpoken {
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
- mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
@@ -188,7 +188,6 @@ public class ExpandHelper implements Gefingerpoken {
float target = hand + mOldHeight;
float newHeight = clamp(target);
mScaler.setHeight(newHeight);
-
mLastFocusY = mSGD.getFocusY();
mLastSpanY = mSGD.getCurrentSpan();
}
@@ -252,6 +251,9 @@ public class ExpandHelper implements Gefingerpoken {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ return false;
+ }
final int action = ev.getAction();
if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
@@ -270,38 +272,34 @@ public class ExpandHelper implements Gefingerpoken {
if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan);
if (mExpanding) {
+ mLastMotionY = ev.getRawY();
return true;
} else {
if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) {
// we've begun Venetian blinds style expansion
return true;
}
- final float xspan = mSGD.getCurrentSpanX();
- if ((action == MotionEvent.ACTION_MOVE &&
- xspan > mPullGestureMinXSpan &&
- xspan > mSGD.getCurrentSpanY())) {
- // detect a vertical pulling gesture with fingers somewhat separated
- if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
-
- final ExpandableView underFocus = findView(x, y);
- startExpanding(underFocus, PULL);
- return true;
- }
- if (mScrollAdapter != null && !mScrollAdapter.isScrolledToTop()) {
- return false;
- }
- // Now look for other gestures
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
+ final float xspan = mSGD.getCurrentSpanX();
+ if (xspan > mPullGestureMinXSpan &&
+ xspan > mSGD.getCurrentSpanY() && !mExpanding) {
+ // detect a vertical pulling gesture with fingers somewhat separated
+ if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
+ startExpanding(mResizedView, PULL);
+ mWatchingForPull = false;
+ }
if (mWatchingForPull) {
- final int yDiff = y - mLastMotionY;
+ final float yDiff = ev.getRawY() - mInitialTouchY;
if (yDiff > mTouchSlop) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
- mLastMotionY = y;
- final ExpandableView underFocus = findView(x, y);
- if (startExpanding(underFocus, BLINDS)) {
- mInitialTouchY = mLastMotionY;
- mHasPopped = false;
+ mWatchingForPull = false;
+ if (mResizedView != null && !isFullyExpanded(mResizedView)) {
+ if (startExpanding(mResizedView, BLINDS)) {
+ mLastMotionY = ev.getRawY();
+ mInitialTouchY = ev.getRawY();
+ mHasPopped = false;
+ }
}
}
}
@@ -310,8 +308,10 @@ public class ExpandHelper implements Gefingerpoken {
case MotionEvent.ACTION_DOWN:
mWatchingForPull = mScrollAdapter != null &&
- isInside(mScrollAdapter.getHostView(), x, y);
- mLastMotionY = y;
+ isInside(mScrollAdapter.getHostView(), x, y)
+ && mScrollAdapter.isScrolledToTop();
+ mResizedView = findView(x, y);
+ mInitialTouchY = ev.getY();
break;
case MotionEvent.ACTION_CANCEL:
@@ -321,12 +321,28 @@ public class ExpandHelper implements Gefingerpoken {
clearView();
break;
}
+ mLastMotionY = ev.getRawY();
return mExpanding;
}
}
+ public void setEnabled(boolean enable) {
+ mEnabled = enable;
+ }
+
+ private boolean isEnabled() {
+ return mEnabled;
+ }
+
+ private boolean isFullyExpanded(ExpandableView underFocus) {
+ return underFocus.getIntrinsicHeight() == underFocus.getMaxHeight();
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ return false;
+ }
final int action = ev.getActionMasked();
if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
@@ -335,47 +351,71 @@ public class ExpandHelper implements Gefingerpoken {
(0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
mSGD.onTouchEvent(ev);
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ if (mOnlyMovements) {
+ mLastMotionY = ev.getRawY();
+ return false;
+ }
switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mWatchingForPull = mScrollAdapter != null &&
+ isInside(mScrollAdapter.getHostView(), x, y);
+ mResizedView = findView(x, y);
+ mInitialTouchY = ev.getY();
+ break;
case MotionEvent.ACTION_MOVE: {
- if (0 != (mExpansionStyle & BLINDS)) {
- final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight;
+ if (mWatchingForPull) {
+ final float yDiff = ev.getRawY() - mInitialTouchY;
+ if (yDiff > mTouchSlop) {
+ if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
+ mWatchingForPull = false;
+ if (mResizedView != null && !isFullyExpanded(mResizedView)) {
+ if (startExpanding(mResizedView, BLINDS)) {
+ mInitialTouchY = ev.getRawY();
+ mLastMotionY = ev.getRawY();
+ mHasPopped = false;
+ }
+ }
+ }
+ }
+ if (mExpanding && 0 != (mExpansionStyle & BLINDS)) {
+ final float rawHeight = ev.getRawY() - mLastMotionY + mCurrentHeight;
final float newHeight = clamp(rawHeight);
- final boolean wasClosed = (mOldHeight == mSmallSize);
boolean isFinished = false;
+ boolean expanded = false;
if (rawHeight > mNaturalHeight) {
isFinished = true;
+ expanded = true;
}
if (rawHeight < mSmallSize) {
isFinished = true;
+ expanded = false;
}
- final float pull = Math.abs(ev.getY() - mInitialTouchY);
- if (mHasPopped || pull > mPopLimit) {
- if (!mHasPopped) {
- vibrate(mPopDuration);
- mHasPopped = true;
- }
+ if (!mHasPopped) {
+ vibrate(mPopDuration);
+ mHasPopped = true;
}
- if (mHasPopped) {
- mScaler.setHeight(newHeight);
- }
-
- final int x = (int) mSGD.getFocusX();
- final int y = (int) mSGD.getFocusY();
- ExpandableView underFocus = findView(x, y);
- if (isFinished && underFocus != null && underFocus != mCurrView) {
- finishExpanding(false); // @@@ needed?
- startExpanding(underFocus, BLINDS);
- mInitialTouchY = y;
- mHasPopped = false;
+ mScaler.setHeight(newHeight);
+ mLastMotionY = ev.getRawY();
+ if (isFinished) {
+ mCallback.setUserExpandedChild(mResizedView, expanded);
+ mCallback.expansionStateChanged(false);
+ return false;
+ } else {
+ mCallback.expansionStateChanged(true);
}
return true;
}
if (mExpanding) {
+
+ // Gestural expansion is running
updateExpansion();
+ mLastMotionY = ev.getRawY();
return true;
}
@@ -396,6 +436,7 @@ public class ExpandHelper implements Gefingerpoken {
clearView();
break;
}
+ mLastMotionY = ev.getRawY();
return true;
}
@@ -407,15 +448,16 @@ public class ExpandHelper implements Gefingerpoken {
return false;
}
mExpansionStyle = expandType;
- if (mExpanding && v == mCurrView) {
+ if (mExpanding && v == mResizedView) {
return true;
}
mExpanding = true;
+ mCallback.expansionStateChanged(true);
if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
mCallback.setUserLockedChild(v, true);
- setView(v);
- mScaler.setView((ExpandableView) v);
+ mScaler.setView(v);
mOldHeight = mScaler.getHeight();
+ mCurrentHeight = mOldHeight;
if (mCallback.canChildBeExpanded(v)) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
@@ -425,14 +467,13 @@ public class ExpandHelper implements Gefingerpoken {
}
if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
- v.getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
private void finishExpanding(boolean force) {
if (!mExpanding) return;
- if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
float currentHeight = mScaler.getHeight();
float targetHeight = mSmallSize;
@@ -446,11 +487,12 @@ public class ExpandHelper implements Gefingerpoken {
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mCallback.setUserExpandedChild(mCurrView, targetHeight == mNaturalHeight);
+ mCallback.setUserExpandedChild(mResizedView, targetHeight == mNaturalHeight);
+ mCallback.expansionStateChanged(false);
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
- final View scaledView = mCurrView;
+ final View scaledView = mResizedView;
mScaleAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -460,7 +502,7 @@ public class ExpandHelper implements Gefingerpoken {
});
mScaleAnimation.start();
} else {
- mCallback.setUserLockedChild(mCurrView, false);
+ mCallback.setUserLockedChild(mResizedView, false);
}
mExpanding = false;
@@ -470,16 +512,11 @@ public class ExpandHelper implements Gefingerpoken {
if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight);
if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize);
if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight);
- if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "scale was finished on view: " + mResizedView);
}
private void clearView() {
- mCurrView = null;
-
- }
-
- private void setView(View v) {
- mCurrView = v;
+ mResizedView = null;
}
/**
@@ -494,6 +531,18 @@ public class ExpandHelper implements Gefingerpoken {
}
/**
+ * Change the expansion mode to only observe movements and don't perform any resizing.
+ * This is needed when the expanding is finished and the scroller kicks in,
+ * performing an overscroll motion. We only want to shrink it again when we are not
+ * overscrolled.
+ *
+ * @param onlyMovements Should only movements be observed?
+ */
+ public void onlyObserveMovements(boolean onlyMovements) {
+ mOnlyMovements = onlyMovements;
+ }
+
+ /**
* Triggers haptic feedback.
*/
private synchronized void vibrate(long duration) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 5b2ea0b..517a4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -25,7 +25,6 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -87,6 +86,7 @@ public class DragDownHelper implements Gefingerpoken {
captureStartingChild(mInitialTouchX, mInitialTouchY);
mInitialTouchY = y;
mInitialTouchX = x;
+ mOnDragDownListener.onTouchSlopExceeded();
return true;
}
break;
@@ -202,5 +202,6 @@ public class DragDownHelper implements Gefingerpoken {
void onDraggedDown(View startingChild);
void onDragDownReset();
void onThresholdReached();
+ void onTouchSlopExceeded();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index dbce718..e30117f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -83,10 +83,7 @@ public class NotificationPanelView extends PanelView implements
private float mQsExpansionHeight;
private int mQsMinExpansionHeight;
private int mQsMaxExpansionHeight;
- private int mMinStackHeight;
private int mQsPeekHeight;
- private float mNotificationTranslation;
- private int mStackScrollerIntrinsicPadding;
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
@@ -165,7 +162,6 @@ public class NotificationPanelView extends PanelView implements
super.loadDimens();
mNotificationTopPadding = getResources().getDimensionPixelSize(
R.dimen.notifications_top_padding);
- mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
mStatusBarMinHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
@@ -185,7 +181,8 @@ public class NotificationPanelView extends PanelView implements
mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
if (mQsExpanded) {
if (mQsFullyExpanded) {
- setQsStackScrollerPadding(mQsMaxExpansionHeight);
+ mQsExpansionHeight = mQsMaxExpansionHeight;
+ requestScrollerTopPaddingUpdate(false /* animate */);
}
} else {
if (!mStackScrollerOverscrolling) {
@@ -202,11 +199,12 @@ public class NotificationPanelView extends PanelView implements
*/
private void positionClockAndNotifications() {
boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+ int stackScrollerPadding;
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
int bottom = mStackScrollerOverscrolling
? mHeader.getCollapsedHeight()
: mHeader.getBottom();
- mStackScrollerIntrinsicPadding = bottom + mQsPeekHeight
+ stackScrollerPadding = bottom + mQsPeekHeight
+ mNotificationTopPadding;
mTopPaddingAdjustment = 0;
} else {
@@ -224,11 +222,11 @@ public class NotificationPanelView extends PanelView implements
mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
applyClockAlpha(mClockPositionResult.clockAlpha);
- mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+ stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
- mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
- mAnimateNextTopPaddingChange || animateClock);
+ mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
+ requestScrollerTopPaddingUpdate(animateClock);
mAnimateNextTopPaddingChange = false;
}
@@ -384,6 +382,7 @@ public class NotificationPanelView extends PanelView implements
mInitialTouchX = x;
mQsTracking = true;
mIntercepting = false;
+ mNotificationStackScroller.removeLongPressCallback();
return true;
}
break;
@@ -523,6 +522,13 @@ public class NotificationPanelView extends PanelView implements
updateQsState();
}
+ @Override
+ public void flingTopOverscroll(float velocity, boolean open) {
+ mStackScrollerOverscrolling = false;
+ setQsExpansion(mQsExpansionHeight);
+ flingSettings(velocity, open);
+ }
+
private void onQsExpansionStarted() {
onQsExpansionStarted(0);
}
@@ -554,7 +560,9 @@ public class NotificationPanelView extends PanelView implements
mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
mNotificationStackScroller.setEnabled(!mQsExpanded);
mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded ? View.INVISIBLE : View.VISIBLE);
+ mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
+ ? View.INVISIBLE
+ : View.VISIBLE);
mScrollView.setTouchEnabled(mQsExpanded);
}
@@ -569,9 +577,7 @@ public class NotificationPanelView extends PanelView implements
mQsExpansionHeight = height;
mHeader.setExpansion(height - mQsPeekHeight);
setQsTranslation(height);
- if (!mStackScrollerOverscrolling) {
- setQsStackScrollerPadding(height);
- }
+ requestScrollerTopPaddingUpdate(false /* animate */);
mStatusBar.userActivity();
}
@@ -579,24 +585,11 @@ public class NotificationPanelView extends PanelView implements
mQsContainer.setY(height - mQsContainer.getHeight());
}
- private void setQsStackScrollerPadding(float height) {
- float start = height - mScrollView.getScrollY() + mNotificationTopPadding;
- float stackHeight = mNotificationStackScroller.getHeight() - start;
- if (stackHeight <= mMinStackHeight) {
- float overflow = mMinStackHeight - stackHeight;
- stackHeight = mMinStackHeight;
- start = mNotificationStackScroller.getHeight() - stackHeight;
- mNotificationStackScroller.setTranslationY(overflow);
- mNotificationTranslation = overflow + mScrollView.getScrollY();
- } else {
- mNotificationStackScroller.setTranslationY(0);
- mNotificationTranslation = mScrollView.getScrollY();
- }
- mNotificationStackScroller.setTopPadding(clampQsStackScrollerPadding((int) start), false);
- }
- private int clampQsStackScrollerPadding(int desiredPadding) {
- return Math.max(desiredPadding, mStackScrollerIntrinsicPadding);
+ private void requestScrollerTopPaddingUpdate(boolean animate) {
+ mNotificationStackScroller.updateTopPadding(mQsExpansionHeight,
+ mScrollView.getScrollY(),
+ mAnimateNextTopPaddingChange || animate);
}
private void trackMovement(MotionEvent event) {
@@ -705,9 +698,11 @@ public class NotificationPanelView extends PanelView implements
protected int getMaxPanelHeight() {
// TODO: Figure out transition for collapsing when QS is open, adjust height here.
int maxPanelHeight = super.getMaxPanelHeight();
- int emptyBottomMargin = mStackScrollerContainer.getHeight()
- - mNotificationStackScroller.getHeight()
- + mNotificationStackScroller.getEmptyBottomMargin();
+ int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+ emptyBottomMargin = (int) Math.max(0,
+ emptyBottomMargin - mNotificationStackScroller.getCurrentOverScrollAmount(true));
+ emptyBottomMargin += mStackScrollerContainer.getHeight()
+ - mNotificationStackScroller.getHeight();
int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
return maxHeight;
@@ -814,13 +809,14 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onOverExpansionChanged(float overExpansion) {
- float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
- float expansionChange = overExpansion - mOverExpansion;
- expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
- mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
- true /* onTop */,
- false /* animate */);
- super.onOverExpansionChanged(overExpansion);
+ if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+ float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
+ float expansionChange = overExpansion - mOverExpansion;
+ expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
+ mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
+ true /* onTop */,
+ false /* animate */);
+ }
}
@Override
@@ -835,7 +831,6 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand);
- mOverExpansion = 0.0f;
mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, true /* animate */);
if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
@@ -860,8 +855,7 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onScrollChanged() {
if (mQsExpanded) {
- mNotificationStackScroller.setTranslationY(
- mNotificationTranslation - mScrollView.getScrollY());
+ requestScrollerTopPaddingUpdate(false /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index f43f348..e4133db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -53,7 +53,7 @@ public abstract class PanelView extends FrameLayout {
private int mEdgeTapAreaWidth;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
- private float mExpandedHeight = 0;
+ protected float mExpandedHeight = 0;
private boolean mJustPeeked;
private boolean mClosing;
protected boolean mTracking;
@@ -369,8 +369,10 @@ public abstract class PanelView extends FrameLayout {
protected void fling(float vel, boolean expand) {
cancelPeek();
float target = expand ? getMaxPanelHeight() : 0.0f;
- if (target == mExpandedHeight) {
+ if (target == mExpandedHeight || mOverExpansion > 0) {
onExpandingFinished();
+ mExpandedHeight = target;
+ mOverExpansion = 0.0f;
mBar.panelExpansionChanged(this, mExpandedFraction);
return;
}
@@ -459,6 +461,7 @@ public abstract class PanelView extends FrameLayout {
overExpansion = Math.max(0, overExpansion);
if (overExpansion != mOverExpansion) {
onOverExpansionChanged(overExpansion);
+ mOverExpansion = overExpansion;
}
if (DEBUG) {
@@ -469,9 +472,7 @@ public abstract class PanelView extends FrameLayout {
mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
}
- protected void onOverExpansionChanged(float overExpansion) {
- mOverExpansion = overExpansion;
- }
+ protected abstract void onOverExpansionChanged(float overExpansion);
protected abstract void onHeightUpdated(float expandedHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 082fe3a..93b5ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2894,9 +2894,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void updateStackScrollerState() {
if (mStackScroller == null) return;
- mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
- mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+ boolean onKeyguard = mState == StatusBarState.KEYGUARD;
+ mStackScroller.setDimmed(onKeyguard, false /* animate */);
+ mStackScroller.setVisibility(!mShowLockscreenNotifications && onKeyguard
? View.INVISIBLE : View.VISIBLE);
+ mStackScroller.setScrollingEnabled(!onKeyguard);
+ mStackScroller.setExpandingEnabled(!onKeyguard);
}
public void userActivity() {
@@ -3032,6 +3035,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
}
+ @Override
+ public void onTouchSlopExceeded() {
+ mStackScroller.removeLongPressCallback();
+ }
+
/**
* If secure with redaction: Show bouncer, go to unlocked shade.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index b51626d..d5e8e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -40,7 +40,6 @@ public class StatusBarWindowView extends FrameLayout {
public static final String TAG = "StatusBarWindowView";
public static final boolean DEBUG = BaseStatusBar.DEBUG;
- private ExpandHelper mExpandHelper;
private DragDownHelper mDragDownHelper;
private NotificationStackScrollLayout mStackScrollLayout;
private NotificationPanelView mNotificationPanel;
@@ -73,12 +72,6 @@ public class StatusBarWindowView extends FrameLayout {
mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
R.id.notification_stack_scroller);
mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
- mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout,
- minHeight, maxHeight);
- mExpandHelper.setEventSource(this);
- mExpandHelper.setScrollAdapter(mStackScrollLayout);
mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
// We really need to be able to animate while window animations are going on
@@ -114,12 +107,6 @@ public class StatusBarWindowView extends FrameLayout {
boolean intercept = false;
if (mNotificationPanel.isFullyExpanded()
&& mStackScrollLayout.getVisibility() == View.VISIBLE
- && (mService.getBarState() == StatusBarState.SHADE
- || (mService.getBarState() == StatusBarState.SHADE_LOCKED
- && !mService.isBouncerShowing()))) {
- intercept = mExpandHelper.onInterceptTouchEvent(ev);
- } else if (mNotificationPanel.isFullyExpanded()
- && mStackScrollLayout.getVisibility() == View.VISIBLE
&& mService.getBarState() == StatusBarState.KEYGUARD
&& !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
@@ -139,10 +126,7 @@ public class StatusBarWindowView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
- if (mNotificationPanel.isFullyExpanded()
- && mService.getBarState() != StatusBarState.KEYGUARD) {
- handled = mExpandHelper.onTouchEvent(ev);
- } else if (mService.getBarState() == StatusBarState.KEYGUARD) {
+ if (mService.getBarState() == StatusBarState.KEYGUARD) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
@@ -168,8 +152,8 @@ public class StatusBarWindowView extends FrameLayout {
}
public void cancelExpandHelper() {
- if (mExpandHelper != null) {
- mExpandHelper.cancel();
+ if (mStackScrollLayout != null) {
+ mStackScrollLayout.cancelExpandHelper();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index ac26da2..df01c12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -225,6 +225,11 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
}
}
+ @Override
+ public void expansionStateChanged(boolean isExpanding) {
+
+ }
+
// SwipeHelper.Callback methods
@Override
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 5c98d51..bcfe7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -30,7 +30,6 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.widget.OverScroller;
-
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
@@ -51,13 +50,15 @@ 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;
+ private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
+ private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
/**
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
*/
private static final int INVALID_POINTER = -1;
+ private ExpandHelper mExpandHelper;
private SwipeHelper mSwipeHelper;
private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
@@ -73,6 +74,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private float mMaxOverScroll;
private boolean mIsBeingDragged;
private int mLastMotionY;
+ private int mDownX;
private int mActivePointerId;
private int mSidePaddings;
@@ -128,6 +130,38 @@ public class NotificationStackScrollLayout extends ViewGroup
private boolean mChildrenUpdateRequested;
private SpeedBumpView mSpeedBumpView;
private boolean mIsExpansionChanging;
+ private boolean mExpandingNotification;
+ private boolean mExpandedInThisMotion;
+ private boolean mScrollingEnabled;
+
+ /**
+ * Was the scroller scrolled to the top when the down motion was observed?
+ */
+ private boolean mScrolledToTopOnFirstDown;
+
+ /**
+ * The minimal amount of over scroll which is needed in order to switch to the quick settings
+ * when over scrolling on a expanded card.
+ */
+ private float mMinTopOverScrollToEscape;
+ private int mIntrinsicPadding;
+ private int mNotificationTopPadding;
+ private int mMinStackHeight;
+ private boolean mDontReportNextOverScroll;
+
+ /**
+ * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
+ * This is needed to avoid scrolling too far after the notification was collapsed in the same
+ * motion.
+ */
+ private int mMaxScrollAfterExpand;
+
+ /**
+ * Should in this touch motion only be scrolling allowed? It's true when the scroller was
+ * animating.
+ */
+ private boolean mOnlyScrollingInThisMotion;
+
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -207,6 +241,17 @@ public class NotificationStackScrollLayout extends ViewGroup
mPaddingBetweenElementsNormal = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding);
updatePadding(false);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ mExpandHelper = new ExpandHelper(getContext(), this,
+ minHeight, maxHeight);
+ mExpandHelper.setEventSource(this);
+ mExpandHelper.setScrollAdapter(this);
+ mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
+ R.dimen.min_top_overscroll_to_qs);
+ mNotificationTopPadding = getResources().getDimensionPixelSize(
+ R.dimen.notifications_top_padding);
+ mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
}
private void updatePadding(boolean dimmed) {
@@ -343,7 +388,7 @@ public class NotificationStackScrollLayout extends ViewGroup
return mTopPadding;
}
- public void setTopPadding(int topPadding, boolean animate) {
+ private void setTopPadding(int topPadding, boolean animate) {
if (mTopPadding != topPadding) {
mTopPadding = topPadding;
updateAlgorithmHeightAndPadding();
@@ -511,6 +556,29 @@ public class NotificationStackScrollLayout extends ViewGroup
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
}
+ removeLongPressCallback();
+ requestDisallowInterceptTouchEvent(true);
+ }
+
+ @Override
+ public void expansionStateChanged(boolean isExpanding) {
+ mExpandingNotification = isExpanding;
+ if (!mExpandedInThisMotion) {
+ mMaxScrollAfterExpand = mOwnScrollY;
+ mExpandedInThisMotion = true;
+ }
+ }
+
+ public void setScrollingEnabled(boolean enable) {
+ mScrollingEnabled = enable;
+ }
+
+ public void setExpandingEnabled(boolean enable) {
+ mExpandHelper.setEnabled(enable);
+ }
+
+ private boolean isScrollingEnabled() {
+ return mScrollingEnabled;
}
public View getChildContentView(View v) {
@@ -548,18 +616,44 @@ public class NotificationStackScrollLayout extends ViewGroup
if (!isEnabled()) {
return false;
}
+ boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || ev.getActionMasked()== MotionEvent.ACTION_UP;
+ boolean expandWantsIt = false;
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (isCancelOrUp) {
+ mExpandHelper.onlyObserveMovements(false);
+ }
+ boolean wasExpandingBefore = mExpandingNotification;
+ expandWantsIt = mExpandHelper.onTouchEvent(ev);
+ if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore) {
+ dispatchDownEventToScroller(ev);
+ }
+ }
boolean scrollerWantsIt = false;
- if (!mSwipingInProgress) {
+ if (!mSwipingInProgress && !mExpandingNotification) {
scrollerWantsIt = onScrollTouch(ev);
}
boolean horizontalSwipeWantsIt = false;
- if (!mIsBeingDragged) {
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion) {
horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
- return horizontalSwipeWantsIt || scrollerWantsIt || super.onTouchEvent(ev);
+ return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+ }
+
+ private void dispatchDownEventToScroller(MotionEvent ev) {
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ onScrollTouch(downEvent);
+ downEvent.recycle();
}
private boolean onScrollTouch(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
@@ -583,6 +677,7 @@ public class NotificationStackScrollLayout extends ViewGroup
// Remember where the motion event started
mLastMotionY = (int) ev.getY();
+ mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
break;
}
@@ -594,8 +689,11 @@ public class NotificationStackScrollLayout extends ViewGroup
}
final int y = (int) ev.getY(activePointerIndex);
+ final int x = (int) ev.getX(activePointerIndex);
int deltaY = mLastMotionY - y;
- if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
+ final int xDiff = Math.abs(x - mDownX);
+ final int yDiff = Math.abs(deltaY);
+ if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
if (deltaY > 0) {
deltaY -= mTouchSlop;
@@ -606,7 +704,10 @@ public class NotificationStackScrollLayout extends ViewGroup
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y;
- final int range = getScrollRange();
+ int range = getScrollRange();
+ if (mExpandedInThisMotion) {
+ range = Math.min(range, mMaxScrollAfterExpand);
+ }
float scrollAmount;
if (deltaY < 0) {
@@ -631,19 +732,28 @@ public class NotificationStackScrollLayout extends ViewGroup
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- if (getChildCount() > 0) {
- if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
- fling(-initialVelocity);
- } else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
- getScrollRange())) {
- postInvalidateOnAnimation();
+ if (shouldOverScrollFling(initialVelocity)) {
+ onOverScrollFling(true, initialVelocity);
+ } else {
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ float currentOverScrollTop = getCurrentOverScrollAmount(true);
+ if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ fling(-initialVelocity);
+ } else {
+ onOverScrollFling(false, initialVelocity);
+ }
+ } else {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ getScrollRange())) {
+ postInvalidateOnAnimation();
+ }
}
}
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
}
break;
case MotionEvent.ACTION_CANCEL:
@@ -658,17 +768,27 @@ public class NotificationStackScrollLayout extends ViewGroup
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
+ mDownX = (int) ev.getX(index);
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+ mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
break;
}
return true;
}
+ private void onOverScrollFling(boolean open, int initialVelocity) {
+ if (mOverscrollTopChangedListener != null) {
+ mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+ }
+ mDontReportNextOverScroll = true;
+ setOverScrollAmount(0.0f, true, false);
+ }
+
/**
* Perform a scroll upwards and adapt the overscroll amounts accordingly
*
@@ -689,11 +809,13 @@ public class NotificationStackScrollLayout extends ViewGroup
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 */);
+ if (!mExpandedInThisMotion) {
+ float currentBottomPixels = getCurrentOverScrolledPixels(false);
+ // We overScroll on the top
+ setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+ false /* onTop */,
+ false /* animate */);
+ }
mOwnScrollY = range;
scrollAmount = 0.0f;
}
@@ -834,7 +956,7 @@ public class NotificationStackScrollLayout extends ViewGroup
* @param animate Should an animation be performed.
*/
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
- setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+ setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true);
}
/**
@@ -870,17 +992,21 @@ public class NotificationStackScrollLayout extends ViewGroup
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop);
} else {
- setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+ setOverScrolledPixels(amount / getRubberBandFactor(), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
- requestChildrenUpdate();
if (onTop) {
- float scrollAmount = mOwnScrollY < 0 ? -mOwnScrollY : 0;
- notifyOverscrollTopListener(scrollAmount + amount);
+ notifyOverscrollTopListener(amount);
}
+ requestChildrenUpdate();
}
}
private void notifyOverscrollTopListener(float amount) {
+ mExpandHelper.onlyObserveMovements(amount > 1.0f);
+ if (mDontReportNextOverScroll) {
+ mDontReportNextOverScroll = false;
+ return;
+ }
if (mOverscrollTopChangedListener != null) {
mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
}
@@ -928,7 +1054,7 @@ public class NotificationStackScrollLayout extends ViewGroup
updateChildren();
float overScrollTop = getCurrentOverScrollAmount(true);
if (mOwnScrollY < 0) {
- notifyOverscrollTopListener(-mOwnScrollY + overScrollTop);
+ notifyOverscrollTopListener(-mOwnScrollY);
} else {
notifyOverscrollTopListener(overScrollTop);
}
@@ -950,6 +1076,7 @@ public class NotificationStackScrollLayout extends ViewGroup
onTop = true;
newAmount = -mOwnScrollY;
mOwnScrollY = 0;
+ mDontReportNextOverScroll = true;
} else {
onTop = false;
newAmount = mOwnScrollY - scrollRange;
@@ -1085,13 +1212,14 @@ public class NotificationStackScrollLayout extends ViewGroup
float bottomAmount = getCurrentOverScrollAmount(false);
if (velocityY < 0 && topAmount > 0) {
mOwnScrollY -= (int) topAmount;
+ mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
mOwnScrollY += bottomAmount;
setOverScrollAmount(0, false, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
* mOverflingDistance + bottomAmount;
} else {
// it will be set once we reach the boundary
@@ -1104,6 +1232,44 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ /**
+ * @return Whether a fling performed on the top overscroll edge lead to the expanded
+ * overScroll view (i.e QS).
+ */
+ private boolean shouldOverScrollFling(int initialVelocity) {
+ float topOverScroll = getCurrentOverScrollAmount(true);
+ return mScrolledToTopOnFirstDown
+ && !mExpandedInThisMotion
+ && topOverScroll > mMinTopOverScrollToEscape
+ && initialVelocity > 0;
+ }
+
+ public void updateTopPadding(float qsHeight, int scrollY, boolean animate) {
+ float start = qsHeight - scrollY + mNotificationTopPadding;
+ float stackHeight = getHeight() - start;
+ if (stackHeight <= mMinStackHeight) {
+ float overflow = mMinStackHeight - stackHeight;
+ stackHeight = mMinStackHeight;
+ start = getHeight() - stackHeight;
+ setTranslationY(overflow);
+ } else {
+ setTranslationY(0);
+ }
+ setTopPadding(clampPadding((int) start), animate);
+ }
+
+ private int clampPadding(int desiredPadding) {
+ return Math.max(desiredPadding, mIntrinsicPadding);
+ }
+
+ private float getRubberBandFactor() {
+ return mExpandedInThisMotion
+ ? RUBBER_BAND_FACTOR_AFTER_EXPAND
+ : (mScrolledToTopOnFirstDown
+ ? 1.0f
+ : RUBBER_BAND_FACTOR_NORMAL);
+ }
+
private void endDrag() {
setIsBeingDragged(false);
@@ -1119,16 +1285,30 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ initDownStates(ev);
+ boolean expandWantsIt = false;
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+ expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+ }
boolean scrollWantsIt = false;
- if (!mSwipingInProgress) {
+ if (!mSwipingInProgress && !mExpandingNotification) {
scrollWantsIt = onInterceptTouchEventScroll(ev);
}
boolean swipeWantsIt = false;
- if (!mIsBeingDragged) {
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion) {
swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
- return swipeWantsIt || scrollWantsIt ||
- super.onInterceptTouchEvent(ev);
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+ }
+
+ private void initDownStates(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mExpandedInThisMotion = false;
+ mOnlyScrollingInThisMotion = !mScroller.isFinished();
+ }
}
@Override
@@ -1350,6 +1530,9 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onMotionEvent will be called and we do the actual
@@ -1366,13 +1549,6 @@ public class NotificationStackScrollLayout extends ViewGroup
return true;
}
- /*
- * Don't try to intercept touch if we can't scroll anyway.
- */
- if (mOwnScrollY == 0 && getScrollRange() == 0) {
- return false;
- }
-
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
/*
@@ -1398,10 +1574,13 @@ public class NotificationStackScrollLayout extends ViewGroup
}
final int y = (int) ev.getY(pointerIndex);
+ final int x = (int) ev.getX(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
- if (yDiff > mTouchSlop) {
+ final int xDiff = Math.abs(x - mDownX);
+ if (yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
mLastMotionY = y;
+ mDownX = x;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
}
@@ -1421,7 +1600,9 @@ public class NotificationStackScrollLayout extends ViewGroup
* ACTION_DOWN always refers to pointer index 0.
*/
mLastMotionY = y;
+ mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
+ mScrolledToTopOnFirstDown = isScrolledToTop();
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
@@ -1468,7 +1649,7 @@ public class NotificationStackScrollLayout extends ViewGroup
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
- mSwipeHelper.removeLongPressCallback();
+ removeLongPressCallback();
}
}
@@ -1476,10 +1657,14 @@ public class NotificationStackScrollLayout extends ViewGroup
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
- mSwipeHelper.removeLongPressCallback();
+ removeLongPressCallback();
}
}
+ public void removeLongPressCallback() {
+ mSwipeHelper.removeLongPressCallback();
+ }
+
@Override
public boolean isScrolledToTop() {
return mOwnScrollY == 0;
@@ -1608,6 +1793,14 @@ public class NotificationStackScrollLayout extends ViewGroup
updateSpeedBump(true);
}
+ public void cancelExpandHelper() {
+ mExpandHelper.cancel();
+ }
+
+ public void setIntrinsicPadding(int intrinsicPadding) {
+ mIntrinsicPadding = intrinsicPadding;
+ }
+
/**
* @return the y position of the first notification
*/
@@ -1627,6 +1820,15 @@ public class NotificationStackScrollLayout extends ViewGroup
*/
public interface OnOverscrollTopChangedListener {
public void onOverscrollTopChanged(float amount);
+
+ /**
+ * Notify a listener that the scroller wants to escape from the scrolling motion and
+ * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
+ *
+ * @param velocity The velocity that the Scroller had when over flinging
+ * @param open Should the fling open or close the overscroll view.
+ */
+ public void flingTopOverscroll(float velocity, boolean open);
}
static class AnimationEvent {
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 2b52c7e..a48cab8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -131,10 +131,14 @@ public class StackScrollAlgorithm {
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
- float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
- algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
- + bottomOverScroll - topOverScroll);
+
+ int scrollY = ambientState.getScrollY();
+
+ // Due to the overScroller, the stackscroller can have negative scroll state. This is
+ // already accounted for by the top padding and doesn't need an additional adaption
+ scrollY = Math.max(0, scrollY);
+ algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);
updateVisibleChildren(resultState, algorithmState);