FileDocCategorySizeDatePackage
NotificationStackScrollLayout.javaAPI DocAndroid 5.1 API100356Thu Mar 12 22:22:42 GMT 2015com.android.systemui.statusbar.stack

NotificationStackScrollLayout

public class NotificationStackScrollLayout extends android.view.ViewGroup implements com.android.systemui.statusbar.policy.ScrollAdapter, ExpandableView.OnHeightChangedListener, ExpandHelper.Callback, SwipeHelper.Callback
A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final float
RUBBER_BAND_FACTOR_NORMAL
private static final float
RUBBER_BAND_FACTOR_AFTER_EXPAND
private static final float
RUBBER_BAND_FACTOR_ON_PANEL_EXPAND
private static final int
INVALID_POINTER
Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
private com.android.systemui.ExpandHelper
mExpandHelper
private com.android.systemui.SwipeHelper
mSwipeHelper
private boolean
mSwipingInProgress
private int
mCurrentStackHeight
private float
mLastSetStackHeight
mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set externally from {@link #setStackHeight}
private int
mOwnScrollY
private int
mMaxLayoutHeight
private android.view.VelocityTracker
mVelocityTracker
private android.widget.OverScroller
mScroller
private int
mTouchSlop
private int
mMinimumVelocity
private int
mMaximumVelocity
private int
mOverflingDistance
private float
mMaxOverScroll
private boolean
mIsBeingDragged
private int
mLastMotionY
private int
mDownX
private int
mActivePointerId
private boolean
mTouchIsClick
private float
mInitialTouchX
private float
mInitialTouchY
private int
mSidePaddings
private android.graphics.Paint
mDebugPaint
private int
mContentHeight
private int
mCollapsedSize
private int
mBottomStackSlowDownHeight
private int
mBottomStackPeekSize
private int
mPaddingBetweenElements
private int
mPaddingBetweenElementsDimmed
private int
mPaddingBetweenElementsNormal
private int
mTopPadding
private int
mCollapseSecondCardPadding
private StackScrollAlgorithm
mStackScrollAlgorithm
The algorithm which calculates the properties for our children
private StackScrollState
mCurrentStackScrollState
The current State this Layout is in
private AmbientState
mAmbientState
private ArrayList
mChildrenToAddAnimated
private ArrayList
mChildrenToRemoveAnimated
private ArrayList
mSnappedBackChildren
private ArrayList
mDragAnimPendingChildren
private ArrayList
mChildrenChangingPositions
private HashSet
mFromMoreCardAdditions
private ArrayList
mAnimationEvents
private ArrayList
mSwipedOutViews
private final StackStateAnimator
mStateAnimator
private boolean
mAnimationsEnabled
private boolean
mChangePositionInProgress
private float
mOverScrolledTopPixels
The raw amount of the overScroll on the top, which is not rubber-banded.
private float
mOverScrolledBottomPixels
The raw amount of the overScroll on the bottom, which is not rubber-banded.
private OnChildLocationsChangedListener
mListener
private OnOverscrollTopChangedListener
mOverscrollTopChangedListener
private ExpandableView.OnHeightChangedListener
mOnHeightChangedListener
private OnEmptySpaceClickListener
mOnEmptySpaceClickListener
private boolean
mNeedsAnimation
private boolean
mTopPaddingNeedsAnimation
private boolean
mDimmedNeedsAnimation
private boolean
mHideSensitiveNeedsAnimation
private boolean
mDarkNeedsAnimation
private int
mDarkAnimationOriginIndex
private boolean
mActivateNeedsAnimation
private boolean
mGoToFullShadeNeedsAnimation
private boolean
mIsExpanded
private boolean
mChildrenUpdateRequested
private com.android.systemui.statusbar.SpeedBumpView
mSpeedBumpView
private boolean
mIsExpansionChanging
private boolean
mExpandingNotification
private boolean
mExpandedInThisMotion
private boolean
mScrollingEnabled
private com.android.systemui.statusbar.DismissView
mDismissView
private com.android.systemui.statusbar.EmptyShadeView
mEmptyShadeView
private boolean
mDismissAllInProgress
private boolean
mScrolledToTopOnFirstDown
Was the scroller scrolled to the top when the down motion was observed?
private float
mMinTopOverScrollToEscape
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 int
mIntrinsicPadding
private int
mNotificationTopPadding
private float
mTopPaddingOverflow
private boolean
mDontReportNextOverScroll
private boolean
mRequestViewResizeAnimationOnLayout
private boolean
mNeedViewResizeAnimation
private boolean
mEverythingNeedsAnimation
private int
mMaxScrollAfterExpand
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 SwipeHelper.LongPressListener
mLongPressListener
private boolean
mOnlyScrollingInThisMotion
Should in this touch motion only be scrolling allowed? It's true when the scroller was animating.
private android.view.ViewGroup
mScrollView
private boolean
mInterceptDelegateEnabled
private boolean
mDelegateToScrollView
private boolean
mDisallowScrollingInThisMotion
private long
mGoToFullShadeDelay
private ViewTreeObserver.OnPreDrawListener
mChildrenUpdater
private com.android.systemui.statusbar.phone.PhoneStatusBar
mPhoneStatusBar
private int[]
mTempInt2
Constructors Summary
public NotificationStackScrollLayout(android.content.Context context)


       
        this(context, null);
    
public NotificationStackScrollLayout(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public NotificationStackScrollLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public NotificationStackScrollLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);
        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);

        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
        mSwipeHelper.setLongPressListener(mLongPressListener);
        initView(context);
        if (DEBUG) {
            setWillNotDraw(false);
            mDebugPaint = new Paint();
            mDebugPaint.setColor(0xffff0000);
            mDebugPaint.setStrokeWidth(2);
            mDebugPaint.setStyle(Paint.Style.STROKE);
        }
    
Methods Summary
private voidapplyCurrentState()

        mCurrentStackScrollState.apply();
        if (mListener != null) {
            mListener.onChildLocationsChanged(this);
        }
    
public booleancanChildBeDismissed(android.view.View v)

        final View veto = v.findViewById(R.id.veto);
        return (veto != null && veto.getVisibility() != View.GONE);
    
public booleancanChildBeExpanded(android.view.View v)

        return v instanceof ExpandableNotificationRow
                && ((ExpandableNotificationRow) v).isExpandable();
    
public voidcancelExpandHelper()

        mExpandHelper.cancel();
    
public voidchangeViewPosition(android.view.View child, int newIndex)
Change the position of child to a new location

param
child the view to change the position for
param
newIndex the new index

        int currentIndex = indexOfChild(child);
        if (child != null && child.getParent() == this && currentIndex != newIndex) {
            mChangePositionInProgress = true;
            removeView(child);
            addView(child, newIndex);
            mChangePositionInProgress = false;
            if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
                mChildrenChangingPositions.add(child);
                mNeedsAnimation = true;
            }
        }
    
private intclampPadding(int desiredPadding)

        return Math.max(desiredPadding, mIntrinsicPadding);
    
private voidclampScrollPosition()

        int scrollRange = getScrollRange();
        if (scrollRange < mOwnScrollY) {
            mOwnScrollY = scrollRange;
        }
    
public voidcomputeScroll()

        if (mScroller.computeScrollOffset()) {
            // This is called at drawing time by ViewGroup.
            int oldX = mScrollX;
            int oldY = mOwnScrollY;
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            if (oldX != x || oldY != y) {
                final int range = getScrollRange();
                if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
                    float currVelocity = mScroller.getCurrVelocity();
                    if (currVelocity >= mMinimumVelocity) {
                        mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
                    }
                }

                overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
                        0, (int) (mMaxOverScroll), false);
                onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
            }

            // Keep on drawing until the animation has finished.
            postInvalidateOnAnimation();
        }
    
