/**
   * Prepares this task view for the enter-recents animations. This is called earlier in the first
   * layout because the actual animation into recents may take a long time.
   */
  void prepareEnterRecentsAnimation(
      boolean isTaskViewLaunchTargetTask, boolean occludesLaunchTarget, int offscreenY) {
    int initialDim = getDim();
    if (mConfig.launchedHasConfigurationChanged) {
      // Just load the views as-is
    } else if (mConfig.launchedFromAppWithThumbnail) {
      if (isTaskViewLaunchTargetTask) {
        // Set the dim to 0 so we can animate it in
        initialDim = 0;
        // Hide the action button
        mActionButtonView.setAlpha(0f);
      } else if (occludesLaunchTarget) {
        // Move the task view off screen (below) so we can animate it in
        setTranslationY(offscreenY);
      }

    } else if (mConfig.launchedFromHome) {
      // Move the task view off screen (below) so we can animate it in
      setTranslationY(offscreenY);
      setTranslationZ(0);
      setScaleX(1f);
      setScaleY(1f);
    }
    // Apply the current dim
    setDim(initialDim);
    // Prepare the thumbnail view alpha
    mThumbnailView.prepareEnterRecentsAnimation(isTaskViewLaunchTargetTask);
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
    int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;

    // Measure the content
    mContent.measure(
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));

    // Measure the bar view, and action button
    mHeaderView.measure(
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
    mActionButtonView.measure(
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
        MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
    // Measure the thumbnail to be square
    mThumbnailView.measure(
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
    setMeasuredDimension(width, height);
    invalidateOutline();
  }
  /** Unsets the focused task explicitly. */
  void unsetFocusedTask() {
    mIsFocused = false;
    if (mFocusAnimationsEnabled) {
      // Un-focus the header bar
      mHeaderView.onTaskViewFocusChanged(false, true);
    }

    // Update the thumbnail alpha with the focus
    mThumbnailView.onFocusChanged(false);
    // Call the callback
    if (mCb != null) {
      mCb.onTaskViewFocusChanged(this, false);
    }
    invalidate();
  }
  /** Animates this task view as it exits recents */
  void startLaunchTaskAnimation(
      final Runnable postAnimRunnable,
      boolean isLaunchingTask,
      boolean occludesLaunchTarget,
      boolean lockToTask) {
    if (isLaunchingTask) {
      // Animate the thumbnail alpha back into full opacity for the window animation out
      mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);

      // Animate the dim
      if (mDimAlpha > 0) {
        ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
        anim.setDuration(mConfig.taskViewExitToAppDuration);
        anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
        anim.start();
      }

      // Animate the action button away
      if (!lockToTask) {
        float toScale = 0.9f;
        mActionButtonView.animate().scaleX(toScale).scaleY(toScale);
      }
      mActionButtonView
          .animate()
          .alpha(0f)
          .setStartDelay(0)
          .setDuration(mConfig.taskViewExitToAppDuration)
          .setInterpolator(mConfig.fastOutLinearInInterpolator)
          .withLayer()
          .start();
    } else {
      // Hide the dismiss button
      mHeaderView.startLaunchTaskDismissAnimation();
      // If this is another view in the task grouping and is in front of the launch task,
      // animate it away first
      if (occludesLaunchTarget) {
        animate()
            .alpha(0f)
            .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
            .setStartDelay(0)
            .setUpdateListener(null)
            .setInterpolator(mConfig.fastOutLinearInInterpolator)
            .setDuration(mConfig.taskViewExitToAppDuration)
            .start();
      }
    }
  }
 @Override
 public void onTaskDataUnloaded() {
   if (mThumbnailView != null && mHeaderView != null) {
     // Unbind each of the views from the task data and remove the task callback
     mTask.setCallbacks(null);
     mThumbnailView.unbindFromTask();
     mHeaderView.unbindFromTask();
     // Unbind any listeners
     mHeaderView.mApplicationIcon.setOnClickListener(null);
     mHeaderView.mDismissButton.setOnClickListener(null);
     mActionButtonView.setOnClickListener(null);
     if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
       mHeaderView.mApplicationIcon.setOnLongClickListener(null);
     }
   }
   mTaskDataLoaded = false;
 }
 @Override
 public void onTaskDataLoaded() {
   if (mThumbnailView != null && mHeaderView != null) {
     // Bind each of the views to the new task data
     mThumbnailView.rebindToTask(mTask);
     mHeaderView.rebindToTask(mTask);
     // Rebind any listeners
     mHeaderView.mApplicationIcon.setOnClickListener(this);
     mHeaderView.mDismissButton.setOnClickListener(this);
     mActionButtonView.setOnClickListener(this);
     if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
       if (mConfig.developerOptionsEnabled) {
         mHeaderView.mApplicationIcon.setOnLongClickListener(this);
       }
     }
   }
   mTaskDataLoaded = true;
 }
 @Override
 protected void onFinishInflate() {
   // Bind the views
   mContent = findViewById(R.id.task_view_content);
   mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
   mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
   mThumbnailView.updateClipToTaskBar(mHeaderView);
   mActionButtonView = findViewById(R.id.lock_to_app_fab);
   mActionButtonView.setOutlineProvider(
       new ViewOutlineProvider() {
         @Override
         public void getOutline(View view, Outline outline) {
           // Set the outline to match the FAB background
           outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
         }
       });
   mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
 }
 /** Returns the current dim. */
 public void setDim(int dim) {
   mDimAlpha = dim;
   if (mConfig.useHardwareLayers) {
     // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
     if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
       mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
       mDimLayerPaint.setColorFilter(mDimColorFilter);
       mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
     }
   } else {
     float dimAlpha = mDimAlpha / 255.0f;
     if (mThumbnailView != null) {
       mThumbnailView.setDimAlpha(dimAlpha);
     }
     if (mHeaderView != null) {
       mHeaderView.setDimAlpha(dim);
     }
   }
 }
 /**
  * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
  * if the view is not currently visible, or we are in touch state (where we still want to keep
  * track of focus).
  */
 public void setFocusedTask(boolean animateFocusedState) {
   mIsFocused = true;
   if (mFocusAnimationsEnabled) {
     // Focus the header bar
     mHeaderView.onTaskViewFocusChanged(true, animateFocusedState);
   }
   // Update the thumbnail alpha with the focus
   mThumbnailView.onFocusChanged(true);
   // Call the callback
   if (mCb != null) {
     mCb.onTaskViewFocusChanged(this, true);
   }
   // Workaround, we don't always want it focusable in touch mode, but we want the first task
   // to be focused after the enter-recents animation, which can be triggered from either touch
   // or keyboard
   setFocusableInTouchMode(true);
   requestFocus();
   setFocusableInTouchMode(false);
   invalidate();
 }
  /** Animates this task view as it enters recents */
  void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
    final TaskViewTransform transform = ctx.currentTaskTransform;
    int startDelay = 0;

    if (mConfig.launchedFromAppWithThumbnail) {
      if (mTask.isLaunchTarget) {
        // Animate the dim/overlay
        if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
          // Animate the thumbnail alpha before the dim animation (to prevent updating the
          // hardware layer)
          mThumbnailView.startEnterRecentsAnimation(
              mConfig.transitionEnterFromAppDelay,
              new Runnable() {
                @Override
                public void run() {
                  animateDimToProgress(
                      0,
                      mConfig.taskViewEnterFromAppDuration,
                      ctx.postAnimationTrigger.decrementOnAnimationEnd());
                }
              });
        } else {
          // Immediately start the dim animation
          animateDimToProgress(
              mConfig.transitionEnterFromAppDelay,
              mConfig.taskViewEnterFromAppDuration,
              ctx.postAnimationTrigger.decrementOnAnimationEnd());
        }
        ctx.postAnimationTrigger.increment();

        // Animate the action button in
        fadeInActionButton(
            mConfig.transitionEnterFromAppDelay, mConfig.taskViewEnterFromAppDuration);
      } else {
        // Animate the task up if it was occluding the launch target
        if (ctx.currentTaskOccludesLaunchTarget) {
          setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
          setAlpha(0f);
          animate()
              .alpha(1f)
              .translationY(transform.translationY)
              .setStartDelay(mConfig.transitionEnterFromAppDelay)
              .setUpdateListener(null)
              .setInterpolator(mConfig.fastOutSlowInInterpolator)
              .setDuration(mConfig.taskViewEnterFromHomeDuration)
              .withEndAction(
                  new Runnable() {
                    @Override
                    public void run() {
                      // Decrement the post animation trigger
                      ctx.postAnimationTrigger.decrement();
                    }
                  })
              .start();
          ctx.postAnimationTrigger.increment();
        }
      }
      startDelay = mConfig.transitionEnterFromAppDelay;

    } else if (mConfig.launchedFromHome) {
      // Animate the tasks up
      int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
      int delay =
          mConfig.transitionEnterFromHomeDelay
              + frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;

      setScaleX(transform.scale);
      setScaleY(transform.scale);
      if (!mConfig.fakeShadows) {
        animate().translationZ(transform.translationZ);
      }
      animate()
          .translationY(transform.translationY)
          .setStartDelay(delay)
          .setUpdateListener(ctx.updateListener)
          .setInterpolator(mConfig.quintOutInterpolator)
          .setDuration(
              mConfig.taskViewEnterFromHomeDuration
                  + frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
          .withEndAction(
              new Runnable() {
                @Override
                public void run() {
                  // Decrement the post animation trigger
                  ctx.postAnimationTrigger.decrement();
                }
              })
          .start();
      ctx.postAnimationTrigger.increment();
      startDelay = delay;
    }

    // Enable the focus animations from this point onwards so that they aren't affected by the
    // window transitions
    postDelayed(
        new Runnable() {
          @Override
          public void run() {
            enableFocusAnimations();
          }
        },
        startDelay);
  }