private int getMaxAllowedChildHeight(View child, AmbientState ambientState) { Log.d(TAG, "getMaxAllowedChildHeight: "); if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; if (ambientState == null && row.isHeadsUp() || ambientState != null && ambientState.getTopHeadsUpEntry() == child) { int extraSize = row.getIntrinsicHeight() - row.getHeadsUpHeight(); return mCollapsedSize + extraSize; } return row.getIntrinsicHeight(); } else if (child instanceof ExpandableView) { ExpandableView expandableView = (ExpandableView) child; return expandableView.getIntrinsicHeight(); } return child == null ? mCollapsedSize : child.getHeight(); }
/** * Determine the positions for the views. This is the main part of the algorithm. * * @param resultState The result state to update if a change to the properties of a child occurs * @param algorithmState The state in which the current pass of the algorithm is currently in * @param ambientState The current ambient state */ private void updatePositionsForState( StackScrollState resultState, StackScrollAlgorithmState algorithmState, AmbientState ambientState) { Log.d(TAG, "updatePositionsForState: "); // The starting position of the bottom stack peek float bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize; // The position where the bottom stack starts. float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength; // The y coordinate of the current child. float currentYPosition = 0.0f; // How far in is the element currently transitioning into the bottom stack. float yPositionInScrollView = 0.0f; // If we have a heads-up higher than the collapsed height we need to add the difference to // the padding of all other elements, i.e push in the top stack slightly. ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry(); int childCount = algorithmState.visibleChildren.size(); int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f ? algorithmState.lastTopStackIndex : (int) algorithmState.itemsInTopStack; for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); StackViewState childViewState = resultState.getViewStateForView(child); childViewState.location = StackViewState.LOCATION_UNKNOWN; int childHeight = getMaxAllowedChildHeight(child, ambientState); float yPositionInScrollViewAfterElement = yPositionInScrollView + childHeight + mPaddingBetweenElements; float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize; if (i == algorithmState.lastTopStackIndex + 1) { // Normally the position of this child is the position in the regular scrollview, // but if the two stacks are very close to each other, // then have have to push it even more upwards to the position of the bottom // stack start. currentYPosition = Math.min(scrollOffset, bottomStackStart); } childViewState.yTranslation = currentYPosition; // The y position after this element float nextYPosition = currentYPosition + childHeight + mPaddingBetweenElements; if (i <= algorithmState.lastTopStackIndex) { // Case 1: // We are in the top Stack updateStateForTopStackChild( algorithmState, numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset); clampPositionToTopStackEnd(childViewState, childHeight); // check if we are overlapping with the bottom stack if (childViewState.yTranslation + childHeight + mPaddingBetweenElements >= bottomStackStart && !mIsExpansionChanging && i != 0 && mIsSmallScreen) { // we just collapse this element slightly int newSize = (int) Math.max( bottomStackStart - mPaddingBetweenElements - childViewState.yTranslation, mCollapsedSize); childViewState.height = newSize; updateStateForChildTransitioningInBottom( algorithmState, bottomStackStart, bottomPeekStart, childViewState.yTranslation, childViewState, childHeight); } clampPositionToBottomStackStart(childViewState, childViewState.height, ambientState); } else if (nextYPosition >= bottomStackStart) { // Case 2: // We are in the bottom stack. if (currentYPosition >= bottomStackStart) { // According to the regular scroll view we are fully translated out of the // bottom of the screen so we are fully in the bottom stack updateStateForChildFullyInBottomStack( algorithmState, bottomStackStart, childViewState, childHeight, ambientState); } else { // According to the regular scroll view we are currently translating out of / // into the bottom of the screen updateStateForChildTransitioningInBottom( algorithmState, bottomStackStart, bottomPeekStart, currentYPosition, childViewState, childHeight); } } else { // Case 3: // We are in the regular scroll area. childViewState.location = StackViewState.LOCATION_MAIN_AREA; clampYTranslation(childViewState, childHeight, ambientState); } // The first card is always rendered. if (i == 0) { childViewState.alpha = 1.0f; childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0); if (childViewState.yTranslation + childViewState.height > bottomPeekStart - mCollapseSecondCardPadding) { childViewState.height = (int) Math.max( bottomPeekStart - mCollapseSecondCardPadding - childViewState.yTranslation, mCollapsedSize); } childViewState.location = StackViewState.LOCATION_FIRST_CARD; } if (childViewState.location == StackViewState.LOCATION_UNKNOWN) { Log.wtf(LOG_TAG, "Failed to assign location for child " + i); } currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; yPositionInScrollView = yPositionInScrollViewAfterElement; if (ambientState.isShadeExpanded() && topHeadsUpEntry != null && child != topHeadsUpEntry) { childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() - mCollapsedSize; } childViewState.yTranslation += ambientState.getTopPadding() + ambientState.getStackTranslation(); } updateHeadsUpStates(resultState, algorithmState, ambientState); }