private voidcustomScrollTo(int y)

        mOwnScrollY = y;
        updateChildren();
    
public voiddismissViewAnimated(android.view.View child, java.lang.Runnable endRunnable, int delay, long duration)

        child.setClipBounds(null);
        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration);
    
private voiddispatchDownEventToScroller(android.view.MotionEvent ev)

        MotionEvent downEvent = MotionEvent.obtain(ev);
        downEvent.setAction(MotionEvent.ACTION_DOWN);
        onScrollTouch(downEvent);
        downEvent.recycle();
    
private voidendDrag()

        setIsBeingDragged(false);

        recycleVelocityTracker();

        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
            setOverScrollAmount(0, true /* onTop */, true /* animate */);
        }
        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
            setOverScrollAmount(0, false /* onTop */, true /* animate */);
        }
    
public voidexpansionStateChanged(boolean isExpanding)

        mExpandingNotification = isExpanding;
        if (!mExpandedInThisMotion) {
            mMaxScrollAfterExpand = mOwnScrollY;
            mExpandedInThisMotion = true;
        }
    
private intfindDarkAnimationOriginIndex(android.graphics.PointF screenLocation)

        if (screenLocation == null || screenLocation.y < mTopPadding + mTopPaddingOverflow) {
            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
        }
        if (screenLocation.y > getBottomMostNotificationBottom()) {
            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
        }
        View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
        if (child != null) {
            return getNotGoneIndex(child);
        } else {
            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
        }
    
private voidfling(int velocityY)
Fling the scroll view

param
velocityY The initial velocity in the Y direction. Positive numbers mean that the finger/cursor is moving down the screen, which means we want to scroll towards the top.

        if (getChildCount() > 0) {
            int scrollRange = getScrollRange();

            float topAmount = getCurrentOverScrollAmount(true);
            float bottomAmount = getCurrentOverScrollAmount(false);
            if (velocityY < 0 && topAmount > 0) {
                mOwnScrollY -= (int) topAmount;
                mDontReportNextOverScroll = true;
                setOverScrollAmount(0, true, false);
                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
                        * mOverflingDistance + topAmount;
            } else if (velocityY > 0 && bottomAmount > 0) {
                mOwnScrollY += bottomAmount;
                setOverScrollAmount(0, false, false);
                mMaxOverScroll = Math.abs(velocityY) / 1000f
                        * getRubberBandFactor(false /* onTop */) * 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, scrollRange), 0, Integer.MAX_VALUE / 2);

            postInvalidateOnAnimation();
        }
    
private voidgenerateActivateEvent()

        if (mActivateNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
        }
        mActivateNeedsAnimation = false;
    
public voidgenerateAddAnimation(android.view.View child, boolean fromMoreCard)
Generate an animation for an added child view.

param
child The view to be added.
param
fromMoreCard Whether this add is coming from the "more" card on lockscreen.

        if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
            // Generate Animations
            mChildrenToAddAnimated.add(child);
            if (fromMoreCard) {
                mFromMoreCardAdditions.add(child);
            }
            mNeedsAnimation = true;
        }
    
private voidgenerateAnimateEverythingEvent()

        if (mEverythingNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
        }
        mEverythingNeedsAnimation = false;
    
private voidgenerateChildAdditionEvents()

        for (View child : mChildrenToAddAnimated) {
            if (mFromMoreCardAdditions.contains(child)) {
                mAnimationEvents.add(new AnimationEvent(child,
                        AnimationEvent.ANIMATION_TYPE_ADD,
                        StackStateAnimator.ANIMATION_DURATION_STANDARD));
            } else {
                mAnimationEvents.add(new AnimationEvent(child,
                        AnimationEvent.ANIMATION_TYPE_ADD));
            }
        }
        mChildrenToAddAnimated.clear();
        mFromMoreCardAdditions.clear();
    
private voidgenerateChildHierarchyEvents()

        generateChildRemovalEvents();
        generateChildAdditionEvents();
        generatePositionChangeEvents();
        generateSnapBackEvents();
        generateDragEvents();
        generateTopPaddingEvent();
        generateActivateEvent();
        generateDimmedEvent();
        generateHideSensitiveEvent();
        generateDarkEvent();
        generateGoToFullShadeEvent();
        generateViewResizeEvent();
        generateAnimateEverythingEvent();
        mNeedsAnimation = false;
    
private voidgenerateChildRemovalEvents()

        for (View child : mChildrenToRemoveAnimated) {
            boolean childWasSwipedOut = mSwipedOutViews.contains(child);
            int animationType = childWasSwipedOut
                    ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
                    : AnimationEvent.ANIMATION_TYPE_REMOVE;
            AnimationEvent event = new AnimationEvent(child, animationType);

            // we need to know the view after this one
            event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
            mAnimationEvents.add(event);
        }
        mSwipedOutViews.clear();
        mChildrenToRemoveAnimated.clear();
    
private voidgenerateDarkEvent()

        if (mDarkNeedsAnimation) {
            AnimationEvent ev = new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DARK);
            ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
            mAnimationEvents.add(ev);
        }
        mDarkNeedsAnimation = false;
    
private voidgenerateDimmedEvent()

        if (mDimmedNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
        }
        mDimmedNeedsAnimation = false;
    
private voidgenerateDragEvents()

        for (View child : mDragAnimPendingChildren) {
            mAnimationEvents.add(new AnimationEvent(child,
                    AnimationEvent.ANIMATION_TYPE_START_DRAG));
        }
        mDragAnimPendingChildren.clear();
    
private voidgenerateGoToFullShadeEvent()

        if (mGoToFullShadeNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
        }
        mGoToFullShadeNeedsAnimation = false;
    
private voidgenerateHideSensitiveEvent()

        if (mHideSensitiveNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
        }
        mHideSensitiveNeedsAnimation = false;
    
