@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();
      }
    }
  }
  /** Focuses the task at the specified index in the stack */
  void focusTask(int taskIndex, boolean scrollToNewPosition) {
    // Return early if the task is already focused
    if (taskIndex == mFocusedTaskIndex) return;

    if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
      mFocusedTaskIndex = taskIndex;

      // Focus the view if possible, otherwise, focus the view after we scroll into position
      Task t = mStack.getTasks().get(taskIndex);
      TaskView tv = getChildViewForTask(t);
      Runnable postScrollRunnable = null;
      if (tv != null) {
        tv.setFocusedTask();
      } else {
        postScrollRunnable =
            new Runnable() {
              @Override
              public void run() {
                Task t = mStack.getTasks().get(mFocusedTaskIndex);
                TaskView tv = getChildViewForTask(t);
                if (tv != null) {
                  tv.setFocusedTask();
                }
              }
            };
      }

      // Scroll the view into position (just center it in the curve)
      if (scrollToNewPosition) {
        float newScroll = mLayoutAlgorithm.getStackScrollForTask(t) - 0.5f;
        newScroll = mStackScroller.getBoundedStackScroll(newScroll);
        mStackScroller.animateScroll(
            mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
      } else {
        if (postScrollRunnable != null) {
          postScrollRunnable.run();
        }
      }
    }
  }