private void updateStateForChildFullyInBottomStack(
     StackScrollAlgorithmState algorithmState,
     float transitioningPositionStart,
     StackViewState childViewState,
     int childHeight,
     AmbientState ambientState) {
   Log.d(TAG, "updateStateForChildFullyInBottomStack: ");
   float currentYPosition;
   algorithmState.itemsInBottomStack += 1.0f;
   if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
     // We are visually entering the bottom stack
     currentYPosition =
         transitioningPositionStart
             + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
             - mPaddingBetweenElements;
     childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
   } else {
     // we are fully inside the stack
     if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
       childViewState.alpha = 0.0f;
     } else if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 1) {
       childViewState.alpha = 1.0f - algorithmState.partialInBottom;
     }
     childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
     currentYPosition = ambientState.getInnerHeight();
   }
   childViewState.yTranslation = currentYPosition - childHeight;
   clampPositionToTopStackEnd(childViewState, childHeight);
 }
  private void updateStateForChildTransitioningInBottom(
      StackScrollAlgorithmState algorithmState,
      float transitioningPositionStart,
      float bottomPeakStart,
      float currentYPosition,
      StackViewState childViewState,
      int childHeight) {
    Log.d(TAG, "updateStateForChildTransitioningInBottom: ");
    // This is the transitioning element on top of bottom stack, calculate how far we are in.
    algorithmState.partialInBottom =
        1.0f
            - ((transitioningPositionStart - currentYPosition)
                / (childHeight + mPaddingBetweenElements));

    // the offset starting at the transitionPosition of the bottom stack
    float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
    algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
    int newHeight = childHeight;
    if (childHeight > mCollapsedSize && mIsSmallScreen) {
      newHeight =
          (int)
              Math.max(
                  Math.min(
                      transitioningPositionStart
                          + offset
                          - mPaddingBetweenElements
                          - currentYPosition,
                      childHeight),
                  mCollapsedSize);
      childViewState.height = newHeight;
    }
    childViewState.yTranslation =
        transitioningPositionStart + offset - newHeight - mPaddingBetweenElements;

    // We want at least to be at the end of the top stack when collapsing
    clampPositionToTopStackEnd(childViewState, newHeight);
    childViewState.location = StackViewState.LOCATION_MAIN_AREA;
  }
  public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
    // The state of the local variables are saved in an algorithmState to easily subdivide it
    // into multiple phases.
    Log.d(TAG, "getStackScrollState: ");
    StackScrollAlgorithmState algorithmState = mTempAlgorithmState;

    // First we reset the view states to their default values.
    resultState.resetViewStates();

    algorithmState.itemsInTopStack = 0.0f;
    algorithmState.partialInTop = 0.0f;
    algorithmState.lastTopStackIndex = 0;
    algorithmState.scrolledPixelsTop = 0;
    algorithmState.itemsInBottomStack = 0.0f;
    algorithmState.partialInBottom = 0.0f;
    float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);

    int scrollY = ambientState.getScrollY();

    // Due to the overScroller, the stackscroller can have negative scroll state. This is
    // already accounted for by the top padding and doesn't need an additional adaption
    scrollY = Math.max(0, scrollY);
    algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);

    updateVisibleChildren(resultState, algorithmState);

    // Phase 1:
    findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);

    // Phase 2:
    updatePositionsForState(resultState, algorithmState, ambientState);

    // Phase 3:
    updateZValuesForState(resultState, algorithmState);

    handleDraggedViews(ambientState, resultState, algorithmState);
    updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
    updateClipping(resultState, algorithmState, ambientState);
    updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex());
    getNotificationChildrenStates(resultState, algorithmState);
  }
  /**
   * Find the number of items in the top stack and update the result state if needed.
   *
   * @param resultState The result state to update if a height change of an child occurs
   * @param algorithmState The state in which the current pass of the algorithm is currently in
   */
  private void findNumberOfItemsInTopStackAndUpdateState(
      StackScrollState resultState,
      StackScrollAlgorithmState algorithmState,
      AmbientState ambientState) {
    Log.d(TAG, "findNumberOfItemsInTopStackAndUpdateState: ");
    // The y Position if the element would be in a regular scrollView
    float yPositionInScrollView = 0.0f;
    int childCount = algorithmState.visibleChildren.size();

    // find the number of elements in the top stack.
    for (int i = 0; i < childCount; i++) {
      ExpandableView child = algorithmState.visibleChildren.get(i);
      StackViewState childViewState = resultState.getViewStateForView(child);
      int childHeight = getMaxAllowedChildHeight(child, ambientState);
      float yPositionInScrollViewAfterElement =
          yPositionInScrollView + childHeight + mPaddingBetweenElements;
      if (yPositionInScrollView < algorithmState.scrollY) {
        if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {

          // The starting position of the bottom stack peek
          int bottomPeekStart =
              ambientState.getInnerHeight() - mBottomStackPeekSize - mCollapseSecondCardPadding;
          // Collapse and expand the first child while the shade is being expanded
          float maxHeight =
              mIsExpansionChanging && child == mFirstChildWhileExpanding
                  ? mFirstChildMaxHeight
                  : childHeight;
          childViewState.height =
              (int) Math.max(Math.min(bottomPeekStart, maxHeight), mCollapsedSize);
          algorithmState.itemsInTopStack = 1.0f;

        } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
          // According to the regular scroll view we are fully off screen
          algorithmState.itemsInTopStack += 1.0f;
          if (i == 0) {
            childViewState.height = mCollapsedSize;
          }
        } else {
          // According to the regular scroll view we are partially off screen

          // How much did we scroll into this child
          algorithmState.scrolledPixelsTop = algorithmState.scrollY - yPositionInScrollView;
          algorithmState.partialInTop =
              (algorithmState.scrolledPixelsTop) / (childHeight + mPaddingBetweenElements);

          // Our element can be expanded, so this can get negative
          algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
          algorithmState.itemsInTopStack += algorithmState.partialInTop;

          if (i == 0) {
            // If it is expanded we have to collapse it to a new size
            float newSize =
                yPositionInScrollViewAfterElement
                    - mPaddingBetweenElements
                    - algorithmState.scrollY
                    + mCollapsedSize;
            newSize = Math.max(mCollapsedSize, newSize);
            algorithmState.itemsInTopStack = 1.0f;
            childViewState.height = (int) newSize;
          }
          algorithmState.lastTopStackIndex = i;
          break;
        }
      } else {
        algorithmState.lastTopStackIndex = i - 1;
        // We are already past the stack so we can end the loop
        break;
      }
      yPositionInScrollView = yPositionInScrollViewAfterElement;
    }
  }