private voidgeneratePositionChangeEvents()

        for (View child : mChildrenChangingPositions) {
            mAnimationEvents.add(new AnimationEvent(child,
                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
        }
        mChildrenChangingPositions.clear();
    
private booleangenerateRemoveAnimation(android.view.View child)
Generate a remove animation for a child view.

param
child The view to generate the remove animation for.
return
Whether an animation was generated.

        if (mIsExpanded && mAnimationsEnabled) {
            if (!mChildrenToAddAnimated.contains(child)) {
                // Generate Animations
                mChildrenToRemoveAnimated.add(child);
                mNeedsAnimation = true;
                return true;
            } else {
                mChildrenToAddAnimated.remove(child);
                mFromMoreCardAdditions.remove(child);
                return false;
            }
        }
        return false;
    
private voidgenerateSnapBackEvents()

        for (View child : mSnappedBackChildren) {
            mAnimationEvents.add(new AnimationEvent(child,
                    AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
        }
        mSnappedBackChildren.clear();
    
private voidgenerateTopPaddingEvent()

        if (mTopPaddingNeedsAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
        }
        mTopPaddingNeedsAnimation = false;
    
private voidgenerateViewResizeEvent()

        if (mNeedViewResizeAnimation) {
            mAnimationEvents.add(
                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
        }
        mNeedViewResizeAnimation = false;
    
public com.android.systemui.statusbar.ActivatableNotificationViewgetActivatedChild()

        return mAmbientState.getActivatedChild();
    
public floatgetBottomMostNotificationBottom()

        final int count = getChildCount();
        float max = 0;
        for (int childIdx = 0; childIdx < count; childIdx++) {
            ExpandableView child = (ExpandableView) getChildAt(childIdx);
            if (child.getVisibility() == GONE) {
                continue;
            }
            float bottom = child.getTranslationY() + child.getActualHeight();
            if (bottom > max) {
                max = bottom;
            }
        }
        return max + getTranslationY();
    
public intgetBottomStackPeekSize()

        return mBottomStackPeekSize;
    
public android.view.ViewgetChildAtPosition(android.view.MotionEvent ev)

        return getChildAtPosition(ev.getX(), ev.getY());
    
public com.android.systemui.statusbar.ExpandableViewgetChildAtPosition(float touchX, float touchY)

        // find the view under the pointer, accounting for GONE views
        final int count = getChildCount();
        for (int childIdx = 0; childIdx < count; childIdx++) {
            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
            if (slidingChild.getVisibility() == GONE
                    || slidingChild instanceof StackScrollerDecorView
                    || slidingChild == mSpeedBumpView) {
                continue;
            }
            float childTop = slidingChild.getTranslationY();
            float top = childTop + slidingChild.getClipTopAmount();
            float bottom = childTop + slidingChild.getActualHeight();

            // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
            // camera affordance).
            int left = 0;
            int right = getWidth();

            if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                return slidingChild;
            }
        }
        return null;
    
public com.android.systemui.statusbar.ExpandableViewgetChildAtRawPosition(float touchX, float touchY)

        getLocationOnScreen(mTempInt2);
        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
    
public android.view.ViewgetChildContentView(android.view.View v)

        return v;
    
public intgetChildLocation(android.view.View child)
Returns the location the given child is currently rendered at.

param
child the child to get the location for
return
one of {@link ViewState}'s LOCATION_* constants

        ViewState childViewState = mCurrentStackScrollState.getViewStateForView(child);
        if (childViewState == null) {
            return ViewState.LOCATION_UNKNOWN;
        }
        if (childViewState.gone) {
            return ViewState.LOCATION_GONE;
        }
        return childViewState.location;
    
public com.android.systemui.statusbar.ExpandableViewgetClosestChildAtRawPosition(float touchX, float touchY)

        getLocationOnScreen(mTempInt2);
        float localTouchY = touchY - mTempInt2[1];

        ExpandableView closestChild = null;
        float minDist = Float.MAX_VALUE;

        // find the view closest to the location, accounting for GONE views
        final int count = getChildCount();
        for (int childIdx = 0; childIdx < count; childIdx++) {
            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
            if (slidingChild.getVisibility() == GONE
                    || slidingChild instanceof StackScrollerDecorView
                    || slidingChild == mSpeedBumpView) {
                continue;
            }
            float childTop = slidingChild.getTranslationY();
            float top = childTop + slidingChild.getClipTopAmount();
            float bottom = childTop + slidingChild.getActualHeight();

            float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
            if (dist < minDist) {
                closestChild = slidingChild;
                minDist = dist;
            }
        }
        return closestChild;
    
public intgetCollapseSecondCardPadding()

        return mCollapseSecondCardPadding;
    
public intgetContentHeight()

        return mContentHeight;
    
public floatgetCurrentOverScrollAmount(boolean top)

        return mAmbientState.getOverScrollAmount(top);
    
public floatgetCurrentOverScrolledPixels(boolean top)

        return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
    
public intgetDismissViewHeight()

        int height = mDismissView.getHeight() + mPaddingBetweenElementsNormal;

        // Hack: Accommodate for additional distance when we only have one notification and the
        // dismiss all button.
        if (getNotGoneChildCount() == 2 && getLastChildNotGone() == mDismissView
                && getFirstChildNotGone() instanceof ActivatableNotificationView) {
            height += mCollapseSecondCardPadding;
        }
        return height;
    
public intgetEmptyBottomMargin()

        int emptyMargin = mMaxLayoutHeight - mContentHeight - mBottomStackPeekSize;
        if (needsHeightAdaption()) {
            emptyMargin -= mBottomStackSlowDownHeight;
        } else {
            emptyMargin -= mCollapseSecondCardPadding;
        }
        return Math.max(emptyMargin, 0);
    
public intgetEmptyShadeViewHeight()

        return mEmptyShadeView.getHeight();
    
public floatgetFalsingThresholdFactor()

        return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
    
private android.view.ViewgetFirstChildBelowTranlsationY(float translationY)

return
The first child which has visibility unequal to GONE which is currently below the given translationY or equal to it.

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
                return child;
            }
        }
        return null;
    
private android.view.ViewgetFirstChildNotGone()

return
the first child which has visibility unequal to GONE

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                return child;
            }
        }
        return null;
    
public android.view.ViewgetHostView()

        return this;
    
private intgetIntrinsicHeight(android.view.View view)

        if (view instanceof ExpandableView) {
            ExpandableView expandableView = (ExpandableView) view;
            return expandableView.getIntrinsicHeight();
        }
        return view.getHeight();
    
public intgetIntrinsicPadding()

        return mIntrinsicPadding;
    
public intgetItemHeight()

        return mCollapsedSize;
    
public android.view.ViewgetLastChildNotGone()

return
the last child which has visibility unequal to GONE

        int childCount = getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                return child;
            }
        }
        return null;
    
private intgetLayoutHeight()
Get the current height of the view. This is at most the msize of the view given by a the layout but it can also be made smaller by setting {@link #mCurrentStackHeight}

return
either the layout height or the externally defined height, whichever is smaller

        return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
    
private intgetMaxExpandHeight(android.view.View view)

        if (view instanceof ExpandableNotificationRow) {
            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
            return row.getIntrinsicHeight();
        }
        return view.getHeight();
    
public intgetMinStackHeight()

        return mCollapsedSize + mBottomStackPeekSize + mCollapseSecondCardPadding;
    
public intgetNotGoneChildCount()

return
the number of children which have visibility unequal to GONE

        int childCount = getChildCount();
        int count = 0;
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                count++;
            }
        }
        if (mDismissView.willBeGone()) {
            count--;
        }
        if (mEmptyShadeView.willBeGone()) {
            count--;
        }
        return count;
    
private intgetNotGoneIndex(android.view.View child)

        int count = getChildCount();
        int notGoneIndex = 0;
        for (int i = 0; i < count; i++) {
            View v = getChildAt(i);
            if (child == v) {
                return notGoneIndex;
            }
            if (v.getVisibility() != View.GONE) {
                notGoneIndex++;
            }
        }
        return -1;
    
public intgetNotificationTopPadding()

        return mNotificationTopPadding;
    
public floatgetNotificationsTopY()

return
the y position of the first notification

        return mTopPadding + getTranslationY();
    
public intgetPeekHeight()

        return mIntrinsicPadding + mCollapsedSize + mBottomStackPeekSize
                + mCollapseSecondCardPadding;
    
private intgetPositionInLinearLayout(android.view.View requestedChild)

        int position = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child == requestedChild) {
                return position;
            }
            if (child.getVisibility() != View.GONE) {
                position += getIntrinsicHeight(child);
                if (i < getChildCount()-1) {
                    position += mPaddingBetweenElements;
                }
            }
        }
        return 0;
    
private floatgetRubberBandFactor(boolean onTop)

        if (!onTop) {
            return RUBBER_BAND_FACTOR_NORMAL;
        }
        if (mExpandedInThisMotion) {
            return RUBBER_BAND_FACTOR_AFTER_EXPAND;
        } else if (mIsExpansionChanging) {
            return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
        } else if (mScrolledToTopOnFirstDown) {
            return 1.0f;
        }
        return RUBBER_BAND_FACTOR_NORMAL;
    
