@Override
  public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
    // Remove the view associated with this task, we can't rely on updateTransforms
    // to work here because the task is no longer in the list
    TaskView tv = getChildViewForTask(removedTask);
    if (tv != null) {
      mViewPool.returnViewToPool(tv);
    }

    // Notify the callback that we've removed the task and it can clean up after it
    mCb.onTaskViewDismissed(removedTask);

    // Get the stack scroll of the task to anchor to (since we are removing something, the front
    // most task will be our anchor task)
    Task anchorTask = null;
    float prevAnchorTaskScroll = 0;
    boolean pullStackForward = stack.getTaskCount() > 0;
    if (pullStackForward) {
      anchorTask = mStack.getFrontMostTask();
      prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
    }

    // Update the min/max scroll and animate other task views into their new positions
    updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);

    // Offset the stack by as much as the anchor task would otherwise move back
    if (pullStackForward) {
      float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
      mStackScroller.setStackScroll(
          mStackScroller.getStackScroll() + (anchorTaskScroll - prevAnchorTaskScroll));
      mStackScroller.boundScroll();
    }

    // Animate all the tasks into place
    requestSynchronizeStackViewsWithModel(200);

    // Update the new front most task
    if (newFrontMostTask != null) {
      TaskView frontTv = getChildViewForTask(newFrontMostTask);
      if (frontTv != null) {
        frontTv.onTaskBound(newFrontMostTask);
      }
    }

    // If there are no remaining tasks, then either unfilter the current stack, or just close
    // the activity if there are no filtered stacks
    if (mStack.getTaskCount() == 0) {
      boolean shouldFinishActivity = true;
      if (mStack.hasFilteredTasks()) {
        mStack.unfilterTasks();
        shouldFinishActivity = (mStack.getTaskCount() == 0);
      }
      if (shouldFinishActivity) {
        mCb.onAllTaskViewsDismissed();
      }
    }
  }
  /**
   * This is called with the full window width and height to allow stack view children to perform
   * the full screen transition down.
   */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    // Compute our stack/task rects
    Rect taskStackBounds = new Rect(mTaskStackBounds);
    taskStackBounds.bottom -= mConfig.systemInsets.bottom;
    computeRects(
        width, height, taskStackBounds, mConfig.launchedWithAltTab, mConfig.launchedFromHome);

    // If this is the first layout, then scroll to the front of the stack and synchronize the
    // stack views immediately to load all the views
    if (mAwaitingFirstLayout) {
      mStackScroller.setStackScrollToInitialState();
      requestSynchronizeStackViewsWithModel();
      synchronizeStackViewsWithModel();
    }

    // Measure each of the TaskViews
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      TaskView tv = (TaskView) getChildAt(i);
      if (tv.isFullScreenView()) {
        tv.measure(widthMeasureSpec, heightMeasureSpec);
      } else {
        if (tv.getBackground() != null) {
          tv.getBackground().getPadding(mTmpRect);
        } else {
          mTmpRect.setEmpty();
        }
        tv.measure(
            MeasureSpec.makeMeasureSpec(
                mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
                MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(
                mLayoutAlgorithm.mTaskRect.height()
                    + mTmpRect.top
                    + mTmpRect.bottom
                    + tv.getMaxFooterHeight(),
                MeasureSpec.EXACTLY));
      }
    }

    setMeasuredDimension(width, height);
  }
 /** ** TaskStackCallbacks Implementation *** */
 @Override
 public void onStackTaskAdded(TaskStack stack, Task t) {
   requestSynchronizeStackViewsWithModel();
 }
 /** Requests that the views be synchronized with the model */
 void requestSynchronizeStackViewsWithModel() {
   requestSynchronizeStackViewsWithModel(0);
 }
 /** ** TaskStackViewScroller.TaskStackViewScrollerCallbacks *** */
 @Override
 public void onScrollChanged(float p) {
   mUIDozeTrigger.poke();
   requestSynchronizeStackViewsWithModel();
   postInvalidateOnAnimation();
 }
 @Override
 public void onTaskViewFullScreenTransitionCompleted() {
   requestSynchronizeStackViewsWithModel();
 }