Home | History | Annotate | Download | only in views
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.recents.views;
     18 
     19 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
     20 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
     21 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
     22 
     23 import android.animation.ObjectAnimator;
     24 import android.animation.ValueAnimator;
     25 import android.annotation.IntDef;
     26 import android.content.ComponentName;
     27 import android.content.Context;
     28 import android.content.res.Configuration;
     29 import android.content.res.Resources;
     30 import android.graphics.Canvas;
     31 import android.graphics.Rect;
     32 import android.graphics.drawable.Drawable;
     33 import android.graphics.drawable.GradientDrawable;
     34 import android.os.Bundle;
     35 import android.provider.Settings;
     36 import android.util.ArrayMap;
     37 import android.util.ArraySet;
     38 import android.util.MutableBoolean;
     39 import android.view.LayoutInflater;
     40 import android.view.MotionEvent;
     41 import android.view.View;
     42 import android.view.ViewDebug;
     43 import android.view.ViewGroup;
     44 import android.view.accessibility.AccessibilityEvent;
     45 import android.view.accessibility.AccessibilityNodeInfo;
     46 import android.widget.FrameLayout;
     47 import android.widget.ScrollView;
     48 
     49 import com.android.internal.logging.MetricsLogger;
     50 import com.android.internal.logging.MetricsProto.MetricsEvent;
     51 import com.android.systemui.Interpolators;
     52 import com.android.systemui.R;
     53 import com.android.systemui.recents.Recents;
     54 import com.android.systemui.recents.RecentsActivity;
     55 import com.android.systemui.recents.RecentsActivityLaunchState;
     56 import com.android.systemui.recents.RecentsConfiguration;
     57 import com.android.systemui.recents.RecentsDebugFlags;
     58 import com.android.systemui.recents.events.EventBus;
     59 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
     60 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
     61 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
     62 import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
     63 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
     64 import com.android.systemui.recents.events.activity.HideRecentsEvent;
     65 import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
     66 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
     67 import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
     68 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
     69 import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
     70 import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
     71 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
     72 import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
     73 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
     74 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
     75 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
     76 import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
     77 import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
     78 import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
     79 import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
     80 import com.android.systemui.recents.events.ui.UserInteractionEvent;
     81 import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
     82 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
     83 import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
     84 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
     85 import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
     86 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
     87 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
     88 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
     89 import com.android.systemui.recents.misc.DozeTrigger;
     90 import com.android.systemui.recents.misc.SystemServicesProxy;
     91 import com.android.systemui.recents.misc.Utilities;
     92 import com.android.systemui.recents.model.Task;
     93 import com.android.systemui.recents.model.TaskStack;
     94 
     95 import java.io.PrintWriter;
     96 import java.lang.annotation.Retention;
     97 import java.lang.annotation.RetentionPolicy;
     98 import java.util.ArrayList;
     99 import java.util.List;
    100 
    101 
    102 /* The visual representation of a task stack view */
    103 public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
    104         TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
    105         TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
    106         ViewPool.ViewPoolConsumer<TaskView, Task> {
    107 
    108     private static final String TAG = "TaskStackView";
    109 
    110     // The thresholds at which to show/hide the stack action button.
    111     private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
    112     private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
    113 
    114     public static final int DEFAULT_SYNC_STACK_DURATION = 200;
    115     public static final int SLOW_SYNC_STACK_DURATION = 250;
    116     private static final int DRAG_SCALE_DURATION = 175;
    117     static final float DRAG_SCALE_FACTOR = 1.05f;
    118 
    119     private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
    120     private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
    121 
    122     // The actions to perform when resetting to initial state,
    123     @Retention(RetentionPolicy.SOURCE)
    124     @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
    125     public @interface InitialStateAction {}
    126     /** Do not update the stack and layout to the initial state. */
    127     private static final int INITIAL_STATE_UPDATE_NONE = 0;
    128     /** Update both the stack and layout to the initial state. */
    129     private static final int INITIAL_STATE_UPDATE_ALL = 1;
    130     /** Update only the layout to the initial state. */
    131     private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
    132 
    133     private LayoutInflater mInflater;
    134     private TaskStack mStack = new TaskStack();
    135     @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
    136     TaskStackLayoutAlgorithm mLayoutAlgorithm;
    137     // The stable layout algorithm is only used to calculate the task rect with the stable bounds
    138     private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
    139     @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
    140     private TaskStackViewScroller mStackScroller;
    141     @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
    142     private TaskStackViewTouchHandler mTouchHandler;
    143     private TaskStackAnimationHelper mAnimationHelper;
    144     private GradientDrawable mFreeformWorkspaceBackground;
    145     private ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
    146     private ViewPool<TaskView, Task> mViewPool;
    147 
    148     private ArrayList<TaskView> mTaskViews = new ArrayList<>();
    149     private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
    150     private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
    151     private AnimationProps mDeferredTaskViewLayoutAnimation = null;
    152 
    153     @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
    154     private DozeTrigger mUIDozeTrigger;
    155     @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
    156     private Task mFocusedTask;
    157 
    158     private int mTaskCornerRadiusPx;
    159     private int mDividerSize;
    160     private int mStartTimerIndicatorDuration;
    161 
    162     @ViewDebug.ExportedProperty(category="recents")
    163     private boolean mTaskViewsClipDirty = true;
    164     @ViewDebug.ExportedProperty(category="recents")
    165     private boolean mAwaitingFirstLayout = true;
    166     @ViewDebug.ExportedProperty(category="recents")
    167     private boolean mLaunchNextAfterFirstMeasure = false;
    168     @ViewDebug.ExportedProperty(category="recents")
    169     @InitialStateAction
    170     private int mInitialState = INITIAL_STATE_UPDATE_ALL;
    171     @ViewDebug.ExportedProperty(category="recents")
    172     private boolean mInMeasureLayout = false;
    173     @ViewDebug.ExportedProperty(category="recents")
    174     private boolean mEnterAnimationComplete = false;
    175     @ViewDebug.ExportedProperty(category="recents")
    176     boolean mTouchExplorationEnabled;
    177     @ViewDebug.ExportedProperty(category="recents")
    178     boolean mScreenPinningEnabled;
    179 
    180     // The stable stack bounds are the full bounds that we were measured with from RecentsView
    181     @ViewDebug.ExportedProperty(category="recents")
    182     private Rect mStableStackBounds = new Rect();
    183     // The current stack bounds are dynamic and may change as the user drags and drops
    184     @ViewDebug.ExportedProperty(category="recents")
    185     private Rect mStackBounds = new Rect();
    186     // The current window bounds at the point we were measured
    187     @ViewDebug.ExportedProperty(category="recents")
    188     private Rect mStableWindowRect = new Rect();
    189     // The current window bounds are dynamic and may change as the user drags and drops
    190     @ViewDebug.ExportedProperty(category="recents")
    191     private Rect mWindowRect = new Rect();
    192     // The current display bounds
    193     @ViewDebug.ExportedProperty(category="recents")
    194     private Rect mDisplayRect = new Rect();
    195     // The current display orientation
    196     @ViewDebug.ExportedProperty(category="recents")
    197     private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
    198 
    199     private Rect mTmpRect = new Rect();
    200     private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
    201     private List<TaskView> mTmpTaskViews = new ArrayList<>();
    202     private TaskViewTransform mTmpTransform = new TaskViewTransform();
    203     private int[] mTmpIntPair = new int[2];
    204     private boolean mResetToInitialStateWhenResized;
    205     private int mLastWidth;
    206     private int mLastHeight;
    207 
    208     // A convenience update listener to request updating clipping of tasks
    209     private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
    210             new ValueAnimator.AnimatorUpdateListener() {
    211                 @Override
    212                 public void onAnimationUpdate(ValueAnimator animation) {
    213                     if (!mTaskViewsClipDirty) {
    214                         mTaskViewsClipDirty = true;
    215                         invalidate();
    216                     }
    217                 }
    218             };
    219 
    220     // The drop targets for a task drag
    221     private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
    222         @Override
    223         public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
    224                 boolean isCurrentTarget) {
    225             // This drop target has a fixed bounds and should be checked last, so just fall through
    226             // if it is the current target
    227             if (!isCurrentTarget) {
    228                 return mLayoutAlgorithm.mFreeformRect.contains(x, y);
    229             }
    230             return false;
    231         }
    232     };
    233 
    234     private DropTarget mStackDropTarget = new DropTarget() {
    235         @Override
    236         public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
    237                 boolean isCurrentTarget) {
    238             // This drop target has a fixed bounds and should be checked last, so just fall through
    239             // if it is the current target
    240             if (!isCurrentTarget) {
    241                 return mLayoutAlgorithm.mStackRect.contains(x, y);
    242             }
    243             return false;
    244         }
    245     };
    246 
    247     public TaskStackView(Context context) {
    248         super(context);
    249         SystemServicesProxy ssp = Recents.getSystemServices();
    250         Resources res = context.getResources();
    251 
    252         // Set the stack first
    253         mStack.setCallbacks(this);
    254         mViewPool = new ViewPool<>(context, this);
    255         mInflater = LayoutInflater.from(context);
    256         mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
    257         mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
    258         mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
    259         mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
    260         mAnimationHelper = new TaskStackAnimationHelper(context, this);
    261         mTaskCornerRadiusPx = res.getDimensionPixelSize(
    262                 R.dimen.recents_task_view_rounded_corners_radius);
    263         mDividerSize = ssp.getDockedDividerSize(context);
    264         mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
    265         mDisplayRect = ssp.getDisplayRect();
    266 
    267         int taskBarDismissDozeDelaySeconds = getResources().getInteger(
    268                 R.integer.recents_task_bar_dismiss_delay_seconds);
    269         mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
    270             @Override
    271             public void run() {
    272                 // Show the task bar dismiss buttons
    273                 List<TaskView> taskViews = getTaskViews();
    274                 int taskViewCount = taskViews.size();
    275                 for (int i = 0; i < taskViewCount; i++) {
    276                     TaskView tv = taskViews.get(i);
    277                     tv.startNoUserInteractionAnimation();
    278                 }
    279             }
    280         });
    281         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    282 
    283         mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
    284                 R.drawable.recents_freeform_workspace_bg);
    285         mFreeformWorkspaceBackground.setCallback(this);
    286         if (ssp.hasFreeformWorkspaceSupport()) {
    287             mFreeformWorkspaceBackground.setColor(
    288                     getContext().getColor(R.color.recents_freeform_workspace_bg_color));
    289         }
    290     }
    291 
    292     @Override
    293     protected void onAttachedToWindow() {
    294         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
    295         super.onAttachedToWindow();
    296         readSystemFlags();
    297     }
    298 
    299     @Override
    300     protected void onDetachedFromWindow() {
    301         super.onDetachedFromWindow();
    302         EventBus.getDefault().unregister(this);
    303     }
    304 
    305     /**
    306      * Called from RecentsActivity when it is relaunched.
    307      */
    308     void onReload(boolean isResumingFromVisible) {
    309         if (!isResumingFromVisible) {
    310             // Reset the focused task
    311             resetFocusedTask(getFocusedTask());
    312         }
    313 
    314         // Reset the state of each of the task views
    315         List<TaskView> taskViews = new ArrayList<>();
    316         taskViews.addAll(getTaskViews());
    317         taskViews.addAll(mViewPool.getViews());
    318         for (int i = taskViews.size() - 1; i >= 0; i--) {
    319             taskViews.get(i).onReload(isResumingFromVisible);
    320         }
    321 
    322         // Reset the stack state
    323         readSystemFlags();
    324         mTaskViewsClipDirty = true;
    325         mEnterAnimationComplete = false;
    326         mUIDozeTrigger.stopDozing();
    327         if (isResumingFromVisible) {
    328             // Animate in the freeform workspace
    329             int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
    330             animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
    331                     Interpolators.FAST_OUT_SLOW_IN));
    332         } else {
    333             mStackScroller.reset();
    334             mStableLayoutAlgorithm.reset();
    335             mLayoutAlgorithm.reset();
    336         }
    337 
    338         // Since we always animate to the same place in (the initial state), always reset the stack
    339         // to the initial state when resuming
    340         mAwaitingFirstLayout = true;
    341         mLaunchNextAfterFirstMeasure = false;
    342         mInitialState = INITIAL_STATE_UPDATE_ALL;
    343         requestLayout();
    344     }
    345 
    346     /**
    347      * Sets the stack tasks of this TaskStackView from the given TaskStack.
    348      */
    349     public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
    350         boolean isInitialized = mLayoutAlgorithm.isInitialized();
    351 
    352         // Only notify if we are already initialized, otherwise, everything will pick up all the
    353         // new and old tasks when we next layout
    354         mStack.setTasks(getContext(), stack.computeAllTasksList(),
    355                 allowNotifyStackChanges && isInitialized);
    356     }
    357 
    358     /** Returns the task stack. */
    359     public TaskStack getStack() {
    360         return mStack;
    361     }
    362 
    363     /**
    364      * Updates this TaskStackView to the initial state.
    365      */
    366     public void updateToInitialState() {
    367         mStackScroller.setStackScrollToInitialState();
    368         mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
    369     }
    370 
    371     /** Updates the list of task views */
    372     void updateTaskViewsList() {
    373         mTaskViews.clear();
    374         int childCount = getChildCount();
    375         for (int i = 0; i < childCount; i++) {
    376             View v = getChildAt(i);
    377             if (v instanceof TaskView) {
    378                 mTaskViews.add((TaskView) v);
    379             }
    380         }
    381     }
    382 
    383     /** Gets the list of task views */
    384     List<TaskView> getTaskViews() {
    385         return mTaskViews;
    386     }
    387 
    388     /**
    389      * Returns the front most task view.
    390      *
    391      * @param stackTasksOnly if set, will return the front most task view in the stack (by default
    392      *                       the front most task view will be freeform since they are placed above
    393      *                       stack tasks)
    394      */
    395     private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
    396         List<TaskView> taskViews = getTaskViews();
    397         int taskViewCount = taskViews.size();
    398         for (int i = taskViewCount - 1; i >= 0; i--) {
    399             TaskView tv = taskViews.get(i);
    400             Task task = tv.getTask();
    401             if (stackTasksOnly && task.isFreeformTask()) {
    402                 continue;
    403             }
    404             return tv;
    405         }
    406         return null;
    407     }
    408 
    409     /**
    410      * Finds the child view given a specific {@param task}.
    411      */
    412     public TaskView getChildViewForTask(Task t) {
    413         List<TaskView> taskViews = getTaskViews();
    414         int taskViewCount = taskViews.size();
    415         for (int i = 0; i < taskViewCount; i++) {
    416             TaskView tv = taskViews.get(i);
    417             if (tv.getTask() == t) {
    418                 return tv;
    419             }
    420         }
    421         return null;
    422     }
    423 
    424     /** Returns the stack algorithm for this task stack. */
    425     public TaskStackLayoutAlgorithm getStackAlgorithm() {
    426         return mLayoutAlgorithm;
    427     }
    428 
    429     /**
    430      * Returns the touch handler for this task stack.
    431      */
    432     public TaskStackViewTouchHandler getTouchHandler() {
    433         return mTouchHandler;
    434     }
    435 
    436     /**
    437      * Adds a task to the ignored set.
    438      */
    439     void addIgnoreTask(Task task) {
    440         mIgnoreTasks.add(task.key);
    441     }
    442 
    443     /**
    444      * Removes a task from the ignored set.
    445      */
    446     void removeIgnoreTask(Task task) {
    447         mIgnoreTasks.remove(task.key);
    448     }
    449 
    450     /**
    451      * Returns whether the specified {@param task} is ignored.
    452      */
    453     boolean isIgnoredTask(Task task) {
    454         return mIgnoreTasks.contains(task.key);
    455     }
    456 
    457     /**
    458      * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
    459      * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
    460      * visible range includes all tasks at the target stack scroll. This is useful for ensure that
    461      * all views necessary for a transition or animation will be visible at the start.
    462      *
    463      * This call ignores freeform tasks.
    464      *
    465      * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
    466      *                       match the size of {@param tasks}
    467      * @param tasks The set of tasks for which to generate transforms
    468      * @param curStackScroll The current stack scroll
    469      * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
    470      *                          The range of the union of the visible views at the current and
    471      *                          target stack scrolls will be returned.
    472      * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
    473      *                       Transforms will still be calculated for the ignore tasks.
    474      * @return the front and back most visible task indices (there may be non visible tasks in
    475      *         between this range)
    476      */
    477     int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
    478             ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
    479             ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
    480         int taskCount = tasks.size();
    481         int[] visibleTaskRange = mTmpIntPair;
    482         visibleTaskRange[0] = -1;
    483         visibleTaskRange[1] = -1;
    484         boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
    485 
    486         // We can reuse the task transforms where possible to reduce object allocation
    487         Utilities.matchTaskListSize(tasks, taskTransforms);
    488 
    489         // Update the stack transforms
    490         TaskViewTransform frontTransform = null;
    491         TaskViewTransform frontTransformAtTarget = null;
    492         TaskViewTransform transform = null;
    493         TaskViewTransform transformAtTarget = null;
    494         for (int i = taskCount - 1; i >= 0; i--) {
    495             Task task = tasks.get(i);
    496 
    497             // Calculate the current and (if necessary) the target transform for the task
    498             transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
    499                     taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
    500             if (useTargetStackScroll && !transform.visible) {
    501                 // If we have a target stack scroll and the task is not currently visible, then we
    502                 // just update the transform at the new scroll
    503                 // TODO: Optimize this
    504                 transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
    505                         targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
    506                 if (transformAtTarget.visible) {
    507                     transform.copyFrom(transformAtTarget);
    508                 }
    509             }
    510 
    511             // For ignore tasks, only calculate the stack transform and skip the calculation of the
    512             // visible stack indices
    513             if (ignoreTasksSet.contains(task.key)) {
    514                 continue;
    515             }
    516 
    517             // For freeform tasks, only calculate the stack transform and skip the calculation of
    518             // the visible stack indices
    519             if (task.isFreeformTask()) {
    520                 continue;
    521             }
    522 
    523             frontTransform = transform;
    524             frontTransformAtTarget = transformAtTarget;
    525             if (transform.visible) {
    526                 if (visibleTaskRange[0] < 0) {
    527                     visibleTaskRange[0] = i;
    528                 }
    529                 visibleTaskRange[1] = i;
    530             }
    531         }
    532         return visibleTaskRange;
    533     }
    534 
    535     /**
    536      * Binds the visible {@link TaskView}s at the given target scroll.
    537      */
    538     void bindVisibleTaskViews(float targetStackScroll) {
    539         bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
    540     }
    541 
    542     /**
    543      * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
    544      * current {@link TaskStack}. This call does not continue on to update their position to the
    545      * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
    546      * be added/removed from the view hierarchy and placed in the correct Z order and initial
    547      * position (if not currently on screen).
    548      *
    549      * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
    550      *                          includes those visible at the current stack scroll, and all at the
    551      *                          target stack scroll.
    552      * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
    553      *                            tasks at their non-overridden task progress
    554      */
    555     void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
    556         // Get all the task transforms
    557         ArrayList<Task> tasks = mStack.getStackTasks();
    558         int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
    559                 mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
    560                 ignoreTaskOverrides);
    561 
    562         // Return all the invisible children to the pool
    563         mTmpTaskViewMap.clear();
    564         List<TaskView> taskViews = getTaskViews();
    565         int lastFocusedTaskIndex = -1;
    566         int taskViewCount = taskViews.size();
    567         for (int i = taskViewCount - 1; i >= 0; i--) {
    568             TaskView tv = taskViews.get(i);
    569             Task task = tv.getTask();
    570 
    571             // Skip ignored tasks
    572             if (mIgnoreTasks.contains(task.key)) {
    573                 continue;
    574             }
    575 
    576             // It is possible for the set of lingering TaskViews to differ from the stack if the
    577             // stack was updated before the relayout.  If the task view is no longer in the stack,
    578             // then just return it back to the view pool.
    579             int taskIndex = mStack.indexOfStackTask(task);
    580             TaskViewTransform transform = null;
    581             if (taskIndex != -1) {
    582                 transform = mCurrentTaskTransforms.get(taskIndex);
    583             }
    584 
    585             if (task.isFreeformTask() || (transform != null && transform.visible)) {
    586                 mTmpTaskViewMap.put(task.key, tv);
    587             } else {
    588                 if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
    589                     lastFocusedTaskIndex = taskIndex;
    590                     resetFocusedTask(task);
    591                 }
    592                 mViewPool.returnViewToPool(tv);
    593             }
    594         }
    595 
    596         // Pick up all the newly visible children
    597         for (int i = tasks.size() - 1; i >= 0; i--) {
    598             Task task = tasks.get(i);
    599             TaskViewTransform transform = mCurrentTaskTransforms.get(i);
    600 
    601             // Skip ignored tasks
    602             if (mIgnoreTasks.contains(task.key)) {
    603                 continue;
    604             }
    605 
    606             // Skip the invisible non-freeform stack tasks
    607             if (!task.isFreeformTask() && !transform.visible) {
    608                 continue;
    609             }
    610 
    611             TaskView tv = mTmpTaskViewMap.get(task.key);
    612             if (tv == null) {
    613                 tv = mViewPool.pickUpViewFromPool(task, task);
    614                 if (task.isFreeformTask()) {
    615                     updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
    616                 } else {
    617                     if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
    618                         updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
    619                                 AnimationProps.IMMEDIATE);
    620                     } else {
    621                         updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
    622                                 AnimationProps.IMMEDIATE);
    623                     }
    624                 }
    625             } else {
    626                 // Reattach it in the right z order
    627                 final int taskIndex = mStack.indexOfStackTask(task);
    628                 final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
    629                 if (insertIndex != getTaskViews().indexOf(tv)){
    630                     detachViewFromParent(tv);
    631                     attachViewToParent(tv, insertIndex, tv.getLayoutParams());
    632                     updateTaskViewsList();
    633                 }
    634             }
    635         }
    636 
    637         // Update the focus if the previous focused task was returned to the view pool
    638         if (lastFocusedTaskIndex != -1) {
    639             int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
    640                     ? visibleTaskRange[1]
    641                     : visibleTaskRange[0];
    642             setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
    643                     true /* requestViewFocus */);
    644             TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
    645             if (focusedTaskView != null) {
    646                 focusedTaskView.requestAccessibilityFocus();
    647             }
    648         }
    649     }
    650 
    651     /**
    652      * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
    653      */
    654     public void relayoutTaskViews(AnimationProps animation) {
    655         relayoutTaskViews(animation, null /* animationOverrides */,
    656                 false /* ignoreTaskOverrides */);
    657     }
    658 
    659     /**
    660      * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
    661      * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
    662      * animations that are current running on those task views, and will ensure that the children
    663      * {@link TaskView}s will match the set of visible tasks in the stack.  If a {@link Task} has
    664      * an animation provided in {@param animationOverrides}, that will be used instead.
    665      */
    666     private void relayoutTaskViews(AnimationProps animation,
    667             ArrayMap<Task, AnimationProps> animationOverrides,
    668             boolean ignoreTaskOverrides) {
    669         // If we had a deferred animation, cancel that
    670         cancelDeferredTaskViewLayoutAnimation();
    671 
    672         // Synchronize the current set of TaskViews
    673         bindVisibleTaskViews(mStackScroller.getStackScroll(),
    674                 ignoreTaskOverrides /* ignoreTaskOverrides */);
    675 
    676         // Animate them to their final transforms with the given animation
    677         List<TaskView> taskViews = getTaskViews();
    678         int taskViewCount = taskViews.size();
    679         for (int i = 0; i < taskViewCount; i++) {
    680             TaskView tv = taskViews.get(i);
    681             Task task = tv.getTask();
    682 
    683             if (mIgnoreTasks.contains(task.key)) {
    684                 continue;
    685             }
    686 
    687             int taskIndex = mStack.indexOfStackTask(task);
    688             TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
    689             if (animationOverrides != null && animationOverrides.containsKey(task)) {
    690                 animation = animationOverrides.get(task);
    691             }
    692 
    693             updateTaskViewToTransform(tv, transform, animation);
    694         }
    695     }
    696 
    697     /**
    698      * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
    699      */
    700     void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
    701         mDeferredTaskViewLayoutAnimation = animation;
    702         invalidate();
    703     }
    704 
    705     /**
    706      * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
    707      * given set of {@link AnimationProps} properties.
    708      */
    709     public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
    710             AnimationProps animation) {
    711         if (taskView.isAnimatingTo(transform)) {
    712             return;
    713         }
    714         taskView.cancelTransformAnimation();
    715         taskView.updateViewPropertiesToTaskTransform(transform, animation,
    716                 mRequestUpdateClippingListener);
    717     }
    718 
    719     /**
    720      * Returns the current task transforms of all tasks, falling back to the stack layout if there
    721      * is no {@link TaskView} for the task.
    722      */
    723     public void getCurrentTaskTransforms(ArrayList<Task> tasks,
    724             ArrayList<TaskViewTransform> transformsOut) {
    725         Utilities.matchTaskListSize(tasks, transformsOut);
    726         int focusState = mLayoutAlgorithm.getFocusState();
    727         for (int i = tasks.size() - 1; i >= 0; i--) {
    728             Task task = tasks.get(i);
    729             TaskViewTransform transform = transformsOut.get(i);
    730             TaskView tv = getChildViewForTask(task);
    731             if (tv != null) {
    732                 transform.fillIn(tv);
    733             } else {
    734                 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
    735                         focusState, transform, null, true /* forceUpdate */,
    736                         false /* ignoreTaskOverrides */);
    737             }
    738             transform.visible = true;
    739         }
    740     }
    741 
    742     /**
    743      * Returns the task transforms for all the tasks in the stack if the stack was at the given
    744      * {@param stackScroll} and {@param focusState}.
    745      */
    746     public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
    747             boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
    748         Utilities.matchTaskListSize(tasks, transformsOut);
    749         for (int i = tasks.size() - 1; i >= 0; i--) {
    750             Task task = tasks.get(i);
    751             TaskViewTransform transform = transformsOut.get(i);
    752             mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
    753                     true /* forceUpdate */, ignoreTaskOverrides);
    754             transform.visible = true;
    755         }
    756     }
    757 
    758     /**
    759      * Cancels the next deferred task view layout.
    760      */
    761     void cancelDeferredTaskViewLayoutAnimation() {
    762         mDeferredTaskViewLayoutAnimation = null;
    763     }
    764 
    765     /**
    766      * Cancels all {@link TaskView} animations.
    767      */
    768     void cancelAllTaskViewAnimations() {
    769         List<TaskView> taskViews = getTaskViews();
    770         for (int i = taskViews.size() - 1; i >= 0; i--) {
    771             final TaskView tv = taskViews.get(i);
    772             if (!mIgnoreTasks.contains(tv.getTask().key)) {
    773                 tv.cancelTransformAnimation();
    774             }
    775         }
    776     }
    777 
    778     /**
    779      * Updates the clip for each of the task views from back to front.
    780      */
    781     private void clipTaskViews() {
    782         // Update the clip on each task child
    783         List<TaskView> taskViews = getTaskViews();
    784         TaskView tmpTv = null;
    785         TaskView prevVisibleTv = null;
    786         int taskViewCount = taskViews.size();
    787         for (int i = 0; i < taskViewCount; i++) {
    788             TaskView tv = taskViews.get(i);
    789             TaskView frontTv = null;
    790             int clipBottom = 0;
    791 
    792             if (isIgnoredTask(tv.getTask())) {
    793                 // For each of the ignore tasks, update the translationZ of its TaskView to be
    794                 // between the translationZ of the tasks immediately underneath it
    795                 if (prevVisibleTv != null) {
    796                     tv.setTranslationZ(Math.max(tv.getTranslationZ(),
    797                             prevVisibleTv.getTranslationZ() + 0.1f));
    798                 }
    799             }
    800 
    801             if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
    802                 // Find the next view to clip against
    803                 for (int j = i + 1; j < taskViewCount; j++) {
    804                     tmpTv = taskViews.get(j);
    805 
    806                     if (tmpTv.shouldClipViewInStack()) {
    807                         frontTv = tmpTv;
    808                         break;
    809                     }
    810                 }
    811 
    812                 // Clip against the next view, this is just an approximation since we are
    813                 // stacked and we can make assumptions about the visibility of the this
    814                 // task relative to the ones in front of it.
    815                 if (frontTv != null) {
    816                     float taskBottom = tv.getBottom();
    817                     float frontTaskTop = frontTv.getTop();
    818                     if (frontTaskTop < taskBottom) {
    819                         // Map the stack view space coordinate (the rects) to view space
    820                         clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
    821                     }
    822                 }
    823             }
    824             tv.getViewBounds().setClipBottom(clipBottom);
    825             tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
    826             prevVisibleTv = tv;
    827         }
    828         mTaskViewsClipDirty = false;
    829     }
    830 
    831     /**
    832      * Updates the layout algorithm min and max virtual scroll bounds.
    833      */
    834    public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
    835         // Compute the min and max scroll values
    836         mLayoutAlgorithm.update(mStack, mIgnoreTasks);
    837 
    838         // Update the freeform workspace background
    839         SystemServicesProxy ssp = Recents.getSystemServices();
    840         if (ssp.hasFreeformWorkspaceSupport()) {
    841             mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
    842             mFreeformWorkspaceBackground.setBounds(mTmpRect);
    843         }
    844 
    845         if (boundScrollToNewMinMax) {
    846             mStackScroller.boundScroll();
    847         }
    848     }
    849 
    850     /**
    851      * Updates the stack layout to its stable places.
    852      */
    853     private void updateLayoutToStableBounds() {
    854         mWindowRect.set(mStableWindowRect);
    855         mStackBounds.set(mStableStackBounds);
    856         mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
    857         mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
    858                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
    859         updateLayoutAlgorithm(true /* boundScroll */);
    860     }
    861 
    862     /** Returns the scroller. */
    863     public TaskStackViewScroller getScroller() {
    864         return mStackScroller;
    865     }
    866 
    867     /**
    868      * Sets the focused task to the provided (bounded taskIndex).
    869      *
    870      * @return whether or not the stack will scroll as a part of this focus change
    871      */
    872     private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
    873             final boolean requestViewFocus) {
    874         return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
    875     }
    876 
    877     /**
    878      * Sets the focused task to the provided (bounded focusTaskIndex).
    879      *
    880      * @return whether or not the stack will scroll as a part of this focus change
    881      */
    882     private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
    883             boolean requestViewFocus, int timerIndicatorDuration) {
    884         // Find the next task to focus
    885         int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
    886                 Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
    887         final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
    888                 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
    889 
    890         // Reset the last focused task state if changed
    891         if (mFocusedTask != null) {
    892             // Cancel the timer indicator, if applicable
    893             if (timerIndicatorDuration > 0) {
    894                 final TaskView tv = getChildViewForTask(mFocusedTask);
    895                 if (tv != null) {
    896                     tv.getHeaderView().cancelFocusTimerIndicator();
    897                 }
    898             }
    899 
    900             resetFocusedTask(mFocusedTask);
    901         }
    902 
    903         boolean willScroll = false;
    904         mFocusedTask = newFocusedTask;
    905 
    906         if (newFocusedTask != null) {
    907             // Start the timer indicator, if applicable
    908             if (timerIndicatorDuration > 0) {
    909                 final TaskView tv = getChildViewForTask(mFocusedTask);
    910                 if (tv != null) {
    911                     tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
    912                 } else {
    913                     // The view is null; set a flag for later
    914                     mStartTimerIndicatorDuration = timerIndicatorDuration;
    915                 }
    916             }
    917 
    918             if (scrollToTask) {
    919                 // Cancel any running enter animations at this point when we scroll or change focus
    920                 if (!mEnterAnimationComplete) {
    921                     cancelAllTaskViewAnimations();
    922                 }
    923 
    924                 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
    925                 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
    926                         requestViewFocus);
    927             } else {
    928                 // Focus the task view
    929                 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
    930                 if (newFocusedTaskView != null) {
    931                     newFocusedTaskView.setFocusedState(true, requestViewFocus);
    932                 }
    933             }
    934         }
    935         return willScroll;
    936     }
    937 
    938     /**
    939      * Sets the focused task relative to the currently focused task.
    940      *
    941      * @param forward whether to go to the next task in the stack (along the curve) or the previous
    942      * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
    943      *                       if the currently focused task is not a stack task, will set the focus
    944      *                       to the first visible stack task
    945      * @param animated determines whether to actually draw the highlight along with the change in
    946      *                            focus.
    947      */
    948     public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
    949         setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
    950     }
    951 
    952     /**
    953      * Sets the focused task relative to the currently focused task.
    954      *
    955      * @param forward whether to go to the next task in the stack (along the curve) or the previous
    956      * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
    957      *                       if the currently focused task is not a stack task, will set the focus
    958      *                       to the first visible stack task
    959      * @param animated determines whether to actually draw the highlight along with the change in
    960      *                            focus.
    961      * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
    962      *                               happens.
    963      * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
    964      */
    965     public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
    966                                        boolean cancelWindowAnimations, int timerIndicatorDuration) {
    967         Task focusedTask = getFocusedTask();
    968         int newIndex = mStack.indexOfStackTask(focusedTask);
    969         if (focusedTask != null) {
    970             if (stackTasksOnly) {
    971                 List<Task> tasks =  mStack.getStackTasks();
    972                 if (focusedTask.isFreeformTask()) {
    973                     // Try and focus the front most stack task
    974                     TaskView tv = getFrontMostTaskView(stackTasksOnly);
    975                     if (tv != null) {
    976                         newIndex = mStack.indexOfStackTask(tv.getTask());
    977                     }
    978                 } else {
    979                     // Try the next task if it is a stack task
    980                     int tmpNewIndex = newIndex + (forward ? -1 : 1);
    981                     if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
    982                         Task t = tasks.get(tmpNewIndex);
    983                         if (!t.isFreeformTask()) {
    984                             newIndex = tmpNewIndex;
    985                         }
    986                     }
    987                 }
    988             } else {
    989                 // No restrictions, lets just move to the new task (looping forward/backwards if
    990                 // necessary)
    991                 int taskCount = mStack.getTaskCount();
    992                 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
    993             }
    994         } else {
    995             // We don't have a focused task
    996             float stackScroll = mStackScroller.getStackScroll();
    997             ArrayList<Task> tasks = mStack.getStackTasks();
    998             int taskCount = tasks.size();
    999             if (forward) {
   1000                 // Walk backwards and focus the next task smaller than the current stack scroll
   1001                 for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
   1002                     float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
   1003                     if (Float.compare(taskP, stackScroll) <= 0) {
   1004                         break;
   1005                     }
   1006                 }
   1007             } else {
   1008                 // Walk forwards and focus the next task larger than the current stack scroll
   1009                 for (newIndex = 0; newIndex < taskCount; newIndex++) {
   1010                     float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
   1011                     if (Float.compare(taskP, stackScroll) >= 0) {
   1012                         break;
   1013                     }
   1014                 }
   1015             }
   1016         }
   1017         if (newIndex != -1) {
   1018             boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
   1019                     true /* requestViewFocus */, timerIndicatorDuration);
   1020             if (willScroll && cancelWindowAnimations) {
   1021                 // As we iterate to the next/previous task, cancel any current/lagging window
   1022                 // transition animations
   1023                 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
   1024             }
   1025         }
   1026     }
   1027 
   1028     /**
   1029      * Resets the focused task.
   1030      */
   1031     void resetFocusedTask(Task task) {
   1032         if (task != null) {
   1033             TaskView tv = getChildViewForTask(task);
   1034             if (tv != null) {
   1035                 tv.setFocusedState(false, false /* requestViewFocus */);
   1036             }
   1037         }
   1038         mFocusedTask = null;
   1039     }
   1040 
   1041     /**
   1042      * Returns the focused task.
   1043      */
   1044     Task getFocusedTask() {
   1045         return mFocusedTask;
   1046     }
   1047 
   1048     /**
   1049      * Returns the accessibility focused task.
   1050      */
   1051     Task getAccessibilityFocusedTask() {
   1052         List<TaskView> taskViews = getTaskViews();
   1053         int taskViewCount = taskViews.size();
   1054         for (int i = 0; i < taskViewCount; i++) {
   1055             TaskView tv = taskViews.get(i);
   1056             if (Utilities.isDescendentAccessibilityFocused(tv)) {
   1057                 return tv.getTask();
   1058             }
   1059         }
   1060         TaskView frontTv = getFrontMostTaskView(true /* stackTasksOnly */);
   1061         if (frontTv != null) {
   1062             return frontTv.getTask();
   1063         }
   1064         return null;
   1065     }
   1066 
   1067     @Override
   1068     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   1069         super.onInitializeAccessibilityEvent(event);
   1070         List<TaskView> taskViews = getTaskViews();
   1071         int taskViewCount = taskViews.size();
   1072         if (taskViewCount > 0) {
   1073             TaskView backMostTask = taskViews.get(0);
   1074             TaskView frontMostTask = taskViews.get(taskViewCount - 1);
   1075             event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
   1076             event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
   1077             event.setContentDescription(frontMostTask.getTask().title);
   1078         }
   1079         event.setItemCount(mStack.getTaskCount());
   1080 
   1081         int stackHeight = mLayoutAlgorithm.mStackRect.height();
   1082         event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
   1083         event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
   1084     }
   1085 
   1086     @Override
   1087     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
   1088         super.onInitializeAccessibilityNodeInfo(info);
   1089         List<TaskView> taskViews = getTaskViews();
   1090         int taskViewCount = taskViews.size();
   1091         if (taskViewCount > 1) {
   1092             // Find the accessibility focused task
   1093             Task focusedTask = getAccessibilityFocusedTask();
   1094             info.setScrollable(true);
   1095             int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);
   1096             if (focusedTaskIndex > 0) {
   1097                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
   1098             }
   1099             if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
   1100                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
   1101             }
   1102         }
   1103     }
   1104 
   1105     @Override
   1106     public CharSequence getAccessibilityClassName() {
   1107         return ScrollView.class.getName();
   1108     }
   1109 
   1110     @Override
   1111     public boolean performAccessibilityAction(int action, Bundle arguments) {
   1112         if (super.performAccessibilityAction(action, arguments)) {
   1113             return true;
   1114         }
   1115         Task focusedTask = getAccessibilityFocusedTask();
   1116         int taskIndex = mStack.indexOfStackTask(focusedTask);
   1117         if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
   1118             switch (action) {
   1119                 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
   1120                     setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
   1121                             0);
   1122                     return true;
   1123                 }
   1124                 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
   1125                     setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
   1126                             0);
   1127                     return true;
   1128                 }
   1129             }
   1130         }
   1131         return false;
   1132     }
   1133 
   1134     @Override
   1135     public boolean onInterceptTouchEvent(MotionEvent ev) {
   1136         return mTouchHandler.onInterceptTouchEvent(ev);
   1137     }
   1138 
   1139     @Override
   1140     public boolean onTouchEvent(MotionEvent ev) {
   1141         return mTouchHandler.onTouchEvent(ev);
   1142     }
   1143 
   1144     @Override
   1145     public boolean onGenericMotionEvent(MotionEvent ev) {
   1146         return mTouchHandler.onGenericMotionEvent(ev);
   1147     }
   1148 
   1149     @Override
   1150     public void computeScroll() {
   1151         if (mStackScroller.computeScroll()) {
   1152             // Notify accessibility
   1153             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
   1154         }
   1155         if (mDeferredTaskViewLayoutAnimation != null) {
   1156             relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
   1157             mTaskViewsClipDirty = true;
   1158             mDeferredTaskViewLayoutAnimation = null;
   1159         }
   1160         if (mTaskViewsClipDirty) {
   1161             clipTaskViews();
   1162         }
   1163     }
   1164 
   1165     /**
   1166      * Computes the maximum number of visible tasks and thumbnails. Requires that
   1167      * updateLayoutForStack() is called first.
   1168      */
   1169     public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
   1170         return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
   1171     }
   1172 
   1173     /**
   1174      * Updates the system insets.
   1175      */
   1176     public void setSystemInsets(Rect systemInsets) {
   1177         boolean changed = false;
   1178         changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
   1179         changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
   1180         if (changed) {
   1181             requestLayout();
   1182         }
   1183     }
   1184 
   1185     /**
   1186      * This is called with the full window width and height to allow stack view children to
   1187      * perform the full screen transition down.
   1188      */
   1189     @Override
   1190     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   1191         mInMeasureLayout = true;
   1192         int width = MeasureSpec.getSize(widthMeasureSpec);
   1193         int height = MeasureSpec.getSize(heightMeasureSpec);
   1194 
   1195         // Update the stable stack bounds, but only update the current stack bounds if the stable
   1196         // bounds have changed.  This is because we may get spurious measures while dragging where
   1197         // our current stack bounds reflect the target drop region.
   1198         mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
   1199                 mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
   1200                 mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
   1201         if (!mTmpRect.equals(mStableStackBounds)) {
   1202             mStableStackBounds.set(mTmpRect);
   1203             mStackBounds.set(mTmpRect);
   1204             mStableWindowRect.set(0, 0, width, height);
   1205             mWindowRect.set(0, 0, width, height);
   1206         }
   1207 
   1208         // Compute the rects in the stack algorithm
   1209         mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds,
   1210                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
   1211         mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
   1212                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
   1213         updateLayoutAlgorithm(false /* boundScroll */);
   1214 
   1215         // If this is the first layout, then scroll to the front of the stack, then update the
   1216         // TaskViews with the stack so that we can lay them out
   1217         boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
   1218                 && mResetToInitialStateWhenResized;
   1219         if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE
   1220                 || resetToInitialState) {
   1221             if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
   1222                 updateToInitialState();
   1223                 mResetToInitialStateWhenResized = false;
   1224             }
   1225             if (!mAwaitingFirstLayout) {
   1226                 mInitialState = INITIAL_STATE_UPDATE_NONE;
   1227             }
   1228         }
   1229         // If we got the launch-next event before the first layout pass, then re-send it after the
   1230         // initial state has been updated
   1231         if (mLaunchNextAfterFirstMeasure) {
   1232             mLaunchNextAfterFirstMeasure = false;
   1233             EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
   1234         }
   1235 
   1236         // Rebind all the views, including the ignore ones
   1237         bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
   1238 
   1239         // Measure each of the TaskViews
   1240         mTmpTaskViews.clear();
   1241         mTmpTaskViews.addAll(getTaskViews());
   1242         mTmpTaskViews.addAll(mViewPool.getViews());
   1243         int taskViewCount = mTmpTaskViews.size();
   1244         for (int i = 0; i < taskViewCount; i++) {
   1245             measureTaskView(mTmpTaskViews.get(i));
   1246         }
   1247 
   1248         setMeasuredDimension(width, height);
   1249         mLastWidth = width;
   1250         mLastHeight = height;
   1251         mInMeasureLayout = false;
   1252     }
   1253 
   1254     /**
   1255      * Measures a TaskView.
   1256      */
   1257     private void measureTaskView(TaskView tv) {
   1258         Rect padding = new Rect();
   1259         if (tv.getBackground() != null) {
   1260             tv.getBackground().getPadding(padding);
   1261         }
   1262         mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
   1263         mTmpRect.union(mLayoutAlgorithm.mTaskRect);
   1264         tv.measure(
   1265                 MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
   1266                         MeasureSpec.EXACTLY),
   1267                 MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
   1268                         MeasureSpec.EXACTLY));
   1269     }
   1270 
   1271     @Override
   1272     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   1273         // Layout each of the TaskViews
   1274         mTmpTaskViews.clear();
   1275         mTmpTaskViews.addAll(getTaskViews());
   1276         mTmpTaskViews.addAll(mViewPool.getViews());
   1277         int taskViewCount = mTmpTaskViews.size();
   1278         for (int i = 0; i < taskViewCount; i++) {
   1279             layoutTaskView(changed, mTmpTaskViews.get(i));
   1280         }
   1281 
   1282         if (changed) {
   1283             if (mStackScroller.isScrollOutOfBounds()) {
   1284                 mStackScroller.boundScroll();
   1285             }
   1286         }
   1287 
   1288         // Relayout all of the task views including the ignored ones
   1289         relayoutTaskViews(AnimationProps.IMMEDIATE);
   1290         clipTaskViews();
   1291 
   1292         if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
   1293             mAwaitingFirstLayout = false;
   1294             mInitialState = INITIAL_STATE_UPDATE_NONE;
   1295             onFirstLayout();
   1296         }
   1297     }
   1298 
   1299     /**
   1300      * Lays out a TaskView.
   1301      */
   1302     private void layoutTaskView(boolean changed, TaskView tv) {
   1303         if (changed) {
   1304             Rect padding = new Rect();
   1305             if (tv.getBackground() != null) {
   1306                 tv.getBackground().getPadding(padding);
   1307             }
   1308             mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
   1309             mTmpRect.union(mLayoutAlgorithm.mTaskRect);
   1310             tv.cancelTransformAnimation();
   1311             tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
   1312                     mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
   1313         } else {
   1314             // If the layout has not changed, then just lay it out again in-place
   1315             tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
   1316         }
   1317     }
   1318 
   1319     /** Handler for the first layout. */
   1320     void onFirstLayout() {
   1321         // Setup the view for the enter animation
   1322         mAnimationHelper.prepareForEnterAnimation();
   1323 
   1324         // Animate in the freeform workspace
   1325         int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
   1326         animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
   1327                 Interpolators.FAST_OUT_SLOW_IN));
   1328 
   1329         // Set the task focused state without requesting view focus, and leave the focus animations
   1330         // until after the enter-animation
   1331         RecentsConfiguration config = Recents.getConfiguration();
   1332         RecentsActivityLaunchState launchState = config.getLaunchState();
   1333         int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
   1334         if (focusedTaskIndex != -1) {
   1335             setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
   1336                     false /* requestViewFocus */);
   1337         }
   1338 
   1339         // Update the stack action button visibility
   1340         if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
   1341                 mStack.getTaskCount() > 0) {
   1342             EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
   1343         } else {
   1344             EventBus.getDefault().send(new HideStackActionButtonEvent());
   1345         }
   1346     }
   1347 
   1348     public boolean isTouchPointInView(float x, float y, TaskView tv) {
   1349         mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
   1350         mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
   1351         return mTmpRect.contains((int) x, (int) y);
   1352     }
   1353 
   1354     /**
   1355      * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
   1356      * calculating the scroll position before and after a layout change.
   1357      */
   1358     public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
   1359         for (int i = tasks.size() - 1; i >= 0; i--) {
   1360             Task task = tasks.get(i);
   1361 
   1362             // Ignore deleting tasks
   1363             if (isIgnoredTask(task)) {
   1364                 if (i == tasks.size() - 1) {
   1365                     isFrontMostTask.value = true;
   1366                 }
   1367                 continue;
   1368             }
   1369             return task;
   1370         }
   1371         return null;
   1372     }
   1373 
   1374     @Override
   1375     protected void onDraw(Canvas canvas) {
   1376         super.onDraw(canvas);
   1377 
   1378         // Draw the freeform workspace background
   1379         SystemServicesProxy ssp = Recents.getSystemServices();
   1380         if (ssp.hasFreeformWorkspaceSupport()) {
   1381             if (mFreeformWorkspaceBackground.getAlpha() > 0) {
   1382                 mFreeformWorkspaceBackground.draw(canvas);
   1383             }
   1384         }
   1385     }
   1386 
   1387     @Override
   1388     protected boolean verifyDrawable(Drawable who) {
   1389         if (who == mFreeformWorkspaceBackground) {
   1390             return true;
   1391         }
   1392         return super.verifyDrawable(who);
   1393     }
   1394 
   1395     /**
   1396      * Launches the freeform tasks.
   1397      */
   1398     public boolean launchFreeformTasks() {
   1399         ArrayList<Task> tasks = mStack.getFreeformTasks();
   1400         if (!tasks.isEmpty()) {
   1401             Task frontTask = tasks.get(tasks.size() - 1);
   1402             if (frontTask != null && frontTask.isFreeformTask()) {
   1403                 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
   1404                         frontTask, null, INVALID_STACK_ID, false));
   1405                 return true;
   1406             }
   1407         }
   1408         return false;
   1409     }
   1410 
   1411     /**** TaskStackCallbacks Implementation ****/
   1412 
   1413     @Override
   1414     public void onStackTaskAdded(TaskStack stack, Task newTask) {
   1415         // Update the min/max scroll and animate other task views into their new positions
   1416         updateLayoutAlgorithm(true /* boundScroll */);
   1417 
   1418         // Animate all the tasks into place
   1419         relayoutTaskViews(mAwaitingFirstLayout
   1420                 ? AnimationProps.IMMEDIATE
   1421                 : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
   1422     }
   1423 
   1424     /**
   1425      * We expect that the {@link TaskView} associated with the removed task is already hidden.
   1426      */
   1427     @Override
   1428     public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
   1429             AnimationProps animation, boolean fromDockGesture) {
   1430         if (mFocusedTask == removedTask) {
   1431             resetFocusedTask(removedTask);
   1432         }
   1433 
   1434         // Remove the view associated with this task, we can't rely on updateTransforms
   1435         // to work here because the task is no longer in the list
   1436         TaskView tv = getChildViewForTask(removedTask);
   1437         if (tv != null) {
   1438             mViewPool.returnViewToPool(tv);
   1439         }
   1440 
   1441         // Remove the task from the ignored set
   1442         removeIgnoreTask(removedTask);
   1443 
   1444         // If requested, relayout with the given animation
   1445         if (animation != null) {
   1446             updateLayoutAlgorithm(true /* boundScroll */);
   1447             relayoutTaskViews(animation);
   1448         }
   1449 
   1450         // Update the new front most task's action button
   1451         if (mScreenPinningEnabled && newFrontMostTask != null) {
   1452             TaskView frontTv = getChildViewForTask(newFrontMostTask);
   1453             if (frontTv != null) {
   1454                 frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
   1455             }
   1456         }
   1457 
   1458         // If there are no remaining tasks, then just close recents
   1459         if (mStack.getTaskCount() == 0) {
   1460             EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
   1461                     ? R.string.recents_empty_message
   1462                     : R.string.recents_empty_message_dismissed_all));
   1463         }
   1464     }
   1465 
   1466     @Override
   1467     public void onStackTasksRemoved(TaskStack stack) {
   1468         // Reset the focused task
   1469         resetFocusedTask(getFocusedTask());
   1470 
   1471         // Return all the views to the pool
   1472         List<TaskView> taskViews = new ArrayList<>();
   1473         taskViews.addAll(getTaskViews());
   1474         for (int i = taskViews.size() - 1; i >= 0; i--) {
   1475             mViewPool.returnViewToPool(taskViews.get(i));
   1476         }
   1477 
   1478         // Remove all the ignore tasks
   1479         mIgnoreTasks.clear();
   1480 
   1481         // If there are no remaining tasks, then just close recents
   1482         EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
   1483                 R.string.recents_empty_message_dismissed_all));
   1484     }
   1485 
   1486     @Override
   1487     public void onStackTasksUpdated(TaskStack stack) {
   1488         // Update the layout and immediately layout
   1489         updateLayoutAlgorithm(false /* boundScroll */);
   1490         relayoutTaskViews(AnimationProps.IMMEDIATE);
   1491 
   1492         // Rebind all the task views.  This will not trigger new resources to be loaded
   1493         // unless they have actually changed
   1494         List<TaskView> taskViews = getTaskViews();
   1495         int taskViewCount = taskViews.size();
   1496         for (int i = 0; i < taskViewCount; i++) {
   1497             TaskView tv = taskViews.get(i);
   1498             bindTaskView(tv, tv.getTask());
   1499         }
   1500     }
   1501 
   1502     /**** ViewPoolConsumer Implementation ****/
   1503 
   1504     @Override
   1505     public TaskView createView(Context context) {
   1506         return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
   1507     }
   1508 
   1509     @Override
   1510     public void onReturnViewToPool(TaskView tv) {
   1511         final Task task = tv.getTask();
   1512 
   1513         // Unbind the task from the task view
   1514         unbindTaskView(tv, task);
   1515 
   1516         // Reset the view properties and view state
   1517         tv.clearAccessibilityFocus();
   1518         tv.resetViewProperties();
   1519         tv.setFocusedState(false, false /* requestViewFocus */);
   1520         tv.setClipViewInStack(false);
   1521         if (mScreenPinningEnabled) {
   1522             tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
   1523         }
   1524 
   1525         // Detach the view from the hierarchy
   1526         detachViewFromParent(tv);
   1527         // Update the task views list after removing the task view
   1528         updateTaskViewsList();
   1529     }
   1530 
   1531     @Override
   1532     public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
   1533         // Find the index where this task should be placed in the stack
   1534         int taskIndex = mStack.indexOfStackTask(task);
   1535         int insertIndex = findTaskViewInsertIndex(task, taskIndex);
   1536 
   1537         // Add/attach the view to the hierarchy
   1538         if (isNewView) {
   1539             if (mInMeasureLayout) {
   1540                 // If we are measuring the layout, then just add the view normally as it will be
   1541                 // laid out during the layout pass
   1542                 addView(tv, insertIndex);
   1543             } else {
   1544                 // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
   1545                 // pass, and we should layout the new child ourselves
   1546                 ViewGroup.LayoutParams params = tv.getLayoutParams();
   1547                 if (params == null) {
   1548                     params = generateDefaultLayoutParams();
   1549                 }
   1550                 addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
   1551                 measureTaskView(tv);
   1552                 layoutTaskView(true /* changed */, tv);
   1553             }
   1554         } else {
   1555             attachViewToParent(tv, insertIndex, tv.getLayoutParams());
   1556         }
   1557         // Update the task views list after adding the new task view
   1558         updateTaskViewsList();
   1559 
   1560         // Bind the task view to the new task
   1561         bindTaskView(tv, task);
   1562 
   1563         // If the doze trigger has already fired, then update the state for this task view
   1564         if (mUIDozeTrigger.isAsleep()) {
   1565             tv.setNoUserInteractionState();
   1566         }
   1567 
   1568         // Set the new state for this view, including the callbacks and view clipping
   1569         tv.setCallbacks(this);
   1570         tv.setTouchEnabled(true);
   1571         tv.setClipViewInStack(true);
   1572         if (mFocusedTask == task) {
   1573             tv.setFocusedState(true, false /* requestViewFocus */);
   1574             if (mStartTimerIndicatorDuration > 0) {
   1575                 // The timer indicator couldn't be started before, so start it now
   1576                 tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
   1577                 mStartTimerIndicatorDuration = 0;
   1578             }
   1579         }
   1580 
   1581         // Restore the action button visibility if it is the front most task view
   1582         if (mScreenPinningEnabled && tv.getTask() ==
   1583                 mStack.getStackFrontMostTask(false /* includeFreeform */)) {
   1584             tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
   1585         }
   1586     }
   1587 
   1588     @Override
   1589     public boolean hasPreferredData(TaskView tv, Task preferredData) {
   1590         return (tv.getTask() == preferredData);
   1591     }
   1592 
   1593     private void bindTaskView(TaskView tv, Task task) {
   1594         // Rebind the task and request that this task's data be filled into the TaskView
   1595         tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
   1596 
   1597         // Load the task data
   1598         Recents.getTaskLoader().loadTaskData(task);
   1599     }
   1600 
   1601     private void unbindTaskView(TaskView tv, Task task) {
   1602         // Report that this task's data is no longer being used
   1603         Recents.getTaskLoader().unloadTaskData(task);
   1604     }
   1605 
   1606     /**** TaskViewCallbacks Implementation ****/
   1607 
   1608     @Override
   1609     public void onTaskViewClipStateChanged(TaskView tv) {
   1610         if (!mTaskViewsClipDirty) {
   1611             mTaskViewsClipDirty = true;
   1612             invalidate();
   1613         }
   1614     }
   1615 
   1616     /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
   1617 
   1618     @Override
   1619     public void onFocusStateChanged(int prevFocusState, int curFocusState) {
   1620         if (mDeferredTaskViewLayoutAnimation == null) {
   1621             mUIDozeTrigger.poke();
   1622             relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
   1623         }
   1624     }
   1625 
   1626     /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
   1627 
   1628     @Override
   1629     public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
   1630         mUIDozeTrigger.poke();
   1631         if (animation != null) {
   1632             relayoutTaskViewsOnNextFrame(animation);
   1633         }
   1634 
   1635         if (mEnterAnimationComplete) {
   1636             if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
   1637                     curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
   1638                     mStack.getTaskCount() > 0) {
   1639                 EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
   1640             } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
   1641                     curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
   1642                 EventBus.getDefault().send(new HideStackActionButtonEvent());
   1643             }
   1644         }
   1645     }
   1646 
   1647     /**** EventBus Events ****/
   1648 
   1649     public final void onBusEvent(PackagesChangedEvent event) {
   1650         // Compute which components need to be removed
   1651         ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
   1652                 event.packageName, event.userId);
   1653 
   1654         // For other tasks, just remove them directly if they no longer exist
   1655         ArrayList<Task> tasks = mStack.getStackTasks();
   1656         for (int i = tasks.size() - 1; i >= 0; i--) {
   1657             final Task t = tasks.get(i);
   1658             if (removedComponents.contains(t.key.getComponent())) {
   1659                 final TaskView tv = getChildViewForTask(t);
   1660                 if (tv != null) {
   1661                     // For visible children, defer removing the task until after the animation
   1662                     tv.dismissTask();
   1663                 } else {
   1664                     // Otherwise, remove the task from the stack immediately
   1665                     mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
   1666                 }
   1667             }
   1668         }
   1669     }
   1670 
   1671     public final void onBusEvent(LaunchTaskEvent event) {
   1672         // Cancel any doze triggers once a task is launched
   1673         mUIDozeTrigger.stopDozing();
   1674     }
   1675 
   1676     public final void onBusEvent(LaunchNextTaskRequestEvent event) {
   1677         if (mAwaitingFirstLayout) {
   1678             mLaunchNextAfterFirstMeasure = true;
   1679             return;
   1680         }
   1681 
   1682         int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
   1683         if (launchTaskIndex != -1) {
   1684             launchTaskIndex = Math.max(0, launchTaskIndex - 1);
   1685         } else {
   1686             launchTaskIndex = mStack.getTaskCount() - 1;
   1687         }
   1688         if (launchTaskIndex != -1) {
   1689             // Stop all animations
   1690             cancelAllTaskViewAnimations();
   1691 
   1692             final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
   1693             float curScroll = mStackScroller.getStackScroll();
   1694             float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(launchTask);
   1695             float absScrollDiff = Math.abs(targetScroll - curScroll);
   1696             if (getChildViewForTask(launchTask) == null || absScrollDiff > 0.35f) {
   1697                 int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
   1698                         absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
   1699                 mStackScroller.animateScroll(targetScroll,
   1700                         duration, new Runnable() {
   1701                             @Override
   1702                             public void run() {
   1703                                 EventBus.getDefault().send(new LaunchTaskEvent(
   1704                                         getChildViewForTask(launchTask), launchTask, null,
   1705                                         INVALID_STACK_ID, false /* screenPinningRequested */));
   1706                             }
   1707                         });
   1708             } else {
   1709                 EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
   1710                         launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
   1711             }
   1712 
   1713             MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
   1714                     launchTask.key.getComponent().toString());
   1715         } else if (mStack.getTaskCount() == 0) {
   1716             // If there are no tasks, then just hide recents back to home.
   1717             EventBus.getDefault().send(new HideRecentsEvent(false, true));
   1718         }
   1719     }
   1720 
   1721     public final void onBusEvent(LaunchTaskStartedEvent event) {
   1722         mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
   1723                 event.getAnimationTrigger());
   1724     }
   1725 
   1726     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
   1727         // Stop any scrolling
   1728         mTouchHandler.cancelNonDismissTaskAnimations();
   1729         mStackScroller.stopScroller();
   1730         mStackScroller.stopBoundScrollAnimation();
   1731         cancelDeferredTaskViewLayoutAnimation();
   1732 
   1733         // Start the task animations
   1734         mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
   1735 
   1736         // Dismiss the freeform workspace background
   1737         int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
   1738         animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
   1739                 Interpolators.FAST_OUT_SLOW_IN));
   1740     }
   1741 
   1742     public final void onBusEvent(DismissFocusedTaskViewEvent event) {
   1743         if (mFocusedTask != null) {
   1744             TaskView tv = getChildViewForTask(mFocusedTask);
   1745             if (tv != null) {
   1746                 tv.dismissTask();
   1747             }
   1748             resetFocusedTask(mFocusedTask);
   1749         }
   1750     }
   1751 
   1752     public final void onBusEvent(DismissTaskViewEvent event) {
   1753         // For visible children, defer removing the task until after the animation
   1754         mAnimationHelper.startDeleteTaskAnimation(event.taskView, event.getAnimationTrigger());
   1755     }
   1756 
   1757     public final void onBusEvent(final DismissAllTaskViewsEvent event) {
   1758         // Keep track of the tasks which will have their data removed
   1759         ArrayList<Task> tasks = new ArrayList<>(mStack.getStackTasks());
   1760         mAnimationHelper.startDeleteAllTasksAnimation(getTaskViews(), event.getAnimationTrigger());
   1761         event.addPostAnimationCallback(new Runnable() {
   1762             @Override
   1763             public void run() {
   1764                 // Announce for accessibility
   1765                 announceForAccessibility(getContext().getString(
   1766                         R.string.accessibility_recents_all_items_dismissed));
   1767 
   1768                 // Remove all tasks and delete the task data for all tasks
   1769                 mStack.removeAllTasks();
   1770                 for (int i = tasks.size() - 1; i >= 0; i--) {
   1771                     EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
   1772                 }
   1773 
   1774                 MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
   1775             }
   1776         });
   1777 
   1778     }
   1779 
   1780     public final void onBusEvent(TaskViewDismissedEvent event) {
   1781         // Announce for accessibility
   1782         announceForAccessibility(getContext().getString(
   1783                 R.string.accessibility_recents_item_dismissed, event.task.title));
   1784 
   1785         // Remove the task from the stack
   1786         mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
   1787         EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
   1788 
   1789         MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
   1790                 event.task.key.getComponent().toString());
   1791     }
   1792 
   1793     public final void onBusEvent(FocusNextTaskViewEvent event) {
   1794         // Stop any scrolling
   1795         mStackScroller.stopScroller();
   1796         mStackScroller.stopBoundScrollAnimation();
   1797 
   1798         setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
   1799                 event.timerIndicatorDuration);
   1800     }
   1801 
   1802     public final void onBusEvent(FocusPreviousTaskViewEvent event) {
   1803         // Stop any scrolling
   1804         mStackScroller.stopScroller();
   1805         mStackScroller.stopBoundScrollAnimation();
   1806 
   1807         setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
   1808     }
   1809 
   1810     public final void onBusEvent(UserInteractionEvent event) {
   1811         // Poke the doze trigger on user interaction
   1812         mUIDozeTrigger.poke();
   1813 
   1814         RecentsDebugFlags debugFlags = Recents.getDebugFlags();
   1815         if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
   1816             TaskView tv = getChildViewForTask(mFocusedTask);
   1817             if (tv != null) {
   1818                 tv.getHeaderView().cancelFocusTimerIndicator();
   1819             }
   1820         }
   1821     }
   1822 
   1823     public final void onBusEvent(DragStartEvent event) {
   1824         // Ensure that the drag task is not animated
   1825         addIgnoreTask(event.task);
   1826 
   1827         if (event.task.isFreeformTask()) {
   1828             // Animate to the front of the stack
   1829             mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
   1830         }
   1831 
   1832         // Enlarge the dragged view slightly
   1833         float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
   1834         mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
   1835                 mTmpTransform, null);
   1836         mTmpTransform.scale = finalScale;
   1837         mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
   1838         mTmpTransform.dimAlpha = 0f;
   1839         updateTaskViewToTransform(event.taskView, mTmpTransform,
   1840                 new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
   1841     }
   1842 
   1843     public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
   1844         SystemServicesProxy ssp = Recents.getSystemServices();
   1845         if (ssp.hasFreeformWorkspaceSupport()) {
   1846             event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
   1847             event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
   1848         }
   1849     }
   1850 
   1851     public final void onBusEvent(DragDropTargetChangedEvent event) {
   1852         AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
   1853                 Interpolators.FAST_OUT_SLOW_IN);
   1854         boolean ignoreTaskOverrides = false;
   1855         if (event.dropTarget instanceof TaskStack.DockState) {
   1856             // Calculate the new task stack bounds that matches the window size that Recents will
   1857             // have after the drop
   1858             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
   1859             Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
   1860             // When docked, the nav bar insets are consumed and the activity is measured without
   1861             // insets.  However, the window bounds include the insets, so we need to subtract them
   1862             // here to make them identical.
   1863             int height = getMeasuredHeight();
   1864             height -= systemInsets.bottom;
   1865             systemInsets.bottom = 0;
   1866             mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
   1867                     height, mDividerSize, systemInsets,
   1868                     mLayoutAlgorithm, getResources(), mWindowRect));
   1869             mLayoutAlgorithm.setSystemInsets(systemInsets);
   1870             mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
   1871                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
   1872             updateLayoutAlgorithm(true /* boundScroll */);
   1873             ignoreTaskOverrides = true;
   1874         } else {
   1875             // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
   1876             // task view, so add it back to the ignore set after updating the layout
   1877             removeIgnoreTask(event.task);
   1878             updateLayoutToStableBounds();
   1879             addIgnoreTask(event.task);
   1880         }
   1881         relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
   1882     }
   1883 
   1884     public final void onBusEvent(final DragEndEvent event) {
   1885         // We don't handle drops on the dock regions
   1886         if (event.dropTarget instanceof TaskStack.DockState) {
   1887             // However, we do need to reset the overrides, since the last state of this task stack
   1888             // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
   1889             mLayoutAlgorithm.clearUnfocusedTaskOverrides();
   1890             return;
   1891         }
   1892 
   1893         boolean isFreeformTask = event.task.isFreeformTask();
   1894         boolean hasChangedStacks =
   1895                 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
   1896                         (isFreeformTask && event.dropTarget == mStackDropTarget);
   1897 
   1898         if (hasChangedStacks) {
   1899             // Move the task to the right position in the stack (ie. the front of the stack if
   1900             // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
   1901             // before we update their stack ids, otherwise, the keys will have changed.
   1902             if (event.dropTarget == mFreeformWorkspaceDropTarget) {
   1903                 mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
   1904             } else if (event.dropTarget == mStackDropTarget) {
   1905                 mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
   1906             }
   1907             updateLayoutAlgorithm(true /* boundScroll */);
   1908 
   1909             // Move the task to the new stack in the system after the animation completes
   1910             event.addPostAnimationCallback(new Runnable() {
   1911                 @Override
   1912                 public void run() {
   1913                     SystemServicesProxy ssp = Recents.getSystemServices();
   1914                     ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
   1915                 }
   1916             });
   1917         }
   1918 
   1919         // Restore the task, so that relayout will apply to it below
   1920         removeIgnoreTask(event.task);
   1921 
   1922         // Convert the dragging task view back to its final layout-space rect
   1923         Utilities.setViewFrameFromTranslation(event.taskView);
   1924 
   1925         // Animate all the tasks into place
   1926         ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
   1927         animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
   1928                 Interpolators.FAST_OUT_SLOW_IN,
   1929                 event.getAnimationTrigger().decrementOnAnimationEnd()));
   1930         relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
   1931                 Interpolators.FAST_OUT_SLOW_IN));
   1932         event.getAnimationTrigger().increment();
   1933     }
   1934 
   1935     public final void onBusEvent(final DragEndCancelledEvent event) {
   1936         // Restore the pre-drag task stack bounds, including the dragging task view
   1937         removeIgnoreTask(event.task);
   1938         updateLayoutToStableBounds();
   1939 
   1940         // Convert the dragging task view back to its final layout-space rect
   1941         Utilities.setViewFrameFromTranslation(event.taskView);
   1942 
   1943         // Animate all the tasks into place
   1944         ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
   1945         animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
   1946                 Interpolators.FAST_OUT_SLOW_IN,
   1947                 event.getAnimationTrigger().decrementOnAnimationEnd()));
   1948         relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
   1949                 Interpolators.FAST_OUT_SLOW_IN));
   1950         event.getAnimationTrigger().increment();
   1951     }
   1952 
   1953     public final void onBusEvent(IterateRecentsEvent event) {
   1954         if (!mEnterAnimationComplete) {
   1955             // Cancel the previous task's window transition before animating the focused state
   1956             EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
   1957         }
   1958     }
   1959 
   1960     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
   1961         mEnterAnimationComplete = true;
   1962 
   1963         if (mStack.getTaskCount() > 0) {
   1964             // Start the task enter animations
   1965             mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
   1966 
   1967             // Add a runnable to the post animation ref counter to clear all the views
   1968             event.addPostAnimationCallback(new Runnable() {
   1969                 @Override
   1970                 public void run() {
   1971                     // Start the dozer to trigger to trigger any UI that shows after a timeout
   1972                     mUIDozeTrigger.startDozing();
   1973 
   1974                     // Update the focused state here -- since we only set the focused task without
   1975                     // requesting view focus in onFirstLayout(), actually request view focus and
   1976                     // animate the focused state if we are alt-tabbing now, after the window enter
   1977                     // animation is completed
   1978                     if (mFocusedTask != null) {
   1979                         RecentsConfiguration config = Recents.getConfiguration();
   1980                         RecentsActivityLaunchState launchState = config.getLaunchState();
   1981                         setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
   1982                                 false /* scrollToTask */, launchState.launchedWithAltTab);
   1983                         TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
   1984                         if (mTouchExplorationEnabled && focusedTaskView != null) {
   1985                             focusedTaskView.requestAccessibilityFocus();
   1986                         }
   1987                     }
   1988 
   1989                     EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent());
   1990                 }
   1991             });
   1992         }
   1993     }
   1994 
   1995     public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
   1996         List<TaskView> taskViews = getTaskViews();
   1997         int taskViewCount = taskViews.size();
   1998         for (int i = 0; i < taskViewCount; i++) {
   1999             TaskView tv = taskViews.get(i);
   2000             Task task = tv.getTask();
   2001             if (task.isFreeformTask()) {
   2002                 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
   2003             }
   2004         }
   2005     }
   2006 
   2007     public final void onBusEvent(final MultiWindowStateChangedEvent event) {
   2008         if (event.inMultiWindow || !event.showDeferredAnimation) {
   2009             setTasks(event.stack, true /* allowNotifyStackChanges */);
   2010         } else {
   2011             // Reset the launch state before handling the multiwindow change
   2012             RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
   2013             launchState.reset();
   2014 
   2015             // Defer until the next frame to ensure that we have received all the system insets, and
   2016             // initial layout updates
   2017             event.getAnimationTrigger().increment();
   2018             post(new Runnable() {
   2019                 @Override
   2020                 public void run() {
   2021                     // Scroll the stack to the front to see the undocked task
   2022                     mAnimationHelper.startNewStackScrollAnimation(event.stack,
   2023                             event.getAnimationTrigger());
   2024                     event.getAnimationTrigger().decrement();
   2025                 }
   2026             });
   2027         }
   2028     }
   2029 
   2030     public final void onBusEvent(ConfigurationChangedEvent event) {
   2031         if (event.fromDeviceOrientationChange) {
   2032             mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
   2033             mDisplayRect = Recents.getSystemServices().getDisplayRect();
   2034 
   2035             // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
   2036             // wrong bounds in the new layout
   2037             mStackScroller.stopScroller();
   2038         }
   2039         reloadOnConfigurationChange();
   2040 
   2041         // Notify the task views of the configuration change so they can reload their resources
   2042         if (!event.fromMultiWindow) {
   2043             mTmpTaskViews.clear();
   2044             mTmpTaskViews.addAll(getTaskViews());
   2045             mTmpTaskViews.addAll(mViewPool.getViews());
   2046             int taskViewCount = mTmpTaskViews.size();
   2047             for (int i = 0; i < taskViewCount; i++) {
   2048                 mTmpTaskViews.get(i).onConfigurationChanged();
   2049             }
   2050         }
   2051 
   2052         // Trigger a new layout and update to the initial state if necessary
   2053         if (event.fromMultiWindow) {
   2054             mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
   2055             requestLayout();
   2056         } else if (event.fromDeviceOrientationChange) {
   2057             mInitialState = INITIAL_STATE_UPDATE_ALL;
   2058             requestLayout();
   2059         }
   2060     }
   2061 
   2062     public final void onBusEvent(RecentsGrowingEvent event) {
   2063         mResetToInitialStateWhenResized = true;
   2064     }
   2065 
   2066     public void reloadOnConfigurationChange() {
   2067         mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
   2068         mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
   2069     }
   2070 
   2071     /**
   2072      * Starts an alpha animation on the freeform workspace background.
   2073      */
   2074     private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
   2075             AnimationProps animation) {
   2076         if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
   2077             return;
   2078         }
   2079 
   2080         Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
   2081         mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
   2082                 Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
   2083         mFreeformWorkspaceBackgroundAnimator.setStartDelay(
   2084                 animation.getDuration(AnimationProps.ALPHA));
   2085         mFreeformWorkspaceBackgroundAnimator.setDuration(
   2086                 animation.getDuration(AnimationProps.ALPHA));
   2087         mFreeformWorkspaceBackgroundAnimator.setInterpolator(
   2088                 animation.getInterpolator(AnimationProps.ALPHA));
   2089         mFreeformWorkspaceBackgroundAnimator.start();
   2090     }
   2091 
   2092     /**
   2093      * Returns the insert index for the task in the current set of task views. If the given task
   2094      * is already in the task view list, then this method returns the insert index assuming it
   2095      * is first removed at the previous index.
   2096      *
   2097      * @param task the task we are finding the index for
   2098      * @param taskIndex the index of the task in the stack
   2099      */
   2100     private int findTaskViewInsertIndex(Task task, int taskIndex) {
   2101         if (taskIndex != -1) {
   2102             List<TaskView> taskViews = getTaskViews();
   2103             boolean foundTaskView = false;
   2104             int taskViewCount = taskViews.size();
   2105             for (int i = 0; i < taskViewCount; i++) {
   2106                 Task tvTask = taskViews.get(i).getTask();
   2107                 if (tvTask == task) {
   2108                     foundTaskView = true;
   2109                 } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
   2110                     if (foundTaskView) {
   2111                         return i - 1;
   2112                     } else {
   2113                         return i;
   2114                     }
   2115                 }
   2116             }
   2117         }
   2118         return -1;
   2119     }
   2120 
   2121     /**
   2122      * Reads current system flags related to accessibility and screen pinning.
   2123      */
   2124     private void readSystemFlags() {
   2125         SystemServicesProxy ssp = Recents.getSystemServices();
   2126         mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
   2127         mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
   2128                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
   2129     }
   2130 
   2131     public void dump(String prefix, PrintWriter writer) {
   2132         String innerPrefix = prefix + "  ";
   2133         String id = Integer.toHexString(System.identityHashCode(this));
   2134 
   2135         writer.print(prefix); writer.print(TAG);
   2136         writer.print(" hasDefRelayout=");
   2137         writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
   2138         writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
   2139         writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
   2140         writer.print(" initialState="); writer.print(mInitialState);
   2141         writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
   2142         writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
   2143         writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
   2144         writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
   2145         writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
   2146         writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
   2147         writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
   2148         writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
   2149         writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
   2150         writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
   2151         writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
   2152         writer.print(" orientation="); writer.print(mDisplayOrientation);
   2153         writer.print(" [0x"); writer.print(id); writer.print("]");
   2154         writer.println();
   2155 
   2156         if (mFocusedTask != null) {
   2157             writer.print(innerPrefix);
   2158             writer.print("Focused task: ");
   2159             mFocusedTask.dump("", writer);
   2160         }
   2161 
   2162         mLayoutAlgorithm.dump(innerPrefix, writer);
   2163         mStackScroller.dump(innerPrefix, writer);
   2164     }
   2165 }
   2166