blob: 431f6fe98f682edc85d18c32ffa9d49e3efb59bc [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
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;
Christoph Studer6e3eceb2014-04-01 18:40:27 +020020import android.util.Log;
Selim Cinek67b22602014-03-10 15:40:16 +010021import android.view.View;
22import android.view.ViewGroup;
23import com.android.systemui.R;
Selim Cinek1685e632014-04-08 02:27:49 +020024import com.android.systemui.statusbar.ExpandableNotificationRow;
Selim Cinek67b22602014-03-10 15:40:16 +010025
Jorim Jaggid4a57442014-04-10 02:45:55 +020026import java.util.ArrayList;
27
Selim Cinek67b22602014-03-10 15:40:16 +010028/**
29 * The Algorithm of the {@link com.android.systemui.statusbar.stack
30 * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
31 * .stack.StackScrollState}
32 */
33public class StackScrollAlgorithm {
34
Christoph Studer6e3eceb2014-04-01 18:40:27 +020035 private static final String LOG_TAG = "StackScrollAlgorithm";
36
Selim Cinek67b22602014-03-10 15:40:16 +010037 private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
38 private static final int MAX_ITEMS_IN_TOP_STACK = 3;
39
40 private int mPaddingBetweenElements;
41 private int mCollapsedSize;
42 private int mTopStackPeekSize;
43 private int mBottomStackPeekSize;
44 private int mZDistanceBetweenElements;
45 private int mZBasicHeight;
46
47 private StackIndentationFunctor mTopStackIndentationFunctor;
48 private StackIndentationFunctor mBottomStackIndentationFunctor;
49
50 private float mLayoutHeight;
51 private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
Selim Cinek1685e632014-04-08 02:27:49 +020052 private boolean mIsExpansionChanging;
53 private int mFirstChildMaxHeight;
54 private boolean mIsExpanded;
55 private View mFirstChildWhileExpanding;
56 private boolean mExpandedOnStart;
Selim Cinek67b22602014-03-10 15:40:16 +010057
58 public StackScrollAlgorithm(Context context) {
59 initConstants(context);
60 }
61
62 private void initConstants(Context context) {
63
64 // currently the padding is in the elements themself
65 mPaddingBetweenElements = 0;
66 mCollapsedSize = context.getResources()
67 .getDimensionPixelSize(R.dimen.notification_row_min_height);
68 mTopStackPeekSize = context.getResources()
69 .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
70 mBottomStackPeekSize = context.getResources()
71 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
72 mZDistanceBetweenElements = context.getResources()
73 .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
74 mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
75
76 mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
77 MAX_ITEMS_IN_TOP_STACK,
78 mTopStackPeekSize,
79 mCollapsedSize + mPaddingBetweenElements,
80 0.5f);
81 mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
82 MAX_ITEMS_IN_BOTTOM_STACK,
83 mBottomStackPeekSize,
84 mBottomStackPeekSize,
85 0.5f);
86 }
87
88
89 public void getStackScrollState(StackScrollState resultState) {
90 // The state of the local variables are saved in an algorithmState to easily subdivide it
91 // into multiple phases.
92 StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
93
94 // First we reset the view states to their default values.
95 resultState.resetViewStates();
96
Selim Cinekb6d85eb2014-03-28 20:21:01 +010097 // The first element is always in there so it's initialized with 1.0f;
Selim Cinek67b22602014-03-10 15:40:16 +010098 algorithmState.itemsInTopStack = 1.0f;
99 algorithmState.partialInTop = 0.0f;
100 algorithmState.lastTopStackIndex = 0;
101 algorithmState.scrollY = resultState.getScrollY();
102 algorithmState.itemsInBottomStack = 0.0f;
Jorim Jaggid4a57442014-04-10 02:45:55 +0200103 updateVisibleChildren(resultState, algorithmState);
Selim Cinek67b22602014-03-10 15:40:16 +0100104
105 // Phase 1:
106 findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
107
108 // Phase 2:
109 updatePositionsForState(resultState, algorithmState);
110
111 // Phase 3:
112 updateZValuesForState(resultState, algorithmState);
113
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100114 // write the algorithm state to the result
Selim Cinek67b22602014-03-10 15:40:16 +0100115 resultState.setScrollY(algorithmState.scrollY);
116 }
117
118 /**
Jorim Jaggid4a57442014-04-10 02:45:55 +0200119 * Update the visible children on the state.
120 */
121 private void updateVisibleChildren(StackScrollState resultState,
122 StackScrollAlgorithmState state) {
123 ViewGroup hostView = resultState.getHostView();
124 int childCount = hostView.getChildCount();
125 state.visibleChildren.clear();
126 state.visibleChildren.ensureCapacity(childCount);
127 for (int i = 0; i < childCount; i++) {
128 View v = hostView.getChildAt(i);
129 if (v.getVisibility() != View.GONE) {
130 state.visibleChildren.add(v);
131 }
132 }
133 }
134
135 /**
Selim Cinek67b22602014-03-10 15:40:16 +0100136 * Determine the positions for the views. This is the main part of the algorithm.
137 *
138 * @param resultState The result state to update if a change to the properties of a child occurs
139 * @param algorithmState The state in which the current pass of the algorithm is currently in
140 * and which will be updated
141 */
142 private void updatePositionsForState(StackScrollState resultState,
143 StackScrollAlgorithmState algorithmState) {
144 float stackHeight = getLayoutHeight();
145
Selim Cinek1685e632014-04-08 02:27:49 +0200146 // The starting position of the bottom stack peek
147 float bottomPeekStart = stackHeight - mBottomStackPeekSize;
148
Selim Cinek67b22602014-03-10 15:40:16 +0100149 // The position where the bottom stack starts.
Selim Cinek1685e632014-04-08 02:27:49 +0200150 float transitioningPositionStart = bottomPeekStart - mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100151
152 // The y coordinate of the current child.
153 float currentYPosition = 0.0f;
154
155 // How far in is the element currently transitioning into the bottom stack.
156 float yPositionInScrollView = 0.0f;
157
Jorim Jaggid4a57442014-04-10 02:45:55 +0200158 int childCount = algorithmState.visibleChildren.size();
Selim Cinek67b22602014-03-10 15:40:16 +0100159 int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
160 for (int i = 0; i < childCount; i++) {
Jorim Jaggid4a57442014-04-10 02:45:55 +0200161 View child = algorithmState.visibleChildren.get(i);
Selim Cinek67b22602014-03-10 15:40:16 +0100162 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
163 childViewState.yTranslation = currentYPosition;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200164 childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
Selim Cinek67b22602014-03-10 15:40:16 +0100165 int childHeight = child.getHeight();
166 // The y position after this element
167 float nextYPosition = currentYPosition + childHeight + mPaddingBetweenElements;
168 float yPositionInScrollViewAfterElement = yPositionInScrollView
169 + childHeight
170 + mPaddingBetweenElements;
171 float scrollOffset = yPositionInScrollViewAfterElement - algorithmState.scrollY;
172 if (i < algorithmState.lastTopStackIndex) {
173 // Case 1:
174 // We are in the top Stack
175 nextYPosition = updateStateForTopStackChild(algorithmState,
176 numberOfElementsCompletelyIn,
177 i, childViewState);
Selim Cinek67b22602014-03-10 15:40:16 +0100178 } else if (i == algorithmState.lastTopStackIndex) {
179 // Case 2:
180 // First element of regular scrollview comes next, so the position is just the
181 // scrolling position
Selim Cinek1685e632014-04-08 02:27:49 +0200182 nextYPosition = updateStateForFirstScrollingChild(transitioningPositionStart,
183 childViewState, scrollOffset);
Selim Cinek67b22602014-03-10 15:40:16 +0100184 } else if (nextYPosition >= transitioningPositionStart) {
185 if (currentYPosition >= transitioningPositionStart) {
186 // Case 3:
187 // According to the regular scroll view we are fully translated out of the
188 // bottom of the screen so we are fully in the bottom stack
189 nextYPosition = updateStateForChildFullyInBottomStack(algorithmState,
190 transitioningPositionStart, childViewState, childHeight);
Selim Cinek67b22602014-03-10 15:40:16 +0100191 } else {
192 // Case 4:
193 // According to the regular scroll view we are currently translating out of /
194 // into the bottom of the screen
195 nextYPosition = updateStateForChildTransitioningInBottom(
196 algorithmState, stackHeight, transitioningPositionStart,
197 currentYPosition, childViewState,
198 childHeight, nextYPosition);
199 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200200 } else {
201 childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
202 }
203 // The first card is always rendered.
204 if (i == 0) {
205 childViewState.alpha = 1.0f;
206 childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
207 }
208 if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
209 Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
Selim Cinek67b22602014-03-10 15:40:16 +0100210 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100211 nextYPosition = Math.max(0, nextYPosition);
Selim Cinek67b22602014-03-10 15:40:16 +0100212 currentYPosition = nextYPosition;
213 yPositionInScrollView = yPositionInScrollViewAfterElement;
214 }
215 }
216
Selim Cinek1685e632014-04-08 02:27:49 +0200217 /**
218 * Update the state for the first child which is in the regular scrolling area.
219 *
220 * @param transitioningPositionStart the transition starting position of the bottom stack
221 * @param childViewState the view state of the child
222 * @param scrollOffset the position in the regular scroll view after this child
223 * @return the next child position
224 */
225 private float updateStateForFirstScrollingChild(float transitioningPositionStart,
226 StackScrollState.ViewState childViewState, float scrollOffset) {
227 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
228 if (scrollOffset < transitioningPositionStart) {
229 return scrollOffset;
230 } else {
231 return transitioningPositionStart;
232 }
233 }
234
235 private int getMaxAllowedChildHeight(View child) {
236 if (child instanceof ExpandableNotificationRow) {
237 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
238 return row.getMaximumAllowedExpandHeight();
239 }
240 return child.getHeight();
241 }
242
Selim Cinek67b22602014-03-10 15:40:16 +0100243 private float updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
244 float stackHeight, float transitioningPositionStart, float currentYPosition,
245 StackScrollState.ViewState childViewState, int childHeight, float nextYPosition) {
246 float newSize = transitioningPositionStart + mCollapsedSize - currentYPosition;
247 newSize = Math.min(childHeight, newSize);
248 // Transitioning element on top of bottom stack:
249 algorithmState.partialInBottom = 1.0f - (
250 (stackHeight - mBottomStackPeekSize - nextYPosition) / mCollapsedSize);
251 // Our element can be expanded, so we might even have to scroll further than
252 // mCollapsedSize
253 algorithmState.partialInBottom = Math.min(1.0f, algorithmState.partialInBottom);
254 float offset = mBottomStackIndentationFunctor.getValue(
255 algorithmState.partialInBottom);
256 nextYPosition = transitioningPositionStart + offset;
257 algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
258 // TODO: only temporarily collapse
259 if (childHeight != (int) newSize) {
260 childViewState.height = (int) newSize;
261 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200262 childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
263
Selim Cinek67b22602014-03-10 15:40:16 +0100264 return nextYPosition;
265 }
266
267 private float updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
268 float transitioningPositionStart, StackScrollState.ViewState childViewState,
269 int childHeight) {
270
271 float nextYPosition;
272 algorithmState.itemsInBottomStack += 1.0f;
273 if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
274 // We are visually entering the bottom stack
275 nextYPosition = transitioningPositionStart
276 + mBottomStackIndentationFunctor.getValue(
277 algorithmState.itemsInBottomStack);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200278 childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING;
Selim Cinek67b22602014-03-10 15:40:16 +0100279 } else {
280 // we are fully inside the stack
281 if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
282 childViewState.alpha = 0.0f;
283 } else if (algorithmState.itemsInBottomStack
284 > MAX_ITEMS_IN_BOTTOM_STACK + 1) {
285 childViewState.alpha = 1.0f - algorithmState.partialInBottom;
286 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200287 childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
Selim Cinek67b22602014-03-10 15:40:16 +0100288 nextYPosition = transitioningPositionStart + mBottomStackPeekSize;
289 }
290 // TODO: only temporarily collapse
291 if (childHeight != mCollapsedSize) {
292 childViewState.height = mCollapsedSize;
293 }
294 return nextYPosition;
295 }
296
297 private float updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
298 int numberOfElementsCompletelyIn, int i, StackScrollState.ViewState childViewState) {
299
300 float nextYPosition = 0;
301
302 // First we calculate the index relative to the current stack window of size at most
303 // {@link #MAX_ITEMS_IN_TOP_STACK}
304 int paddedIndex = i
305 - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0);
306 if (paddedIndex >= 0) {
307 // We are currently visually entering the top stack
308 nextYPosition = mCollapsedSize + mPaddingBetweenElements -
309 mTopStackIndentationFunctor.getValue(
310 algorithmState.itemsInTopStack - i - 1);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100311 nextYPosition = Math.min(nextYPosition, mLayoutHeight - mCollapsedSize
312 - mBottomStackPeekSize);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200313 if (paddedIndex == 0) {
Selim Cinek67b22602014-03-10 15:40:16 +0100314 childViewState.alpha = 1.0f - algorithmState.partialInTop;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200315 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
316 } else {
317 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
Selim Cinek67b22602014-03-10 15:40:16 +0100318 }
319 } else {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200320 // We are hidden behind the top card and faded out, so we can hide ourselves.
321 childViewState.alpha = 0.0f;
322 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
Selim Cinek67b22602014-03-10 15:40:16 +0100323 }
324 return nextYPosition;
325 }
326
327 /**
328 * Find the number of items in the top stack and update the result state if needed.
329 *
330 * @param resultState The result state to update if a height change of an child occurs
331 * @param algorithmState The state in which the current pass of the algorithm is currently in
332 * and which will be updated
333 */
334 private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
335 StackScrollAlgorithmState algorithmState) {
336
337 // The y Position if the element would be in a regular scrollView
338 float yPositionInScrollView = 0.0f;
Jorim Jaggid4a57442014-04-10 02:45:55 +0200339 int childCount = algorithmState.visibleChildren.size();
Selim Cinek67b22602014-03-10 15:40:16 +0100340
341 // find the number of elements in the top stack.
342 for (int i = 0; i < childCount; i++) {
Jorim Jaggid4a57442014-04-10 02:45:55 +0200343 View child = algorithmState.visibleChildren.get(i);
Selim Cinek67b22602014-03-10 15:40:16 +0100344 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
345 int childHeight = child.getHeight();
346 float yPositionInScrollViewAfterElement = yPositionInScrollView
347 + childHeight
348 + mPaddingBetweenElements;
349 if (yPositionInScrollView < algorithmState.scrollY) {
350 if (yPositionInScrollViewAfterElement <= algorithmState.scrollY) {
351 // According to the regular scroll view we are fully off screen
352 algorithmState.itemsInTopStack += 1.0f;
353 if (childHeight != mCollapsedSize) {
354 childViewState.height = mCollapsedSize;
355 }
356 } else {
357 // According to the regular scroll view we are partially off screen
358 // If it is expanded we have to collapse it to a new size
359 float newSize = yPositionInScrollViewAfterElement
360 - mPaddingBetweenElements
361 - algorithmState.scrollY;
362
363 // How much did we scroll into this child
364 algorithmState.partialInTop = (mCollapsedSize - newSize) / (mCollapsedSize
365 + mPaddingBetweenElements);
366
367 // Our element can be expanded, so this can get negative
368 algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
369 algorithmState.itemsInTopStack += algorithmState.partialInTop;
370 // TODO: handle overlapping sizes with end stack
371 newSize = Math.max(mCollapsedSize, newSize);
372 // TODO: only temporarily collapse
373 if (newSize != childHeight) {
374 childViewState.height = (int) newSize;
375
376 // We decrease scrollY by the same amount we made this child smaller.
377 // The new scroll position is therefore the start of the element
378 algorithmState.scrollY = (int) yPositionInScrollView;
379 resultState.setScrollY(algorithmState.scrollY);
380 }
381 if (childHeight > mCollapsedSize) {
382 // If we are just resizing this child, this element is not treated to be
383 // transitioning into the stack and therefore it is the last element in
384 // the stack.
385 algorithmState.lastTopStackIndex = i;
386 break;
387 }
388 }
389 } else {
390 algorithmState.lastTopStackIndex = i;
Selim Cinek1685e632014-04-08 02:27:49 +0200391 if (i == 0) {
Selim Cinek67b22602014-03-10 15:40:16 +0100392
Selim Cinek1685e632014-04-08 02:27:49 +0200393 // The starting position of the bottom stack peek
394 float bottomPeekStart = getLayoutHeight() - mBottomStackPeekSize;
395 // Collapse and expand the first child while the shade is being expanded
396 float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
397 ? mFirstChildMaxHeight
398 : childHeight;
399 childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
400 mCollapsedSize);
401 }
Selim Cinek67b22602014-03-10 15:40:16 +0100402 // We are already past the stack so we can end the loop
403 break;
404 }
405 yPositionInScrollView = yPositionInScrollViewAfterElement;
406 }
407 }
408
409 /**
410 * Calculate the Z positions for all children based on the number of items in both stacks and
411 * save it in the resultState
412 *
413 * @param resultState The result state to update the zTranslation values
414 * @param algorithmState The state in which the current pass of the algorithm is currently in
415 */
416 private void updateZValuesForState(StackScrollState resultState,
417 StackScrollAlgorithmState algorithmState) {
Jorim Jaggid4a57442014-04-10 02:45:55 +0200418 int childCount = algorithmState.visibleChildren.size();
Selim Cinek67b22602014-03-10 15:40:16 +0100419 for (int i = 0; i < childCount; i++) {
Jorim Jaggid4a57442014-04-10 02:45:55 +0200420 View child = algorithmState.visibleChildren.get(i);
Selim Cinek67b22602014-03-10 15:40:16 +0100421 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
422 if (i < algorithmState.itemsInTopStack) {
423 float stackIndex = algorithmState.itemsInTopStack - i;
424 stackIndex = Math.min(stackIndex, MAX_ITEMS_IN_TOP_STACK + 2);
425 childViewState.zTranslation = mZBasicHeight
426 + stackIndex * mZDistanceBetweenElements;
427 } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
428 float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack);
429 float translationZ = mZBasicHeight
430 - numItemsAbove * mZDistanceBetweenElements;
431 childViewState.zTranslation = translationZ;
432 } else {
433 childViewState.zTranslation = mZBasicHeight;
434 }
435 }
436 }
437
438 public float getLayoutHeight() {
439 return mLayoutHeight;
440 }
441
442 public void setLayoutHeight(float layoutHeight) {
443 this.mLayoutHeight = layoutHeight;
444 }
445
Selim Cinek1685e632014-04-08 02:27:49 +0200446 public void onExpansionStarted(StackScrollState currentState) {
447 mIsExpansionChanging = true;
448 mExpandedOnStart = mIsExpanded;
449 ViewGroup hostView = currentState.getHostView();
450 updateFirstChildHeightWhileExpanding(hostView);
451 }
452
453 private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) {
Jorim Jaggi9f347ae2014-04-11 00:47:18 +0200454 mFirstChildWhileExpanding = findFirstVisibleChild(hostView);
455 if (mFirstChildWhileExpanding != null) {
Selim Cinek1685e632014-04-08 02:27:49 +0200456 if (mExpandedOnStart) {
457
458 // We are collapsing the shade, so the first child can get as most as high as the
459 // current height.
460 mFirstChildMaxHeight = mFirstChildWhileExpanding.getHeight();
461 } else {
462
463 // We are expanding the shade, expand it to its full height.
464 mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
465 }
466 } else {
Selim Cinek1685e632014-04-08 02:27:49 +0200467 mFirstChildMaxHeight = 0;
468 }
469 }
470
Jorim Jaggi9f347ae2014-04-11 00:47:18 +0200471 private View findFirstVisibleChild(ViewGroup container) {
472 int childCount = container.getChildCount();
473 for (int i = 0; i < childCount; i++) {
474 View child = container.getChildAt(i);
475 if (child.getVisibility() != View.GONE) {
476 return child;
477 }
478 }
479 return null;
480 }
481
Selim Cinek1685e632014-04-08 02:27:49 +0200482 public void onExpansionStopped() {
483 mIsExpansionChanging = false;
484 mFirstChildWhileExpanding = null;
485 }
486
487 public void setIsExpanded(boolean isExpanded) {
488 this.mIsExpanded = isExpanded;
489 }
490
491 public void notifyChildrenChanged(ViewGroup hostView) {
492 if (mIsExpansionChanging) {
493 updateFirstChildHeightWhileExpanding(hostView);
494 }
495 }
496
Selim Cinek67b22602014-03-10 15:40:16 +0100497 class StackScrollAlgorithmState {
498
499 /**
500 * The scroll position of the algorithm
501 */
502 public int scrollY;
503
504 /**
505 * The quantity of items which are in the top stack.
506 */
507 public float itemsInTopStack;
508
509 /**
510 * how far in is the element currently transitioning into the top stack
511 */
512 public float partialInTop;
513
514 /**
515 * The last item index which is in the top stack.
516 * NOTE: In the top stack the item after the transitioning element is also in the stack!
517 * This is needed to ensure a smooth transition between the y position in the regular
518 * scrollview and the one in the stack.
519 */
520 public int lastTopStackIndex;
521
522 /**
523 * The quantity of items which are in the bottom stack.
524 */
525 public float itemsInBottomStack;
526
527 /**
528 * how far in is the element currently transitioning into the bottom stack
529 */
530 public float partialInBottom;
Jorim Jaggid4a57442014-04-10 02:45:55 +0200531
532 /**
533 * The children from the host view which are not gone.
534 */
535 public final ArrayList<View> visibleChildren = new ArrayList<View>();
Selim Cinek67b22602014-03-10 15:40:16 +0100536 }
537
538}