private intgetScrollRange()

        int scrollRange = 0;
        ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
        if (firstChild != null) {
            int contentHeight = getContentHeight();
            int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
            scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
                    + mBottomStackSlowDownHeight);
            if (scrollRange > 0) {
                View lastChild = getLastChildNotGone();
                // We want to at least be able collapse the first item and not ending in a weird
                // end state.
                scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize);
            }
        }
        return scrollRange;
    
public intgetTopPadding()

        return mTopPadding;
    
public floatgetTopPaddingOverflow()

        return mTopPaddingOverflow;
    
public voidgoToFullShade(long delay)

        updateSpeedBump(true /* visibility */);
        mDismissView.setInvisible();
        mEmptyShadeView.setInvisible();
        mGoToFullShadeNeedsAnimation = true;
        mGoToFullShadeDelay = delay;
        mNeedsAnimation = true;
        requestChildrenUpdate();
    
private voidhandleEmptySpaceClick(android.view.MotionEvent ev)

        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:
                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
                    mTouchIsClick = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD && mTouchIsClick &&
                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
                }
                break;
        }
    
private voidinitDownStates(android.view.MotionEvent ev)

        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mExpandedInThisMotion = false;
            mOnlyScrollingInThisMotion = !mScroller.isFinished();
            mDisallowScrollingInThisMotion = false;
            mTouchIsClick = true;
            mInitialTouchX = ev.getX();
            mInitialTouchY = ev.getY();
        }
    
private voidinitOrResetVelocityTracker()

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        } else {
            mVelocityTracker.clear();
        }
    
private voidinitVelocityTrackerIfNotExists()

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    
private voidinitView(android.content.Context context)

        mScroller = new OverScroller(getContext());
        setFocusable(true);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setClipChildren(false);
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mOverflingDistance = configuration.getScaledOverflingDistance();

        mSidePaddings = context.getResources()
                .getDimensionPixelSize(R.dimen.notification_side_padding);
        mCollapsedSize = context.getResources()
                .getDimensionPixelSize(R.dimen.notification_min_height);
        mBottomStackPeekSize = context.getResources()
                .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
        mStackScrollAlgorithm = new StackScrollAlgorithm(context);
        mStackScrollAlgorithm.setDimmed(mAmbientState.isDimmed());
        mPaddingBetweenElementsDimmed = context.getResources()
                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
        mPaddingBetweenElementsNormal = context.getResources()
                .getDimensionPixelSize(R.dimen.notification_padding);
        updatePadding(mAmbientState.isDimmed());
        mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
                R.dimen.min_top_overscroll_to_qs);
        mNotificationTopPadding = getResources().getDimensionPixelSize(
                R.dimen.notifications_top_padding);
        mCollapseSecondCardPadding = getResources().getDimensionPixelSize(
                R.dimen.notification_collapse_second_card_padding);
    
public booleanisAddOrRemoveAnimationPending()

        return mNeedsAnimation
                && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
    
public booleanisAntiFalsingNeeded()

        return mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD;
    
private booleanisBelowLastNotification(float touchX, float touchY)

        ExpandableView lastChildNotGone = (ExpandableView) getLastChildNotGone();
        if (lastChildNotGone == null) {
            return touchY > mIntrinsicPadding;
        }
        if (lastChildNotGone != mDismissView && lastChildNotGone != mEmptyShadeView) {
            return touchY > lastChildNotGone.getY() + lastChildNotGone.getActualHeight();
        } else if (lastChildNotGone == mEmptyShadeView) {
            return touchY > mEmptyShadeView.getY();
        } else {
            float dismissY = mDismissView.getY();
            boolean belowDismissView = touchY > dismissY + mDismissView.getActualHeight();
            return belowDismissView || (touchY > dismissY
                    && mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
                    touchY - dismissY));
        }
    
private booleanisCurrentlyAnimating()

        return mStateAnimator.isRunning();
    
public booleanisDismissViewNotGone()

        return mDismissView.getVisibility() != View.GONE && !mDismissView.willBeGone();
    
public booleanisDismissViewVisible()

        return mDismissView.isVisible();
    
private booleanisInContentBounds(android.view.MotionEvent event)

return
Whether the specified motion event is actually happening over the content.

        return isInContentBounds(event.getY());
    
public booleanisInContentBounds(float y)

return
Whether a y coordinate is inside the content.

        return y < getHeight() - getEmptyBottomMargin();
    
private booleanisRubberbanded(boolean onTop)
Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is rubberbanded, false if it is technically an overscroll but rather a motion to expand the overscroll view (e.g. expand QS).

        return !onTop || mExpandedInThisMotion || mIsExpansionChanging
                || !mScrolledToTopOnFirstDown;
    
public booleanisScrolledToBottom()

        return mOwnScrollY >= getScrollRange();
    
public booleanisScrolledToTop()

        return mOwnScrollY == 0;
    
private booleanisScrollingEnabled()

        return mScrollingEnabled;
    
private booleanneedsHeightAdaption()

return
whether the height of the layout needs to be adapted, in order to ensure that the last child is not in the bottom stack.

        return getNotGoneChildCount() > 1;
    
private voidnotifyHeightChangeListener(com.android.systemui.statusbar.ExpandableView view)

        if (mOnHeightChangedListener != null) {
            mOnHeightChangedListener.onHeightChanged(view);
        }
    
private voidnotifyOverscrollTopListener(float amount, boolean isRubberbanded)

        mExpandHelper.onlyObserveMovements(amount > 1.0f);
        if (mDontReportNextOverScroll) {
            mDontReportNextOverScroll = false;
            return;
        }
        if (mOverscrollTopChangedListener != null) {
            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
        }
    
public voidonBeginDrag(android.view.View v)

        setSwipingInProgress(true);
        mAmbientState.onBeginDrag(v);
        if (mAnimationsEnabled) {
            mDragAnimPendingChildren.add(v);
            mNeedsAnimation = true;
        }
        requestChildrenUpdate();
    
public voidonChildAnimationFinished()

        requestChildrenUpdate();
    
public voidonChildDismissed(android.view.View v)

        if (mDismissAllInProgress) {
            return;
        }
        if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
        final View veto = v.findViewById(R.id.veto);
        if (veto != null && veto.getVisibility() != View.GONE) {
            veto.performClick();
        }
        setSwipingInProgress(false);
        if (mDragAnimPendingChildren.contains(v)) {
            // We start the swipe and finish it in the same frame, we don't want any animation
            // for the drag
            mDragAnimPendingChildren.remove(v);
        }
        mSwipedOutViews.add(v);
        mAmbientState.onDragFinished(v);
    
public voidonChildSnappedBack(android.view.View animView)

        mAmbientState.onDragFinished(animView);
        if (!mDragAnimPendingChildren.contains(animView)) {
            if (mAnimationsEnabled) {
                mSnappedBackChildren.add(animView);
                mNeedsAnimation = true;
            }
            requestChildrenUpdate();
        } else {
            // We start the swipe and snap back in the same frame, we don't want any animation
            mDragAnimPendingChildren.remove(animView);
        }
    
protected voidonConfigurationChanged(android.content.res.Configuration newConfig)

        super.onConfigurationChanged(newConfig);
        float densityScale = getResources().getDisplayMetrics().density;
        mSwipeHelper.setDensityScale(densityScale);
        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
        initView(getContext());
    
