StackScrollAlgorithm.java revision 2ba5f1f4e328dc2da47363a9feda75d5fdb9a4af
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.stack;
18
19import android.content.Context;
20import android.util.Log;
21import android.view.View;
22import android.view.ViewGroup;
23
24import com.android.systemui.R;
25import com.android.systemui.statusbar.ExpandableNotificationRow;
26
27import java.util.ArrayList;
28
29/**
30 * The Algorithm of the {@link com.android.systemui.statusbar.stack
31 * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
32 * .stack.StackScrollState}
33 */
34public class StackScrollAlgorithm {
35
36    private static final String LOG_TAG = "StackScrollAlgorithm";
37
38    private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
39    private static final int MAX_ITEMS_IN_TOP_STACK = 3;
40
41    private int mPaddingBetweenElements;
42    private int mCollapsedSize;
43    private int mTopStackPeekSize;
44    private int mBottomStackPeekSize;
45    private int mZDistanceBetweenElements;
46    private int mZBasicHeight;
47
48    private StackIndentationFunctor mTopStackIndentationFunctor;
49    private StackIndentationFunctor mBottomStackIndentationFunctor;
50
51    private int mLayoutHeight;
52    private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
53    private boolean mIsExpansionChanging;
54    private int mFirstChildMaxHeight;
55    private boolean mIsExpanded;
56    private View mFirstChildWhileExpanding;
57    private boolean mExpandedOnStart;
58    private int mTopStackTotalSize;
59
60    public StackScrollAlgorithm(Context context) {
61        initConstants(context);
62    }
63
64    private void initConstants(Context context) {
65
66        // currently the padding is in the elements themself
67        mPaddingBetweenElements = 0;
68        mCollapsedSize = context.getResources()
69                .getDimensionPixelSize(R.dimen.notification_row_min_height);
70        mTopStackPeekSize = context.getResources()
71                .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
72        mBottomStackPeekSize = context.getResources()
73                .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
74        mZDistanceBetweenElements = context.getResources()
75                .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
76        mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
77        mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
78        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
79                MAX_ITEMS_IN_TOP_STACK,
80                mTopStackPeekSize,
81                mTopStackTotalSize,
82                0.5f);
83        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
84                MAX_ITEMS_IN_BOTTOM_STACK,
85                mBottomStackPeekSize,
86                mCollapsedSize + mPaddingBetweenElements + mBottomStackPeekSize,
87                0.5f);
88    }
89
90
91    public void getStackScrollState(StackScrollState resultState) {
92        // The state of the local variables are saved in an algorithmState to easily subdivide it
93        // into multiple phases.
94        StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
95
96        // First we reset the view states to their default values.
97        resultState.resetViewStates();
98
99        algorithmState.itemsInTopStack = 0.0f;
100        algorithmState.partialInTop = 0.0f;
101        algorithmState.lastTopStackIndex = 0;
102        algorithmState.scrolledPixelsTop = 0;
103        algorithmState.itemsInBottomStack = 0.0f;
104        algorithmState.partialInBottom = 0.0f;
105
106        updateVisibleChildren(resultState, algorithmState);
107
108        algorithmState.scrollY = getAlgorithmScrollPosition(resultState, algorithmState);
109
110        // Phase 1:
111        findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
112
113        // Phase 2:
114        updatePositionsForState(resultState, algorithmState);
115
116        // Phase 3:
117        updateZValuesForState(resultState, algorithmState);
118    }
119
120    /**
121     * Calculates the scroll offset of the algorithm, based on the resultState.
122     *
123     * @param resultState the state to base the calculation on
124     * @param algorithmState The state in which the current pass of the algorithm is currently in
125     * @return the scroll offset used for the algorithm
126     */
127    private int getAlgorithmScrollPosition(StackScrollState resultState,
128            StackScrollAlgorithmState algorithmState) {
129
130        int resultScroll = resultState.getScrollY() + mCollapsedSize;
131
132        // If the first child was collapsed in an earlier pass, we have to decrease the scroll
133        // position to get into the same state again.
134        if (algorithmState.visibleChildren.size() > 0) {
135            View firstView = algorithmState.visibleChildren.get(0);
136            if (firstView instanceof ExpandableNotificationRow) {
137                ExpandableNotificationRow firstRow = (ExpandableNotificationRow) firstView;
138                if (firstRow.isUserLocked()) {
139                    // User is currently modifying this height.
140                    return resultScroll;
141                }
142                int scrolledInAmount = 0;
143                // If the child size was not decreased due to scrolling, we don't substract it,
144                if (!mIsExpansionChanging) {
145                    scrolledInAmount = firstRow.getExpandPotential();
146                } else if (mExpandedOnStart && mFirstChildWhileExpanding == firstView) {
147                    scrolledInAmount = firstRow.getMaximumAllowedExpandHeight() -
148                            mFirstChildMaxHeight;
149                }
150                resultScroll -= scrolledInAmount;
151            }
152        }
153        return resultScroll;
154    }
155
156    /**
157     * Update the visible children on the state.
158     */
159    private void updateVisibleChildren(StackScrollState resultState,
160            StackScrollAlgorithmState state) {
161        ViewGroup hostView = resultState.getHostView();
162        int childCount = hostView.getChildCount();
163        state.visibleChildren.clear();
164        state.visibleChildren.ensureCapacity(childCount);
165        for (int i = 0; i < childCount; i++) {
166            View v = hostView.getChildAt(i);
167            if (v.getVisibility() != View.GONE) {
168                state.visibleChildren.add(v);
169            }
170        }
171    }
172
173    /**
174     * Determine the positions for the views. This is the main part of the algorithm.
175     *
176     * @param resultState The result state to update if a change to the properties of a child occurs
177     * @param algorithmState The state in which the current pass of the algorithm is currently in
178     *                       and which will be updated
179     */
180    private void updatePositionsForState(StackScrollState resultState,
181            StackScrollAlgorithmState algorithmState) {
182
183        // The starting position of the bottom stack peek
184        float bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
185
186        // The position where the bottom stack starts.
187        float bottomStackStart = bottomPeekStart - mCollapsedSize;
188
189        // The y coordinate of the current child.
190        float currentYPosition = 0.0f;
191
192        // How far in is the element currently transitioning into the bottom stack.
193        float yPositionInScrollView = 0.0f;
194
195        int childCount = algorithmState.visibleChildren.size();
196        int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
197        for (int i = 0; i < childCount; i++) {
198            View child = algorithmState.visibleChildren.get(i);
199            StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
200            childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
201            int childHeight = child.getHeight();
202            float yPositionInScrollViewAfterElement = yPositionInScrollView
203                    + childHeight
204                    + mPaddingBetweenElements;
205            float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize;
206
207            if (i == algorithmState.lastTopStackIndex + 1) {
208                // Normally the position of this child is the position in the regular scrollview,
209                // but if the two stacks are very close to each other,
210                // then have have to push it even more upwards to the position of the bottom
211                // stack start.
212                currentYPosition = Math.min(scrollOffset, bottomStackStart);
213            }
214            childViewState.yTranslation = currentYPosition;
215
216            // The y position after this element
217            float nextYPosition = currentYPosition + childHeight +
218                    mPaddingBetweenElements;
219
220            if (i <= algorithmState.lastTopStackIndex) {
221                // Case 1:
222                // We are in the top Stack
223                updateStateForTopStackChild(algorithmState,
224                        numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
225                clampYTranslation(childViewState, childHeight);
226                // check if we are overlapping with the bottom stack
227                if (childViewState.yTranslation + childHeight + mPaddingBetweenElements
228                        >= bottomStackStart && !mIsExpansionChanging) {
229                    // TODO: handle overlapping sizes with end stack better
230                    // we just collapse this element
231                    childViewState.height = mCollapsedSize;
232                }
233            } else if (nextYPosition >= bottomStackStart) {
234                // Case 2:
235                // We are in the bottom stack.
236                if (currentYPosition >= bottomStackStart) {
237                    // According to the regular scroll view we are fully translated out of the
238                    // bottom of the screen so we are fully in the bottom stack
239                    updateStateForChildFullyInBottomStack(algorithmState,
240                            bottomStackStart, childViewState, childHeight);
241                } else {
242                    // According to the regular scroll view we are currently translating out of /
243                    // into the bottom of the screen
244                    updateStateForChildTransitioningInBottom(algorithmState,
245                            bottomStackStart, bottomPeekStart, currentYPosition,
246                            childViewState, childHeight);
247                }
248            } else {
249                // Case 3:
250                // We are in the regular scroll area.
251                childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
252                clampYTranslation(childViewState, childHeight);
253            }
254
255            // The first card is always rendered.
256            if (i == 0) {
257                childViewState.alpha = 1.0f;
258                childViewState.yTranslation = 0;
259                childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
260            }
261            if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
262                Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
263            }
264            currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
265            yPositionInScrollView = yPositionInScrollViewAfterElement;
266        }
267    }
268
269    /**
270     * Clamp the yTranslation both up and down to valid positions.
271     *
272     * @param childViewState the view state of the child
273     * @param childHeight the height of this child
274     */
275    private void clampYTranslation(StackScrollState.ViewState childViewState, int childHeight) {
276        clampPositionToBottomStackStart(childViewState, childHeight);
277        clampPositionToTopStackEnd(childViewState, childHeight);
278    }
279
280    /**
281     * Clamp the yTranslation of the child down such that its end is at most on the beginning of
282     * the bottom stack.
283     *
284     * @param childViewState the view state of the child
285     * @param childHeight the height of this child
286     */
287    private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState,
288            int childHeight) {
289        childViewState.yTranslation = Math.min(childViewState.yTranslation,
290                mLayoutHeight - mBottomStackPeekSize - childHeight);
291    }
292
293    /**
294     * Clamp the yTranslation of the child up such that its end is at lest on the end of the top
295     * stack.
296     *
297     * @param childViewState the view state of the child
298     * @param childHeight the height of this child
299     */
300    private void clampPositionToTopStackEnd(StackScrollState.ViewState childViewState,
301            int childHeight) {
302        childViewState.yTranslation = Math.max(childViewState.yTranslation,
303                mCollapsedSize - childHeight);
304    }
305
306    private int getMaxAllowedChildHeight(View child) {
307        if (child instanceof ExpandableNotificationRow) {
308            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
309            return row.getMaximumAllowedExpandHeight();
310        }
311        return child.getHeight();
312    }
313
314    private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
315            float transitioningPositionStart, float bottomPeakStart, float currentYPosition,
316            StackScrollState.ViewState childViewState, int childHeight) {
317
318        // This is the transitioning element on top of bottom stack, calculate how far we are in.
319        algorithmState.partialInBottom = 1.0f - (
320                (transitioningPositionStart - currentYPosition) / (childHeight +
321                        mPaddingBetweenElements));
322
323        // the offset starting at the transitionPosition of the bottom stack
324        float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
325        algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
326        childViewState.yTranslation = transitioningPositionStart + offset - childHeight;
327
328        // We want at least to be at the end of the top stack when collapsing
329        clampPositionToTopStackEnd(childViewState, childHeight);
330        childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
331    }
332
333    private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
334            float transitioningPositionStart, StackScrollState.ViewState childViewState,
335            int childHeight) {
336
337        float currentYPosition;
338        algorithmState.itemsInBottomStack += 1.0f;
339        if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
340            // We are visually entering the bottom stack
341            currentYPosition = transitioningPositionStart
342                    + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack);
343            childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING;
344        } else {
345            // we are fully inside the stack
346            if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
347                childViewState.alpha = 0.0f;
348            } else if (algorithmState.itemsInBottomStack
349                    > MAX_ITEMS_IN_BOTTOM_STACK + 1) {
350                childViewState.alpha = 1.0f - algorithmState.partialInBottom;
351            }
352            childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
353            currentYPosition = mLayoutHeight;
354        }
355        childViewState.yTranslation = currentYPosition - childHeight;
356        clampPositionToTopStackEnd(childViewState, childHeight);
357    }
358
359    private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
360            int numberOfElementsCompletelyIn, int i, int childHeight,
361            StackScrollState.ViewState childViewState, float scrollOffset) {
362
363
364        // First we calculate the index relative to the current stack window of size at most
365        // {@link #MAX_ITEMS_IN_TOP_STACK}
366        int paddedIndex = i - 1
367                - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0);
368        if (paddedIndex >= 0) {
369
370            // We are currently visually entering the top stack
371            float distanceToStack = childHeight - algorithmState.scrolledPixelsTop;
372            if (i == algorithmState.lastTopStackIndex && distanceToStack > mTopStackTotalSize) {
373
374                // Child is currently translating into stack but not yet inside slow down zone.
375                // Handle it like the regular scrollview.
376                childViewState.yTranslation = scrollOffset;
377            } else {
378                // Apply stacking logic.
379                float numItemsBefore;
380                if (i == algorithmState.lastTopStackIndex) {
381                    numItemsBefore = 1.0f - (distanceToStack / mTopStackTotalSize);
382                } else {
383                    numItemsBefore = algorithmState.itemsInTopStack - i;
384                }
385                // The end position of the current child
386                float currentChildEndY = mCollapsedSize + mTopStackTotalSize -
387                        mTopStackIndentationFunctor.getValue(numItemsBefore);
388                childViewState.yTranslation = currentChildEndY - childHeight;
389            }
390            childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
391        } else {
392            if (paddedIndex == -1) {
393                childViewState.alpha = 1.0f - algorithmState.partialInTop;
394            } else {
395                // We are hidden behind the top card and faded out, so we can hide ourselves.
396                childViewState.alpha = 0.0f;
397            }
398            childViewState.yTranslation = mCollapsedSize - childHeight;
399            childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
400        }
401
402
403    }
404
405    /**
406     * Find the number of items in the top stack and update the result state if needed.
407     *
408     * @param resultState The result state to update if a height change of an child occurs
409     * @param algorithmState The state in which the current pass of the algorithm is currently in
410     *                       and which will be updated
411     */
412    private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
413            StackScrollAlgorithmState algorithmState) {
414
415        // The y Position if the element would be in a regular scrollView
416        float yPositionInScrollView = 0.0f;
417        int childCount = algorithmState.visibleChildren.size();
418
419        // find the number of elements in the top stack.
420        for (int i = 0; i < childCount; i++) {
421            View child = algorithmState.visibleChildren.get(i);
422            StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
423            int childHeight = child.getHeight();
424            float yPositionInScrollViewAfterElement = yPositionInScrollView
425                    + childHeight
426                    + mPaddingBetweenElements;
427            if (yPositionInScrollView < algorithmState.scrollY) {
428                if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
429
430                    // The starting position of the bottom stack peek
431                    int bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
432                    // Collapse and expand the first child while the shade is being expanded
433                    float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
434                            ? mFirstChildMaxHeight
435                            : childHeight;
436                    childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
437                            mCollapsedSize);
438                    algorithmState.itemsInTopStack = 1.0f;
439
440                } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
441                    // According to the regular scroll view we are fully off screen
442                    algorithmState.itemsInTopStack += 1.0f;
443                    if (i == 0) {
444                        childViewState.height = mCollapsedSize;
445                    }
446                } else {
447                    // According to the regular scroll view we are partially off screen
448                    // If it is expanded we have to collapse it to a new size
449                    float newSize = yPositionInScrollViewAfterElement
450                            - mPaddingBetweenElements
451                            - algorithmState.scrollY;
452
453                    if (i == 0) {
454                        newSize += mCollapsedSize;
455                    }
456
457                    // How much did we scroll into this child
458                    algorithmState.scrolledPixelsTop = childHeight - newSize;
459                    algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
460                            + mPaddingBetweenElements);
461
462                    // Our element can be expanded, so this can get negative
463                    algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
464                    algorithmState.itemsInTopStack += algorithmState.partialInTop;
465                    newSize = Math.max(mCollapsedSize, newSize);
466                    if (i == 0) {
467                        childViewState.height = (int) newSize;
468                    }
469                    algorithmState.lastTopStackIndex = i;
470                    break;
471                }
472            } else {
473                algorithmState.lastTopStackIndex = i - 1;
474                // We are already past the stack so we can end the loop
475                break;
476            }
477            yPositionInScrollView = yPositionInScrollViewAfterElement;
478        }
479    }
480
481    /**
482     * Calculate the Z positions for all children based on the number of items in both stacks and
483     * save it in the resultState
484     *
485     * @param resultState The result state to update the zTranslation values
486     * @param algorithmState The state in which the current pass of the algorithm is currently in
487     */
488    private void updateZValuesForState(StackScrollState resultState,
489            StackScrollAlgorithmState algorithmState) {
490        int childCount = algorithmState.visibleChildren.size();
491        for (int i = 0; i < childCount; i++) {
492            View child = algorithmState.visibleChildren.get(i);
493            StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
494            if (i < algorithmState.itemsInTopStack) {
495                float stackIndex = algorithmState.itemsInTopStack - i;
496                stackIndex = Math.min(stackIndex, MAX_ITEMS_IN_TOP_STACK + 2);
497                childViewState.zTranslation = mZBasicHeight
498                        + stackIndex * mZDistanceBetweenElements;
499            } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
500                float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack);
501                float translationZ = mZBasicHeight
502                        - numItemsAbove * mZDistanceBetweenElements;
503                childViewState.zTranslation = translationZ;
504            } else {
505                childViewState.zTranslation = mZBasicHeight;
506            }
507        }
508    }
509
510    public int getLayoutHeight() {
511        return mLayoutHeight;
512    }
513
514    public void setLayoutHeight(int layoutHeight) {
515        this.mLayoutHeight = layoutHeight;
516    }
517
518    public void onExpansionStarted(StackScrollState currentState) {
519        mIsExpansionChanging = true;
520        mExpandedOnStart = mIsExpanded;
521        ViewGroup hostView = currentState.getHostView();
522        updateFirstChildHeightWhileExpanding(hostView);
523    }
524
525    private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) {
526        mFirstChildWhileExpanding = findFirstVisibleChild(hostView);
527        if (mFirstChildWhileExpanding != null) {
528            if (mExpandedOnStart) {
529
530                // We are collapsing the shade, so the first child can get as most as high as the
531                // current height.
532                mFirstChildMaxHeight = mFirstChildWhileExpanding.getHeight();
533            } else {
534
535                // We are expanding the shade, expand it to its full height.
536                if (mFirstChildWhileExpanding.getWidth() == 0) {
537
538                    // This child was not layouted yet, wait for a layout pass
539                    mFirstChildWhileExpanding
540                            .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
541                                @Override
542                                public void onLayoutChange(View v, int left, int top, int right,
543                                        int bottom, int oldLeft, int oldTop, int oldRight,
544                                        int oldBottom) {
545                                    if (mFirstChildWhileExpanding != null) {
546                                        mFirstChildMaxHeight = getMaxAllowedChildHeight(
547                                                mFirstChildWhileExpanding);
548                                    } else {
549                                        mFirstChildMaxHeight = 0;
550                                    }
551                                    v.removeOnLayoutChangeListener(this);
552                                }
553                            });
554                } else {
555                    mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
556                }
557            }
558        } else {
559            mFirstChildMaxHeight = 0;
560        }
561    }
562
563    private View findFirstVisibleChild(ViewGroup container) {
564        int childCount = container.getChildCount();
565        for (int i = 0; i < childCount; i++) {
566            View child = container.getChildAt(i);
567            if (child.getVisibility() != View.GONE) {
568                return child;
569            }
570        }
571        return null;
572    }
573
574    public void onExpansionStopped() {
575        mIsExpansionChanging = false;
576        mFirstChildWhileExpanding = null;
577    }
578
579    public void setIsExpanded(boolean isExpanded) {
580        this.mIsExpanded = isExpanded;
581    }
582
583    public void notifyChildrenChanged(ViewGroup hostView) {
584        if (mIsExpansionChanging) {
585            updateFirstChildHeightWhileExpanding(hostView);
586        }
587    }
588
589    class StackScrollAlgorithmState {
590
591        /**
592         * The scroll position of the algorithm
593         */
594        public int scrollY;
595
596        /**
597         *  The quantity of items which are in the top stack.
598         */
599        public float itemsInTopStack;
600
601        /**
602         * how far in is the element currently transitioning into the top stack
603         */
604        public float partialInTop;
605
606        /**
607         * The number of pixels the last child in the top stack has scrolled in to the stack
608         */
609        public float scrolledPixelsTop;
610
611        /**
612         * The last item index which is in the top stack.
613         */
614        public int lastTopStackIndex;
615
616        /**
617         * The quantity of items which are in the bottom stack.
618         */
619        public float itemsInBottomStack;
620
621        /**
622         * how far in is the element currently transitioning into the bottom stack
623         */
624        public float partialInBottom;
625
626        /**
627         * The children from the host view which are not gone.
628         */
629        public final ArrayList<View> visibleChildren = new ArrayList<View>();
630    }
631
632}
633