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