public voidonDragCancelled(android.view.View v)

        setSwipingInProgress(false);
    
protected voidonDraw(android.graphics.Canvas canvas)

        if (DEBUG) {
            int y = mCollapsedSize;
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
            y = (int) (getLayoutHeight() - mBottomStackPeekSize
                    - mBottomStackSlowDownHeight);
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
            y = (int) (getLayoutHeight() - mBottomStackPeekSize);
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
            y = (int) getLayoutHeight();
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
            y = getHeight() - getEmptyBottomMargin();
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
        }
    
public voidonExpansionStarted()

        mIsExpansionChanging = true;
        mStackScrollAlgorithm.onExpansionStarted(mCurrentStackScrollState);
    
public voidonExpansionStopped()

        mIsExpansionChanging = false;
        mStackScrollAlgorithm.onExpansionStopped();
        if (!mIsExpanded) {
            mOwnScrollY = 0;

            // lets make sure nothing is in the overlay anymore
            getOverlay().clear();
        }
    
public voidonGoToKeyguard()

        if (mIsExpanded && mAnimationsEnabled) {
            mEverythingNeedsAnimation = true;
            requestChildrenUpdate();
        }
    
public voidonHeightChanged(com.android.systemui.statusbar.ExpandableView view)

        updateContentHeight();
        updateScrollPositionOnExpandInBottom(view);
        clampScrollPosition();
        notifyHeightChangeListener(view);
        requestChildrenUpdate();
    
public booleanonInterceptTouchEvent(android.view.MotionEvent ev)

        if (mInterceptDelegateEnabled) {
            transformTouchEvent(ev, this, mScrollView);
            if (mScrollView.onInterceptTouchEvent(ev)) {
                mDelegateToScrollView = true;
                removeLongPressCallback();
                return true;
            }
            transformTouchEvent(ev, mScrollView, this);
        }
        initDownStates(ev);
        handleEmptySpaceClick(ev);
        boolean expandWantsIt = false;
        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
        }
        boolean scrollWantsIt = false;
        if (!mSwipingInProgress && !mExpandingNotification) {
            scrollWantsIt = onInterceptTouchEventScroll(ev);
        }
        boolean swipeWantsIt = false;
        if (!mIsBeingDragged
                && !mExpandingNotification
                && !mExpandedInThisMotion
                && !mOnlyScrollingInThisMotion) {
            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
        }
        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
    
private booleanonInterceptTouchEventScroll(android.view.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
         * scrolling there.
         */

        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */

                /*
                * Locally do absolute value. mLastMotionY is set to the y value
                * of the down event.
                */
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // If we don't have a valid id, the touch down wasn't on content.
                    break;
                }

                final int pointerIndex = ev.findPointerIndex(activePointerId);
                if (pointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + activePointerId
                            + " in onInterceptTouchEvent");
                    break;
                }

                final int y = (int) ev.getY(pointerIndex);
                final int x = (int) ev.getX(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                final int xDiff = Math.abs(x - mDownX);
                if (yDiff > mTouchSlop && yDiff > xDiff) {
                    setIsBeingDragged(true);
                    mLastMotionY = y;
                    mDownX = x;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                final int y = (int) ev.getY();
                if (getChildAtPosition(ev.getX(), y) == null) {
                    setIsBeingDragged(false);
                    recycleVelocityTracker();
                    break;
                }

                /*
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
                mLastMotionY = y;
                mDownX = (int) ev.getX();
                mActivePointerId = ev.getPointerId(0);
                mScrolledToTopOnFirstDown = isScrolledToTop();

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);
                /*
                * If being flinged and user touches the screen, initiate drag;
                * otherwise don't.  mScroller.isFinished should be false when
                * being flinged.
                */
                boolean isBeingDragged = !mScroller.isFinished();
                setIsBeingDragged(isBeingDragged);
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                /* Release the drag */
                setIsBeingDragged(false);
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
                    postInvalidateOnAnimation();
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }

        /*
        * The only time we want to intercept motion events is if we are in the
        * drag mode.
        */
        return mIsBeingDragged;
    
protected voidonLayout(boolean changed, int l, int t, int r, int b)


        // we layout all our children centered on the top
        float centerX = getWidth() / 2.0f;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float width = child.getMeasuredWidth();
            float height = child.getMeasuredHeight();
            child.layout((int) (centerX - width / 2.0f),
                    0,
                    (int) (centerX + width / 2.0f),
                    (int) height);
        }
        setMaxLayoutHeight(getHeight());
        updateContentHeight();
        clampScrollPosition();
        requestAnimationOnViewResize();
        requestChildrenUpdate();
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int childMeasureSpec = MeasureSpec.makeMeasureSpec(size - 2 * mSidePaddings, mode);
        measureChildren(childMeasureSpec, heightMeasureSpec);
    
private voidonOverScrollFling(boolean open, int initialVelocity)

        if (mOverscrollTopChangedListener != null) {
            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
        }
        mDontReportNextOverScroll = true;
        setOverScrollAmount(0.0f, true, false);
    
protected voidonOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)

        // Treat animating scrolls differently; see #computeScroll() for why.
        if (!mScroller.isFinished()) {
            final int oldX = mScrollX;
            final int oldY = mOwnScrollY;
            mScrollX = scrollX;
            mOwnScrollY = scrollY;
            if (clampedY) {
                springBack();
            } else {
                onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
                invalidateParentIfNeeded();
                updateChildren();
                float overScrollTop = getCurrentOverScrollAmount(true);
                if (mOwnScrollY < 0) {
                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
                } else {
                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
                }
            }
        } else {
            customScrollTo(scrollY);
            scrollTo(scrollX, mScrollY);
        }
    
public voidonReset(com.android.systemui.statusbar.ExpandableView view)

        if (mIsExpanded && mAnimationsEnabled) {
            mRequestViewResizeAnimationOnLayout = true;
        }
        mStackScrollAlgorithm.onReset(view);
        updateAnimationState(view);
    
private booleanonScrollTouch(android.view.MotionEvent ev)

        if (!isScrollingEnabled()) {
            return false;
        }
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                if (getChildCount() == 0 || !isInContentBounds(ev)) {
                    return false;
                }
                boolean isBeingDragged = !mScroller.isFinished();
                setIsBeingDragged(isBeingDragged);

                /*
                 * If being flinged and user touches, stop the fling. isFinished
                 * will be false if being flinged.
                 */
                if (!mScroller.isFinished()) {
                    mScroller.forceFinished(true);
                }

                // Remember where the motion event started
                mLastMotionY = (int) ev.getY();
                mDownX = (int) ev.getX();
                mActivePointerId = ev.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                if (activePointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
                    break;
                }

                final int y = (int) ev.getY(activePointerIndex);
                final int x = (int) ev.getX(activePointerIndex);
                int deltaY = mLastMotionY - y;
                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;
                    } else {
                        deltaY += mTouchSlop;
                    }
                }
                if (mIsBeingDragged) {
                    // Scroll to follow the motion event
                    mLastMotionY = y;
                    int range = getScrollRange();
                    if (mExpandedInThisMotion) {
                        range = Math.min(range, mMaxScrollAfterExpand);
                    }

                    float scrollAmount;
                    if (deltaY < 0) {
                        scrollAmount = overScrollDown(deltaY);
                    } else {
                        scrollAmount = overScrollUp(deltaY, range);
                    }

                    // Calling overScrollBy will call onOverScrolled, which
                    // calls onScrollChanged if applicable.
                    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);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);

                    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();
                }

                break;
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged && getChildCount() > 0) {
                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
                        postInvalidateOnAnimation();
                    }
                    mActivePointerId = INVALID_POINTER;
                    endDrag();
                }
                break;
            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 voidonSecondaryPointerUp(android.view.MotionEvent ev)

        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            // TODO: Make this decision more intelligent.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionY = (int) ev.getY(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    
