OSDN Git Service

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