OSDN Git Service

d84a40f4e55c3d903140121c6c21706bd3d5f45f
[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 android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ObjectAnimator;
22 import android.animation.ValueAnimator;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.graphics.Canvas;
26 import android.graphics.Rect;
27 import android.view.LayoutInflater;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.widget.FrameLayout;
31 import android.widget.OverScroller;
32 import com.android.systemui.R;
33 import com.android.systemui.recents.Constants;
34 import com.android.systemui.recents.RecentsConfiguration;
35 import com.android.systemui.recents.misc.Console;
36 import com.android.systemui.recents.misc.DozeTrigger;
37 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
38 import com.android.systemui.recents.misc.Utilities;
39 import com.android.systemui.recents.model.RecentsPackageMonitor;
40 import com.android.systemui.recents.model.RecentsTaskLoader;
41 import com.android.systemui.recents.model.Task;
42 import com.android.systemui.recents.model.TaskStack;
43
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.HashSet;
47
48
49 /* The visual representation of a task stack view */
50 public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
51         TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
52         RecentsPackageMonitor.PackageCallbacks {
53
54     /** The TaskView callbacks */
55     interface TaskStackViewCallbacks {
56         public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
57                                       boolean lockToTask);
58         public void onTaskViewAppInfoClicked(Task t);
59         public void onTaskViewDismissed(Task t);
60         public void onAllTaskViewsDismissed();
61         public void onTaskStackFilterTriggered();
62         public void onTaskStackUnfilterTriggered();
63     }
64
65     RecentsConfiguration mConfig;
66
67     TaskStack mStack;
68     TaskStackViewLayoutAlgorithm mStackAlgorithm;
69     TaskStackViewFilterAlgorithm mFilterAlgorithm;
70     TaskStackViewTouchHandler mTouchHandler;
71     TaskStackViewCallbacks mCb;
72     ViewPool<TaskView, Task> mViewPool;
73     ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
74     DozeTrigger mUIDozeTrigger;
75     DebugOverlayView mDebugOverlay;
76     Rect mTaskStackBounds = new Rect();
77
78     // The virtual stack scroll that we use for the card layout
79     int mStackScroll;
80     int mMinScroll;
81     int mMaxScroll;
82     int mStashedScroll;
83     int mFocusedTaskIndex = -1;
84     OverScroller mScroller;
85     ObjectAnimator mScrollAnimator;
86     boolean mEnableStackClipping = true;
87
88     // Optimizations
89     ReferenceCountedTrigger mHwLayersTrigger;
90     int mStackViewsAnimationDuration;
91     boolean mStackViewsDirty = true;
92     boolean mAwaitingFirstLayout = true;
93     boolean mStartEnterAnimationRequestedAfterLayout;
94     ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
95     int[] mTmpVisibleRange = new int[2];
96     Rect mTmpRect = new Rect();
97     Rect mTmpRect2 = new Rect();
98     TaskViewTransform mTmpTransform = new TaskViewTransform();
99     HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
100     LayoutInflater mInflater;
101
102     // A convenience runnable to return all views to the pool
103     // XXX: After this is set, we should mark this task stack view as disabled and check that in synchronize model
104     Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
105         @Override
106         public void run() {
107             int childCount = getChildCount();
108             for (int i = childCount - 1; i >= 0; i--) {
109                 mViewPool.returnViewToPool((TaskView) getChildAt(i));
110             }
111         }
112     };
113
114     public TaskStackView(Context context, TaskStack stack) {
115         super(context);
116         mConfig = RecentsConfiguration.getInstance();
117         mStack = stack;
118         mStack.setCallbacks(this);
119         mScroller = new OverScroller(context);
120         mTouchHandler = new TaskStackViewTouchHandler(context, this);
121         mViewPool = new ViewPool<TaskView, Task>(context, this);
122         mInflater = LayoutInflater.from(context);
123         mStackAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
124         mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
125         mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
126             @Override
127             public void run() {
128                 // Show the task bar dismiss buttons
129                 int childCount = getChildCount();
130                 for (int i = 0; i < childCount; i++) {
131                     TaskView tv = (TaskView) getChildAt(i);
132                     tv.startNoUserInteractionAnimation();
133                 }
134             }
135         });
136         mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
137             @Override
138             public void run() {
139                 // Enable hw layers on each of the children
140                 int childCount = getChildCount();
141                 for (int i = 0; i < childCount; i++) {
142                     TaskView tv = (TaskView) getChildAt(i);
143                     tv.enableHwLayers();
144                 }
145             }
146         }, new Runnable() {
147             @Override
148             public void run() {
149                 // Disable hw layers on each of the children
150                 int childCount = getChildCount();
151                 for (int i = 0; i < childCount; i++) {
152                     TaskView tv = (TaskView) getChildAt(i);
153                     tv.disableHwLayers();
154                 }
155             }
156         }, new Runnable() {
157             @Override
158             public void run() {
159                 new Throwable("Invalid hw layers ref count").printStackTrace();
160                 Console.logError(getContext(), "Invalid HW layers ref count");
161             }
162         });
163     }
164
165     /** Sets the callbacks */
166     void setCallbacks(TaskStackViewCallbacks cb) {
167         mCb = cb;
168     }
169
170     /** Sets the debug overlay */
171     public void setDebugOverlay(DebugOverlayView overlay) {
172         mDebugOverlay = overlay;
173     }
174
175     /** Requests that the views be synchronized with the model */
176     void requestSynchronizeStackViewsWithModel() {
177         requestSynchronizeStackViewsWithModel(0);
178     }
179     void requestSynchronizeStackViewsWithModel(int duration) {
180         if (!mStackViewsDirty) {
181             invalidate(mStackAlgorithm.mStackRect);
182             mStackViewsDirty = true;
183         }
184         if (mAwaitingFirstLayout) {
185             // Skip the animation if we are awaiting first layout
186             mStackViewsAnimationDuration = 0;
187         } else {
188             mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
189         }
190     }
191
192     /** Finds the child view given a specific task. */
193     public TaskView getChildViewForTask(Task t) {
194         int childCount = getChildCount();
195         for (int i = 0; i < childCount; i++) {
196             TaskView tv = (TaskView) getChildAt(i);
197             if (tv.getTask() == t) {
198                 return tv;
199             }
200         }
201         return null;
202     }
203
204     /** Returns the stack algorithm for this task stack. */
205     public TaskStackViewLayoutAlgorithm getStackAlgorithm() {
206         return mStackAlgorithm;
207     }
208
209     /**
210      * Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
211      */
212     private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
213                                        ArrayList<Task> tasks,
214                                        int stackScroll,
215                                        int[] visibleRangeOut,
216                                        boolean boundTranslationsToRect) {
217         // XXX: We should be intelligent about wheee to look for the visible stack range using the
218         //      current stack scroll.
219         // XXX: We should log extra cases like the ones below where we don't expect to hit very often
220         // XXX: Print out approximately how many indices we have to go through to find the first visible transform
221
222         int taskTransformCount = taskTransforms.size();
223         int taskCount = tasks.size();
224         int frontMostVisibleIndex = -1;
225         int backMostVisibleIndex = -1;
226
227
228
229         // We can reuse the task transforms where possible to reduce object allocation
230         if (taskTransformCount < taskCount) {
231             // If there are less transforms than tasks, then add as many transforms as necessary
232             for (int i = taskTransformCount; i < taskCount; i++) {
233                 taskTransforms.add(new TaskViewTransform());
234             }
235         } else if (taskTransformCount > taskCount) {
236             // If there are more transforms than tasks, then just subset the transform list
237             taskTransforms.subList(0, taskCount);
238         }
239
240         // Update the stack transforms
241         for (int i = taskCount - 1; i >= 0; i--) {
242             TaskViewTransform transform = mStackAlgorithm.getStackTransform(tasks.get(i), stackScroll, taskTransforms.get(i));
243             if (transform.visible) {
244                 if (frontMostVisibleIndex < 0) {
245                     frontMostVisibleIndex = i;
246                 }
247                 backMostVisibleIndex = i;
248             } else {
249                 if (backMostVisibleIndex != -1) {
250                     // We've reached the end of the visible range, so going down the rest of the
251                     // stack, we can just reset the transforms accordingly
252                     while (i >= 0) {
253                         taskTransforms.get(i).reset();
254                         i--;
255                     }
256                     break;
257                 }
258             }
259
260             if (boundTranslationsToRect) {
261                 transform.translationY = Math.min(transform.translationY,
262                         mStackAlgorithm.mViewRect.bottom);
263             }
264         }
265         if (visibleRangeOut != null) {
266             visibleRangeOut[0] = frontMostVisibleIndex;
267             visibleRangeOut[1] = backMostVisibleIndex;
268         }
269         return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
270     }
271
272     /**
273      * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. This
274      * call is less optimal than calling updateStackTransforms directly.
275      */
276     private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
277                                                             int stackScroll,
278                                                             int[] visibleRangeOut,
279                                                             boolean boundTranslationsToRect) {
280         ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
281         updateStackTransforms(taskTransforms, tasks, stackScroll, visibleRangeOut,
282                 boundTranslationsToRect);
283         return taskTransforms;
284     }
285
286     /** Synchronizes the views with the model */
287     boolean synchronizeStackViewsWithModel() {
288         if (mStackViewsDirty) {
289             // Get all the task transforms
290             ArrayList<Task> tasks = mStack.getTasks();
291             int stackScroll = getStackScroll();
292             int[] visibleRange = mTmpVisibleRange;
293             boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
294
295             // Return all the invisible children to the pool
296             mTmpTaskViewMap.clear();
297             int childCount = getChildCount();
298             for (int i = childCount - 1; i >= 0; i--) {
299                 TaskView tv = (TaskView) getChildAt(i);
300                 Task task = tv.getTask();
301                 int taskIndex = mStack.indexOfTask(task);
302                 if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
303                     mTmpTaskViewMap.put(task, tv);
304                 } else {
305                     mViewPool.returnViewToPool(tv);
306                 }
307             }
308
309             // Pick up all the newly visible children and update all the existing children
310             for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
311                 Task task = tasks.get(i);
312                 TaskViewTransform transform = mCurrentTaskTransforms.get(i);
313                 TaskView tv = mTmpTaskViewMap.get(task);
314                 int taskIndex = mStack.indexOfTask(task);
315
316                 if (tv == null) {
317                     tv = mViewPool.pickUpViewFromPool(task, task);
318                     if (mStackViewsAnimationDuration > 0) {
319                         // For items in the list, put them in start animating them from the
320                         // approriate ends of the list where they are expected to appear
321                         if (transform.t < 0) {
322                             mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
323                         } else {
324                             mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(Math.min(tasks.size() - 1, visibleRange[0] + 1)),
325                                     stackScroll, mTmpTransform);
326                         }
327                         tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
328                     }
329                 }
330
331                 // Animate the task into place
332                 tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
333                         mStackViewsAnimationDuration);
334             }
335
336             // Reset the request-synchronize params
337             mStackViewsAnimationDuration = 0;
338             mStackViewsDirty = false;
339             return true;
340         }
341         return false;
342     }
343
344     /** Updates the clip for each of the task views. */
345     void clipTaskViews() {
346         // Update the clip on each task child
347         if (Constants.DebugFlags.App.EnableTaskStackClipping && mEnableStackClipping) {
348             int childCount = getChildCount();
349             for (int i = 0; i < childCount - 1; i++) {
350                 TaskView tv = (TaskView) getChildAt(i);
351                 TaskView nextTv = null;
352                 TaskView tmpTv = null;
353                 int clipBottom = 0;
354                 if (tv.shouldClipViewInStack()) {
355                     // Find the next view to clip against
356                     int nextIndex = i;
357                     while (nextIndex < getChildCount()) {
358                         tmpTv = (TaskView) getChildAt(++nextIndex);
359                         if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
360                             nextTv = tmpTv;
361                             break;
362                         }
363                     }
364
365                     // Clip against the next view, this is just an approximation since we are
366                     // stacked and we can make assumptions about the visibility of the this
367                     // task relative to the ones in front of it.
368                     if (nextTv != null) {
369                         // We calculate the bottom clip independent of the footer (since we animate
370                         // that)
371                         int scaledMaxFooterHeight = (int) (tv.getMaxFooterHeight() * tv.getScaleX());
372                         tv.getHitRect(mTmpRect);
373                         nextTv.getHitRect(mTmpRect2);
374                         clipBottom = (mTmpRect.bottom - scaledMaxFooterHeight - mTmpRect2.top);
375                     }
376                 }
377                 tv.getViewBounds().setClipBottom(clipBottom);
378             }
379             if (getChildCount() > 0) {
380                 // The front most task should never be clipped
381                 TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
382                 tv.getViewBounds().setClipBottom(0);
383             }
384         }
385     }
386
387     /** Enables/Disables clipping of the tasks in the stack. */
388     void setStackClippingEnabled(boolean stackClippingEnabled) {
389         if (!stackClippingEnabled) {
390             int childCount = getChildCount();
391             for (int i = 0; i < childCount; i++) {
392                 TaskView tv = (TaskView) getChildAt(i);
393                 tv.getViewBounds().setClipBottom(0);
394             }
395         }
396         mEnableStackClipping = stackClippingEnabled;
397     }
398
399     /** The stack insets to apply to the stack contents */
400     public void setStackInsetRect(Rect r) {
401         mTaskStackBounds.set(r);
402     }
403
404     /** Sets the current stack scroll */
405     public void setStackScroll(int value) {
406         mStackScroll = value;
407         mUIDozeTrigger.poke();
408         requestSynchronizeStackViewsWithModel();
409     }
410
411     /** Sets the current stack scroll without synchronizing the stack view with the model */
412     public void setStackScrollRaw(int value) {
413         mStackScroll = value;
414         mUIDozeTrigger.poke();
415     }
416     /** Sets the current stack scroll to the initial state when you first enter recents */
417     public void setStackScrollToInitialState() {
418         setStackScroll(getInitialStackScroll());
419     }
420     /** Computes the initial stack scroll for the stack. */
421     int getInitialStackScroll() {
422         if (mStack.getTaskCount() > 2) {
423             return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
424         }
425         return mMaxScroll;
426     }
427
428     /** Gets the current stack scroll */
429     public int getStackScroll() {
430         return mStackScroll;
431     }
432
433     /** Animates the stack scroll into bounds */
434     ObjectAnimator animateBoundScroll() {
435         int curScroll = getStackScroll();
436         int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
437         if (newScroll != curScroll) {
438             // Enable hw layers on the stack
439             addHwLayersRefCount("animateBoundScroll");
440
441             // Start a new scroll animation
442             animateScroll(curScroll, newScroll, new Runnable() {
443                 @Override
444                 public void run() {
445                     // Disable hw layers on the stack
446                     decHwLayersRefCount("animateBoundScroll");
447                 }
448             });
449         }
450         return mScrollAnimator;
451     }
452
453     /** Animates the stack scroll */
454     void animateScroll(int curScroll, int newScroll, final Runnable postRunnable) {
455         // Abort any current animations
456         abortScroller();
457         abortBoundScrollAnimation();
458
459         mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
460         mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
461                 curScroll, 250));
462         mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
463         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
464             @Override
465             public void onAnimationUpdate(ValueAnimator animation) {
466                 setStackScroll((Integer) animation.getAnimatedValue());
467             }
468         });
469         mScrollAnimator.addListener(new AnimatorListenerAdapter() {
470             @Override
471             public void onAnimationEnd(Animator animation) {
472                 if (postRunnable != null) {
473                     postRunnable.run();
474                 }
475                 mScrollAnimator.removeAllListeners();
476             }
477         });
478         mScrollAnimator.start();
479     }
480
481     /** Aborts any current stack scrolls */
482     void abortBoundScrollAnimation() {
483         if (mScrollAnimator != null) {
484             mScrollAnimator.cancel();
485         }
486     }
487
488     /** Aborts the scroller and any current fling */
489     void abortScroller() {
490         if (!mScroller.isFinished()) {
491             // Abort the scroller
492             mScroller.abortAnimation();
493             // And disable hw layers on the stack
494             decHwLayersRefCount("flingScroll");
495         }
496     }
497
498     /** Bounds the current scroll if necessary */
499     public boolean boundScroll() {
500         int curScroll = getStackScroll();
501         int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
502         if (newScroll != curScroll) {
503             setStackScroll(newScroll);
504             return true;
505         }
506         return false;
507     }
508
509     /**
510      * Bounds the current scroll if necessary, but does not synchronize the stack view with the
511      * model.
512      */
513     public boolean boundScrollRaw() {
514         int curScroll = getStackScroll();
515         int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
516         if (newScroll != curScroll) {
517             setStackScrollRaw(newScroll);
518             return true;
519         }
520         return false;
521     }
522
523
524     /** Returns the amount that the scroll is out of bounds */
525     int getScrollAmountOutOfBounds(int scroll) {
526         if (scroll < mMinScroll) {
527             return mMinScroll - scroll;
528         } else if (scroll > mMaxScroll) {
529             return scroll - mMaxScroll;
530         }
531         return 0;
532     }
533
534     /** Returns whether the specified scroll is out of bounds */
535     boolean isScrollOutOfBounds() {
536         return getScrollAmountOutOfBounds(mStackScroll) != 0;
537     }
538
539     /** Updates the min and max virtual scroll bounds */
540     void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
541         // Compute the min and max scroll values
542         mStackAlgorithm.computeMinMaxScroll(mStack.getTasks());
543         mMinScroll = mStackAlgorithm.mMinScroll;
544         mMaxScroll = mStackAlgorithm.mMaxScroll;
545
546         // Debug logging
547         if (boundScrollToNewMinMax) {
548             boundScroll();
549         }
550     }
551
552     /** Animates a task view in this stack as it launches. */
553     public void animateOnLaunchingTask(TaskView tv, final Runnable r) {
554         // Hide each of the task bar dismiss buttons
555         int childCount = getChildCount();
556         for (int i = 0; i < childCount; i++) {
557             TaskView t = (TaskView) getChildAt(i);
558             if (t == tv) {
559                 t.startLaunchTaskAnimation(r, true);
560             } else {
561                 t.startLaunchTaskAnimation(null, false);
562             }
563         }
564     }
565
566     /** Focuses the task at the specified index in the stack */
567     void focusTask(int taskIndex, boolean scrollToNewPosition) {
568         if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
569             mFocusedTaskIndex = taskIndex;
570
571             // Focus the view if possible, otherwise, focus the view after we scroll into position
572             Task t = mStack.getTasks().get(taskIndex);
573             TaskView tv = getChildViewForTask(t);
574             Runnable postScrollRunnable = null;
575             if (tv != null) {
576                 tv.setFocusedTask();
577             } else {
578                 postScrollRunnable = new Runnable() {
579                     @Override
580                     public void run() {
581                         Task t = mStack.getTasks().get(mFocusedTaskIndex);
582                         TaskView tv = getChildViewForTask(t);
583                         if (tv != null) {
584                             tv.setFocusedTask();
585                         }
586                     }
587                 };
588             }
589
590             if (scrollToNewPosition) {
591                 // Scroll the view into position
592                 int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll,
593                         mStackAlgorithm.getStackScrollForTaskIndex(t)));
594
595                 animateScroll(getStackScroll(), newScroll, postScrollRunnable);
596             } else {
597                 if (postScrollRunnable != null) {
598                     postScrollRunnable.run();
599                 }
600             }
601         }
602     }
603
604     /** Focuses the next task in the stack */
605     void focusNextTask(boolean forward) {
606         // Find the next index to focus
607         int numTasks = mStack.getTaskCount();
608         if (mFocusedTaskIndex < 0) {
609             mFocusedTaskIndex = numTasks - 1;
610         }
611         if (0 <= mFocusedTaskIndex && mFocusedTaskIndex < numTasks) {
612             mFocusedTaskIndex = Math.max(0, Math.min(numTasks - 1,
613                     mFocusedTaskIndex + (forward ? -1 : 1)));
614         }
615         focusTask(mFocusedTaskIndex, true);
616     }
617
618     /** Enables the hw layers and increments the hw layer requirement ref count */
619     void addHwLayersRefCount(String reason) {
620         mHwLayersTrigger.increment();
621     }
622
623     /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
624         need them anymore. */
625     void decHwLayersRefCount(String reason) {
626         mHwLayersTrigger.decrement();
627     }
628
629     @Override
630     public boolean onInterceptTouchEvent(MotionEvent ev) {
631         return mTouchHandler.onInterceptTouchEvent(ev);
632     }
633
634     @Override
635     public boolean onTouchEvent(MotionEvent ev) {
636         return mTouchHandler.onTouchEvent(ev);
637     }
638
639     @Override
640     public void computeScroll() {
641         if (mScroller.computeScrollOffset()) {
642             setStackScroll(mScroller.getCurrY());
643             invalidate();
644
645             // If we just finished scrolling, then disable the hw layers
646             if (mScroller.isFinished()) {
647                 decHwLayersRefCount("finishedFlingScroll");
648             }
649         }
650     }
651
652     @Override
653     public void dispatchDraw(Canvas canvas) {
654         if (synchronizeStackViewsWithModel()) {
655             clipTaskViews();
656         }
657         super.dispatchDraw(canvas);
658     }
659
660     /** Computes the stack and task rects */
661     public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
662         // Compute the rects in the stack algorithm
663         mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
664
665         // Update the scroll bounds
666         updateMinMaxScroll(false);
667     }
668
669     /**
670      * This is called with the full window width and height to allow stack view children to 
671      * perform the full screen transition down.
672      */
673     @Override
674     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
675         int width = MeasureSpec.getSize(widthMeasureSpec);
676         int height = MeasureSpec.getSize(heightMeasureSpec);
677
678         // Compute our stack/task rects
679         Rect taskStackBounds = new Rect(mTaskStackBounds);
680         taskStackBounds.bottom -= mConfig.systemInsets.bottom;
681         computeRects(width, height, taskStackBounds);
682
683         // If this is the first layout, then scroll to the front of the stack and synchronize the
684         // stack views immediately to load all the views
685         if (mAwaitingFirstLayout) {
686             setStackScrollToInitialState();
687             requestSynchronizeStackViewsWithModel();
688             synchronizeStackViewsWithModel();
689
690             // Find the first task and mark it as full screen
691             if (mConfig.launchedFromAppWithScreenshot) {
692                 int childCount = getChildCount();
693                 for (int i = 0; i < childCount; i++) {
694                     TaskView tv = (TaskView) getChildAt(i);
695                     if (tv.getTask().isLaunchTarget) {
696                         tv.setIsFullScreen(true);
697                         break;
698                     }
699                 }
700             }
701         }
702
703         // Measure each of the TaskViews
704         int childCount = getChildCount();
705         for (int i = 0; i < childCount; i++) {
706             TaskView tv = (TaskView) getChildAt(i);
707             if (tv.isFullScreenView()) {
708                 tv.measure(widthMeasureSpec, heightMeasureSpec);
709             } else {
710                 tv.measure(
711                     MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
712                             MeasureSpec.EXACTLY),
713                     MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
714                             tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
715             }
716         }
717
718         setMeasuredDimension(width, height);
719     }
720
721     /**
722      * This is called with the size of the space not including the top or right insets, or the
723      * search bar height in portrait (but including the search bar width in landscape, since we want
724      * to draw under it.
725      */
726     @Override
727     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
728         // Layout each of the children
729         int childCount = getChildCount();
730         for (int i = 0; i < childCount; i++) {
731             TaskView tv = (TaskView) getChildAt(i);
732             if (tv.isFullScreenView()) {
733                 tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
734             } else {
735                 tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
736                         mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
737                                 tv.getMaxFooterHeight());
738             }
739         }
740
741         if (mAwaitingFirstLayout) {
742             mAwaitingFirstLayout = false;
743             onFirstLayout();
744         }
745     }
746
747     /** Handler for the first layout. */
748     void onFirstLayout() {
749         // Prepare the first view for its enter animation
750         int offscreenY = mStackAlgorithm.mViewRect.bottom -
751                 (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
752         int childCount = getChildCount();
753         for (int i = childCount - 1; i >= 0; i--) {
754             TaskView tv = (TaskView) getChildAt(i);
755             tv.prepareEnterRecentsAnimation(tv.getTask().isLaunchTarget, offscreenY);
756         }
757
758         // If the enter animation started already and we haven't completed a layout yet, do the
759         // enter animation now
760         if (mStartEnterAnimationRequestedAfterLayout) {
761             startEnterRecentsAnimation(mStartEnterAnimationContext);
762             mStartEnterAnimationRequestedAfterLayout = false;
763             mStartEnterAnimationContext = null;
764         }
765
766         // Update the focused task index to be the next item to the top task
767         if (mConfig.launchedWithAltTab) {
768             // When alt-tabbing, we focus the next previous task
769             focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
770         }
771     }
772
773     /** Requests this task stacks to start it's enter-recents animation */
774     public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
775         // If we are still waiting to layout, then just defer until then
776         if (mAwaitingFirstLayout) {
777             mStartEnterAnimationRequestedAfterLayout = true;
778             mStartEnterAnimationContext = ctx;
779             return;
780         }
781
782         if (mStack.getTaskCount() > 0) {
783             // Animate all the task views into view
784             int childCount = getChildCount();
785             for (int i = childCount - 1; i >= 0; i--) {
786                 TaskView tv = (TaskView) getChildAt(i);
787                 Task task = tv.getTask();
788                 ctx.currentTaskTransform = new TaskViewTransform();
789                 ctx.currentStackViewIndex = i;
790                 ctx.currentStackViewCount = childCount;
791                 ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
792                 mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
793                 tv.startEnterRecentsAnimation(ctx);
794             }
795
796             // Add a runnable to the post animation ref counter to clear all the views
797             ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
798                 @Override
799                 public void run() {
800                     // Start dozing
801                     mUIDozeTrigger.startDozing();
802                     // Request an update of the task views after the animation in to 
803                     // relayout the fullscreen view back to its normal size
804                     if (mConfig.launchedFromAppWithScreenshot) {
805                         requestSynchronizeStackViewsWithModel();
806                     }
807                 }
808             });
809         }
810     }
811
812     /** Requests this task stacks to start it's exit-recents animation. */
813     public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
814         // Animate all the task views into view
815         ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
816                 (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
817         int childCount = getChildCount();
818         for (int i = 0; i < childCount; i++) {
819             TaskView tv = (TaskView) getChildAt(i);
820             tv.startExitToHomeAnimation(ctx);
821         }
822
823         // Add a runnable to the post animation ref counter to clear all the views
824         ctx.postAnimationTrigger.addLastDecrementRunnable(mReturnAllViewsToPoolRunnable);
825     }
826
827     @Override
828     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
829         super.onScrollChanged(l, t, oldl, oldt);
830         requestSynchronizeStackViewsWithModel();
831     }
832
833     public boolean isTransformedTouchPointInView(float x, float y, View child) {
834         return isTransformedTouchPointInView(x, y, child, null);
835     }
836
837     /** Pokes the dozer on user interaction. */
838     void onUserInteraction() {
839         // Poke the doze trigger if it is dozing
840         mUIDozeTrigger.poke();
841     }
842
843     /**** TaskStackCallbacks Implementation ****/
844
845     @Override
846     public void onStackTaskAdded(TaskStack stack, Task t) {
847         // Update the task offsets
848         mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
849
850         requestSynchronizeStackViewsWithModel();
851     }
852
853     @Override
854     public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
855         // Update the task offsets
856         mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
857
858         // Remove the view associated with this task, we can't rely on updateTransforms
859         // to work here because the task is no longer in the list
860         TaskView tv = getChildViewForTask(removedTask);
861         if (tv != null) {
862             mViewPool.returnViewToPool(tv);
863         }
864
865         // Notify the callback that we've removed the task and it can clean up after it
866         mCb.onTaskViewDismissed(removedTask);
867
868         // Update the min/max scroll and animate other task views into their new positions
869         updateMinMaxScroll(true);
870         int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
871         requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
872
873         // Update the new front most task
874         if (newFrontMostTask != null) {
875             TaskView frontTv = getChildViewForTask(newFrontMostTask);
876             if (frontTv != null) {
877                 frontTv.onTaskBound(newFrontMostTask);
878             }
879         }
880
881         // If there are no remaining tasks, then either unfilter the current stack, or just close
882         // the activity if there are no filtered stacks
883         if (mStack.getTaskCount() == 0) {
884             boolean shouldFinishActivity = true;
885             if (mStack.hasFilteredTasks()) {
886                 mStack.unfilterTasks();
887                 shouldFinishActivity = (mStack.getTaskCount() == 0);
888             }
889             if (shouldFinishActivity) {
890                 mCb.onAllTaskViewsDismissed();
891             }
892         }
893     }
894
895     @Override
896     public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
897                                 Task filteredTask) {
898         // Stash the scroll and filtered task for us to restore to when we unfilter
899         mStashedScroll = getStackScroll();
900
901         // Calculate the current task transforms
902         ArrayList<TaskViewTransform> curTaskTransforms =
903                 getStackTransforms(curTasks, getStackScroll(), null, true);
904
905         // Update the task offsets
906         mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
907
908         // Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
909         updateMinMaxScroll(false);
910         float overlapHeight = mStackAlgorithm.getTaskOverlapHeight();
911         setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
912         boundScrollRaw();
913
914         // Compute the transforms of the items in the new stack after setting the new scroll
915         final ArrayList<Task> tasks = mStack.getTasks();
916         final ArrayList<TaskViewTransform> taskTransforms =
917                 getStackTransforms(mStack.getTasks(), getStackScroll(), null, true);
918
919         // Animate
920         mFilterAlgorithm.startFilteringAnimation(curTasks, curTaskTransforms, tasks, taskTransforms);
921
922         // Notify any callbacks
923         mCb.onTaskStackFilterTriggered();
924     }
925
926     @Override
927     public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
928         // Calculate the current task transforms
929         final ArrayList<TaskViewTransform> curTaskTransforms =
930                 getStackTransforms(curTasks, getStackScroll(), null, true);
931
932         // Update the task offsets
933         mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
934
935         // Restore the stashed scroll
936         updateMinMaxScroll(false);
937         setStackScrollRaw(mStashedScroll);
938         boundScrollRaw();
939
940         // Compute the transforms of the items in the new stack after restoring the stashed scroll
941         final ArrayList<Task> tasks = mStack.getTasks();
942         final ArrayList<TaskViewTransform> taskTransforms =
943                 getStackTransforms(tasks, getStackScroll(), null, true);
944
945         // Animate
946         mFilterAlgorithm.startFilteringAnimation(curTasks, curTaskTransforms, tasks, taskTransforms);
947
948         // Clear the saved vars
949         mStashedScroll = 0;
950
951         // Notify any callbacks
952         mCb.onTaskStackUnfilterTriggered();
953     }
954
955     /**** ViewPoolConsumer Implementation ****/
956
957     @Override
958     public TaskView createView(Context context) {
959         return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
960     }
961
962     @Override
963     public void prepareViewToEnterPool(TaskView tv) {
964         Task task = tv.getTask();
965
966         // Report that this tasks's data is no longer being used
967         RecentsTaskLoader.getInstance().unloadTaskData(task);
968
969         // Detach the view from the hierarchy
970         detachViewFromParent(tv);
971
972         // Disable HW layers
973         tv.disableHwLayers();
974
975         // Reset the view properties
976         tv.resetViewProperties();
977     }
978
979     @Override
980     public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
981         // Rebind the task and request that this task's data be filled into the TaskView
982         tv.onTaskBound(task);
983         RecentsTaskLoader.getInstance().loadTaskData(task);
984
985         // Sanity check, the task view should always be clipping against the stack at this point,
986         // but just in case, re-enable it here
987         tv.setClipViewInStack(true);
988
989         // If the doze trigger has already fired, then update the state for this task view
990         if (mUIDozeTrigger.hasTriggered()) {
991             tv.setNoUserInteractionState();
992         }
993
994         // Find the index where this task should be placed in the stack
995         int insertIndex = -1;
996         int taskIndex = mStack.indexOfTask(task);
997         if (taskIndex != -1) {
998             int childCount = getChildCount();
999             for (int i = 0; i < childCount; i++) {
1000                 Task tvTask = ((TaskView) getChildAt(i)).getTask();
1001                 if (taskIndex < mStack.indexOfTask(tvTask)) {
1002                     insertIndex = i;
1003                     break;
1004                 }
1005             }
1006         }
1007
1008         // Add/attach the view to the hierarchy
1009         if (isNewView) {
1010             addView(tv, insertIndex);
1011
1012             // Set the callbacks and listeners for this new view
1013             tv.setTouchEnabled(true);
1014             tv.setCallbacks(this);
1015         } else {
1016             attachViewToParent(tv, insertIndex, tv.getLayoutParams());
1017         }
1018
1019         // Enable hw layers on this view if hw layers are enabled on the stack
1020         if (mHwLayersTrigger.getCount() > 0) {
1021             tv.enableHwLayers();
1022         }
1023     }
1024
1025     @Override
1026     public boolean hasPreferredData(TaskView tv, Task preferredData) {
1027         return (tv.getTask() == preferredData);
1028     }
1029
1030     /**** TaskViewCallbacks Implementation ****/
1031
1032     @Override
1033     public void onTaskViewAppIconClicked(TaskView tv) {
1034         if (Constants.DebugFlags.App.EnableTaskFiltering) {
1035             if (mStack.hasFilteredTasks()) {
1036                 mStack.unfilterTasks();
1037             } else {
1038                 mStack.filterTasks(tv.getTask());
1039             }
1040         }
1041     }
1042
1043     @Override
1044     public void onTaskViewAppInfoClicked(TaskView tv) {
1045         if (mCb != null) {
1046             mCb.onTaskViewAppInfoClicked(tv.getTask());
1047         }
1048     }
1049
1050     @Override
1051     public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
1052         // Cancel any doze triggers
1053         mUIDozeTrigger.stopDozing();
1054
1055         if (mCb != null) {
1056             mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask);
1057         }
1058     }
1059
1060     @Override
1061     public void onTaskViewDismissed(TaskView tv) {
1062         Task task = tv.getTask();
1063         // Remove the task from the view
1064         mStack.removeTask(task);
1065     }
1066
1067     @Override
1068     public void onTaskViewClipStateChanged(TaskView tv) {
1069         invalidate(mStackAlgorithm.mStackRect);
1070     }
1071
1072     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
1073
1074     @Override
1075     public void onComponentRemoved(HashSet<ComponentName> cns) {
1076         // For other tasks, just remove them directly if they no longer exist
1077         ArrayList<Task> tasks = mStack.getTasks();
1078         for (int i = tasks.size() - 1; i >= 0; i--) {
1079             final Task t = tasks.get(i);
1080             if (cns.contains(t.key.baseIntent.getComponent())) {
1081                 TaskView tv = getChildViewForTask(t);
1082                 if (tv != null) {
1083                     // For visible children, defer removing the task until after the animation
1084                     tv.startDeleteTaskAnimation(new Runnable() {
1085                         @Override
1086                         public void run() {
1087                             mStack.removeTask(t);
1088                         }
1089                     });
1090                 } else {
1091                     // Otherwise, remove the task from the stack immediately
1092                     mStack.removeTask(t);
1093                 }
1094             }
1095         }
1096     }
1097 }