public booleanonTouchEvent(android.view.MotionEvent ev)

        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
                || ev.getActionMasked()== MotionEvent.ACTION_UP;
        if (mDelegateToScrollView) {
            if (isCancelOrUp) {
                mDelegateToScrollView = false;
            }
            transformTouchEvent(ev, this, mScrollView);
            return mScrollView.onTouchEvent(ev);
        }
        handleEmptySpaceClick(ev);
        boolean expandWantsIt = false;
        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
            if (isCancelOrUp) {
                mExpandHelper.onlyObserveMovements(false);
            }
            boolean wasExpandingBefore = mExpandingNotification;
            expandWantsIt = mExpandHelper.onTouchEvent(ev);
            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
                    && !mDisallowScrollingInThisMotion) {
                dispatchDownEventToScroller(ev);
            }
        }
        boolean scrollerWantsIt = false;
        if (!mSwipingInProgress && !mExpandingNotification && !mDisallowScrollingInThisMotion) {
            scrollerWantsIt = onScrollTouch(ev);
        }
        boolean horizontalSwipeWantsIt = false;
        if (!mIsBeingDragged
                && !mExpandingNotification
                && !mExpandedInThisMotion
                && !mOnlyScrollingInThisMotion) {
            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
        }
        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
    
protected voidonViewAdded(android.view.View child)

        super.onViewAdded(child);
        mStackScrollAlgorithm.notifyChildrenChanged(this);
        ((ExpandableView) child).setOnHeightChangedListener(this);
        generateAddAnimation(child, false /* fromMoreCard */);
        updateAnimationState(child);
        if (canChildBeDismissed(child)) {
            // Make sure the dismissButton is visible and not in the animated state.
            // We need to do this to avoid a race where a clearable notification is added after the
            // dismiss animation is finished
            mDismissView.showClearButton();
        }
    
protected voidonViewRemoved(android.view.View child)

        super.onViewRemoved(child);
        mStackScrollAlgorithm.notifyChildrenChanged(this);
        if (mChangePositionInProgress) {
            // This is only a position change, don't do anything special
            return;
        }
        ((ExpandableView) child).setOnHeightChangedListener(null);
        mCurrentStackScrollState.removeViewStateForView(child);
        updateScrollStateForRemovedChild(child);
        boolean animationGenerated = generateRemoveAnimation(child);
        if (animationGenerated && !mSwipedOutViews.contains(child)) {
            // Add this view to an overlay in order to ensure that it will still be temporary
            // drawn when removed
            getOverlay().add(child);
        }
        updateAnimationState(false, child);

        // Make sure the clipRect we might have set is removed
        child.setClipBounds(null);
    
public voidonWindowFocusChanged(boolean hasWindowFocus)

        super.onWindowFocusChanged(hasWindowFocus);
        if (!hasWindowFocus) {
            removeLongPressCallback();
        }
    
protected booleanoverScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)


        int newScrollY = scrollY + deltaY;

        final int top = -maxOverScrollY;
        final int bottom = maxOverScrollY + scrollRangeY;

        boolean clampedY = false;
        if (newScrollY > bottom) {
            newScrollY = bottom;
            clampedY = true;
        } else if (newScrollY < top) {
            newScrollY = top;
            clampedY = true;
        }

        onOverScrolled(0, newScrollY, false, clampedY);

        return clampedY;
    
private floatoverScrollDown(int deltaY)
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.

        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 floatoverScrollUp(int deltaY, int range)
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.

        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) {
            if (!mExpandedInThisMotion) {
                float currentBottomPixels = getCurrentOverScrolledPixels(false);
                // We overScroll on the top
                setOverScrolledPixels(currentBottomPixels + newScrollY - range,
                        false /* onTop */,
                        false /* animate */);
            }
            mOwnScrollY = range;
            scrollAmount = 0.0f;
        }
        return scrollAmount;
    
private voidrecycleVelocityTracker()

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    
public voidremoveLongPressCallback()

        mSwipeHelper.removeLongPressCallback();
    
private voidrequestAnimationOnViewResize()

        if (mRequestViewResizeAnimationOnLayout && mIsExpanded && mAnimationsEnabled) {
            mNeedViewResizeAnimation = true;
            mNeedsAnimation = true;
        }
        mRequestViewResizeAnimationOnLayout = false;
    
private voidrequestChildrenUpdate()

        if (!mChildrenUpdateRequested) {
            getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
            mChildrenUpdateRequested = true;
            invalidate();
        }
    
