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