public voidsetActivatedChild(com.android.systemui.statusbar.ActivatableNotificationView activatedChild)
See {@link AmbientState#setActivatedChild}.

        mAmbientState.setActivatedChild(activatedChild);
        if (mAnimationsEnabled) {
            mActivateNeedsAnimation = true;
            mNeedsAnimation =  true;
        }
        requestChildrenUpdate();
    
public voidsetAnimationsEnabled(boolean animationsEnabled)

        mAnimationsEnabled = animationsEnabled;
        updateNotificationAnimationStates();
    
public voidsetChildLocationsChangedListener(com.android.systemui.statusbar.stack.NotificationStackScrollLayout$OnChildLocationsChangedListener listener)

        mListener = listener;
    
public voidsetDark(boolean dark, boolean animate, android.graphics.PointF touchWakeUpScreenLocation)
See {@link AmbientState#setDark}.

        mAmbientState.setDark(dark);
        if (animate && mAnimationsEnabled) {
            mDarkNeedsAnimation = true;
            mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
            mNeedsAnimation =  true;
        }
        requestChildrenUpdate();
    
public voidsetDimmed(boolean dimmed, boolean animate)
See {@link AmbientState#setDimmed}.

        mStackScrollAlgorithm.setDimmed(dimmed);
        mAmbientState.setDimmed(dimmed);
        updatePadding(dimmed);
        if (animate && mAnimationsEnabled) {
            mDimmedNeedsAnimation = true;
            mNeedsAnimation =  true;
        }
        requestChildrenUpdate();
    
public voidsetDismissAllInProgress(boolean dismissAllInProgress)

        mDismissAllInProgress = dismissAllInProgress;
        mDismissView.setDismissAllInProgress(dismissAllInProgress);
    
public voidsetDismissView(com.android.systemui.statusbar.DismissView dismissView)

        mDismissView = dismissView;
        addView(mDismissView);
    
public voidsetEmptyShadeView(com.android.systemui.statusbar.EmptyShadeView emptyShadeView)

        mEmptyShadeView = emptyShadeView;
        addView(mEmptyShadeView);
    
public voidsetExpandingEnabled(boolean enable)

        mExpandHelper.setEnabled(enable);
    
public voidsetHideSensitive(boolean hideSensitive, boolean animate)

        if (hideSensitive != mAmbientState.isHideSensitive()) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                ExpandableView v = (ExpandableView) getChildAt(i);
                v.setHideSensitiveForIntrinsicHeight(hideSensitive);
            }
            mAmbientState.setHideSensitive(hideSensitive);
            if (animate && mAnimationsEnabled) {
                mHideSensitiveNeedsAnimation = true;
                mNeedsAnimation =  true;
            }
            requestChildrenUpdate();
        }
    
public voidsetInterceptDelegateEnabled(boolean interceptDelegateEnabled)

        mInterceptDelegateEnabled = interceptDelegateEnabled;
    
public voidsetIntrinsicPadding(int intrinsicPadding)

        mIntrinsicPadding = intrinsicPadding;
    
private voidsetIsBeingDragged(boolean isDragged)

        mIsBeingDragged = isDragged;
        if (isDragged) {
            requestDisallowInterceptTouchEvent(true);
            removeLongPressCallback();
        }
    
private voidsetIsExpanded(boolean isExpanded)

        boolean changed = isExpanded != mIsExpanded;
        mIsExpanded = isExpanded;
        mStackScrollAlgorithm.setIsExpanded(isExpanded);
        if (changed) {
            updateNotificationAnimationStates();
        }
    
public voidsetLongPressListener(SwipeHelper.LongPressListener listener)

        mSwipeHelper.setLongPressListener(listener);
        mLongPressListener = listener;
    
private voidsetMaxLayoutHeight(int maxLayoutHeight)

        mMaxLayoutHeight = maxLayoutHeight;
        updateAlgorithmHeightAndPadding();
    
public voidsetOnEmptySpaceClickListener(com.android.systemui.statusbar.stack.NotificationStackScrollLayout$OnEmptySpaceClickListener listener)

        mOnEmptySpaceClickListener = listener;
    
public voidsetOnHeightChangedListener(ExpandableView.OnHeightChangedListener mOnHeightChangedListener)

        this.mOnHeightChangedListener = mOnHeightChangedListener;
    
public voidsetOverScrollAmount(float amount, boolean onTop, boolean animate)
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.

        setOverScrollAmount(amount, onTop, animate, true);
    
public voidsetOverScrollAmount(float amount, boolean onTop, boolean animate, boolean cancelAnimators)
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.

        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
    
public voidsetOverScrollAmount(float amount, boolean onTop, boolean animate, boolean cancelAnimators, boolean isRubberbanded)
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.
param
isRubberbanded The value which will be passed to {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}

        if (cancelAnimators) {
            mStateAnimator.cancelOverScrollAnimators(onTop);
        }
        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
    
private voidsetOverScrollAmountInternal(float amount, boolean onTop, boolean animate, boolean isRubberbanded)

        amount = Math.max(0, amount);
        if (animate) {
            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
        } else {
            setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
            mAmbientState.setOverScrollAmount(amount, onTop);
            if (onTop) {
                notifyOverscrollTopListener(amount, isRubberbanded);
            }
            requestChildrenUpdate();
        }
    
public voidsetOverScrolledPixels(float numPixels, boolean onTop, boolean animate)
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.

        setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
    
private voidsetOverScrolledPixels(float amount, boolean onTop)

        if (onTop) {
            mOverScrolledTopPixels = amount;
        } else {
            mOverScrolledBottomPixels = amount;
        }
    
public voidsetOverscrollTopChangedListener(com.android.systemui.statusbar.stack.NotificationStackScrollLayout$OnOverscrollTopChangedListener overscrollTopChangedListener)

        mOverscrollTopChangedListener = overscrollTopChangedListener;
    
public voidsetPhoneStatusBar(com.android.systemui.statusbar.phone.PhoneStatusBar phoneStatusBar)

        this.mPhoneStatusBar = phoneStatusBar;
    
public voidsetScrollView(android.view.ViewGroup scrollView)

        mScrollView = scrollView;
    
public voidsetScrollingEnabled(boolean enable)

        mScrollingEnabled = enable;
    
public voidsetSpeedBumpView(com.android.systemui.statusbar.SpeedBumpView speedBumpView)

        mSpeedBumpView = speedBumpView;
        addView(speedBumpView);
    
public voidsetStackHeight(float height)
Update the height of the stack to a new height.

param
height the new height of the stack

        mLastSetStackHeight = height;
        setIsExpanded(height > 0.0f);
        int newStackHeight = (int) height;
        int minStackHeight = getMinStackHeight();
        int stackHeight;
        if (newStackHeight - mTopPadding - mTopPaddingOverflow >= minStackHeight
                || getNotGoneChildCount() == 0) {
            setTranslationY(mTopPaddingOverflow);
            stackHeight = newStackHeight;
        } else {

            // We did not reach the position yet where we actually start growing,
            // so we translate the stack upwards.
            int translationY = (newStackHeight - minStackHeight);
            // A slight parallax effect is introduced in order for the stack to catch up with
            // the top card.
            float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
                    / minStackHeight;
            partiallyThere = Math.max(0, partiallyThere);
            translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
                    mCollapseSecondCardPadding);
            setTranslationY(translationY - mTopPadding);
            stackHeight = (int) (height - (translationY - mTopPadding));
        }
        if (stackHeight != mCurrentStackHeight) {
            mCurrentStackHeight = stackHeight;
            updateAlgorithmHeightAndPadding();
            requestChildrenUpdate();
        }
    
private voidsetSwipingInProgress(boolean isSwiped)

        mSwipingInProgress = isSwiped;
        if(isSwiped) {
            requestDisallowInterceptTouchEvent(true);
        }
    
private voidsetTopPadding(int topPadding, boolean animate)

        if (mTopPadding != topPadding) {
            mTopPadding = topPadding;
            updateAlgorithmHeightAndPadding();
            updateContentHeight();
            if (animate && mAnimationsEnabled && mIsExpanded) {
                mTopPaddingNeedsAnimation = true;
                mNeedsAnimation =  true;
            }
            requestChildrenUpdate();
            notifyHeightChangeListener(null);
        }
    
public voidsetUserExpandedChild(android.view.View v, boolean userExpanded)

        if (v instanceof ExpandableNotificationRow) {
            ((ExpandableNotificationRow) v).setUserExpanded(userExpanded);
        }
    
public voidsetUserLockedChild(android.view.View v, boolean userLocked)

        if (v instanceof ExpandableNotificationRow) {
            ((ExpandableNotificationRow) v).setUserLocked(userLocked);
        }
        removeLongPressCallback();
        requestDisallowInterceptTouchEvent(true);
    
public booleanshouldDelayChildPressedState()

        return true;
    
private booleanshouldOverScrollFling(int initialVelocity)

return
Whether a fling performed on the top overscroll edge lead to the expanded overScroll view (i.e QS).

        float topOverScroll = getCurrentOverScrollAmount(true);
        return mScrolledToTopOnFirstDown
                && !mExpandedInThisMotion
                && topOverScroll > mMinTopOverScrollToEscape
                && initialVelocity > 0;
    
private voidspringBack()

        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;
                mDontReportNextOverScroll = true;
            } else {
                onTop = false;
                newAmount = mOwnScrollY - scrollRange;
                mOwnScrollY = scrollRange;
            }
            setOverScrollAmount(newAmount, onTop, false);
            setOverScrollAmount(0.0f, onTop, true);
            mScroller.forceFinished(true);
        }
    
private voidstartAnimationToState()

        if (mNeedsAnimation) {
            generateChildHierarchyEvents();
            mNeedsAnimation = false;
        }
        if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
            mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
                    mGoToFullShadeDelay);
            mAnimationEvents.clear();
        } else {
            applyCurrentState();
        }
        mGoToFullShadeDelay = 0;
    
private voidtransformTouchEvent(android.view.MotionEvent ev, android.view.View sourceView, android.view.View targetView)

        ev.offsetLocation(sourceView.getX(), sourceView.getY());
        ev.offsetLocation(-targetView.getX(), -targetView.getY());
    
private voidupdateAlgorithmHeightAndPadding()

        mStackScrollAlgorithm.setLayoutHeight(getLayoutHeight());
        mStackScrollAlgorithm.setTopPadding(mTopPadding);
    
private voidupdateAnimationState(android.view.View child)

        updateAnimationState(mAnimationsEnabled && mIsExpanded, child);
    
private voidupdateAnimationState(boolean running, android.view.View child)

        if (child instanceof ExpandableNotificationRow) {
            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
            row.setIconAnimationRunning(running);
        }
    
private voidupdateChildren()
Updates the children views according to the stack scroll algorithm. Call this whenever modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.

        mAmbientState.setScrollY(mOwnScrollY);
        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
        if (!isCurrentlyAnimating() && !mNeedsAnimation) {
            applyCurrentState();
        } else {
            startAnimationToState();
        }
    
private voidupdateContentHeight()

        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                if (height != 0) {
                    // add the padding before this element
                    height += mPaddingBetweenElements;
                }
                if (child instanceof ExpandableNotificationRow) {
                    ExpandableNotificationRow row = (ExpandableNotificationRow) child;
                    height += row.getIntrinsicHeight();
                } else if (child instanceof ExpandableView) {
                    ExpandableView expandableView = (ExpandableView) child;
                    height += expandableView.getActualHeight();
                }
            }
        }
        mContentHeight = height + mTopPadding;
    
public voidupdateDismissView(boolean visible)

        int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
        int newVisibility = visible ? VISIBLE : GONE;
        if (oldVisibility != newVisibility) {
            if (newVisibility != GONE) {
                if (mDismissView.willBeGone()) {
                    mDismissView.cancelAnimation();
                } else {
                    mDismissView.setInvisible();
                }
                mDismissView.setVisibility(newVisibility);
                mDismissView.setWillBeGone(false);
                updateContentHeight();
                notifyHeightChangeListener(mDismissView);
            } else {
                Runnable dimissHideFinishRunnable = new Runnable() {
                    @Override
                    public void run() {
                        mDismissView.setVisibility(GONE);
                        mDismissView.setWillBeGone(false);
                        updateContentHeight();
                        notifyHeightChangeListener(mDismissView);
                    }
                };
                if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
                    mDismissView.setWillBeGone(true);
                    mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
                } else {
                    dimissHideFinishRunnable.run();
                    mDismissView.showClearButton();
                }
            }
        }
    
public voidupdateEmptyShadeView(boolean visible)

        int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
        int newVisibility = visible ? VISIBLE : GONE;
        if (oldVisibility != newVisibility) {
            if (newVisibility != GONE) {
                if (mEmptyShadeView.willBeGone()) {
                    mEmptyShadeView.cancelAnimation();
                } else {
                    mEmptyShadeView.setInvisible();
                }
                mEmptyShadeView.setVisibility(newVisibility);
                mEmptyShadeView.setWillBeGone(false);
                updateContentHeight();
                notifyHeightChangeListener(mDismissView);
            } else {
                Runnable onFinishedRunnable = new Runnable() {
                    @Override
                    public void run() {
                        mEmptyShadeView.setVisibility(GONE);
                        mEmptyShadeView.setWillBeGone(false);
                        updateContentHeight();
                        notifyHeightChangeListener(mDismissView);
                    }
                };
                if (mAnimationsEnabled) {
                    mEmptyShadeView.setWillBeGone(true);
                    mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
                } else {
                    mEmptyShadeView.setInvisible();
                    onFinishedRunnable.run();
                }
            }
        }
    
public voidupdateIsSmallScreen(int qsMinHeight)

param
qsMinHeight The minimum height of the quick settings including padding See {@link StackScrollAlgorithm#updateIsSmallScreen}.

        mStackScrollAlgorithm.updateIsSmallScreen(mMaxLayoutHeight - qsMinHeight);
    
private voidupdateNotificationAnimationStates()

        boolean running = mIsExpanded && mAnimationsEnabled;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            updateAnimationState(running, child);
        }
    
private voidupdatePadding(boolean dimmed)

        mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed()
                ? mPaddingBetweenElementsDimmed
                : mPaddingBetweenElementsNormal;
        mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
        updateContentHeight();
        notifyHeightChangeListener(null);
    
private voidupdateScrollPositionOnExpandInBottom(com.android.systemui.statusbar.ExpandableView view)

        if (view instanceof ExpandableNotificationRow) {
            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
            if (row.isUserLocked()) {
                // We are actually expanding this view
                float endPosition = row.getTranslationY() + row.getActualHeight();
                int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
                        mBottomStackSlowDownHeight;
                if (endPosition > stackEnd) {
                    mOwnScrollY += endPosition - stackEnd;
                    mDisallowScrollingInThisMotion = true;
                }
            }
        }
    
private voidupdateScrollStateForRemovedChild(android.view.View removedChild)
Updates the scroll position when a child was removed

param
removedChild the removed child

        int startingPosition = getPositionInLinearLayout(removedChild);
        int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
        int endPosition = startingPosition + childHeight;
        if (endPosition <= mOwnScrollY) {
            // This child is fully scrolled of the top, so we have to deduct its height from the
            // scrollPosition
            mOwnScrollY -= childHeight;
        } else if (startingPosition < mOwnScrollY) {
            // This child is currently being scrolled into, set the scroll position to the start of
            // this child
            mOwnScrollY = startingPosition;
        }
    
private voidupdateSpeedBump(boolean visible)

        boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
        if (visible != notGoneBefore) {
            int newVisibility = visible ? VISIBLE : GONE;
            mSpeedBumpView.setVisibility(newVisibility);
            if (visible) {
                // Make invisible to ensure that the appear animation is played.
                mSpeedBumpView.setInvisible();
            } else {
                // TODO: This doesn't really work, because the view is already set to GONE above.
                generateRemoveAnimation(mSpeedBumpView);
            }
        }
    
public voidupdateSpeedBumpIndex(int newIndex)

        int currentIndex = indexOfChild(mSpeedBumpView);

        // If we are currently layouted before the new speed bump index, we have to decrease it.
        boolean validIndex = newIndex > 0;
        if (newIndex > getChildCount() - 1) {
            validIndex = false;
            newIndex = -1;
        }
        if (validIndex && currentIndex != newIndex) {
            changeViewPosition(mSpeedBumpView, newIndex);
        }
        updateSpeedBump(validIndex);
        mAmbientState.setSpeedBumpIndex(newIndex);
    
public booleanupdateSwipeProgress(android.view.View animView, boolean dismissable, float swipeProgress)

        return false;
    
public voidupdateTopPadding(float qsHeight, int scrollY, boolean animate, boolean ignoreIntrinsicPadding)
Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into account.

param
qsHeight the top padding imposed by the quick settings panel
param
scrollY how much the notifications are scrolled inside the QS/notifications scroll container
param
animate whether to animate the change
param
ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and {@code qsHeight} is the final top padding

        float start = qsHeight - scrollY + mNotificationTopPadding;
        float stackHeight = getHeight() - start;
        int minStackHeight = getMinStackHeight();
        if (stackHeight <= minStackHeight) {
            float overflow = minStackHeight - stackHeight;
            stackHeight = minStackHeight;
            start = getHeight() - stackHeight;
            mTopPaddingOverflow = overflow;
        } else {
            mTopPaddingOverflow = 0;
        }
        setTopPadding(ignoreIntrinsicPadding ? (int) start : clampPadding((int) start),
                animate);
        setStackHeight(mLastSetStackHeight);