TaskStackAnimationHelper.java revision e693aafe0511c2a7ffc571b22abeefba44046225
1/*
2 * Copyright (C) 2015 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
17package com.android.systemui.recents.views;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.content.Context;
22import android.content.res.Resources;
23import android.graphics.RectF;
24import android.util.Log;
25import android.view.View;
26import android.view.animation.Interpolator;
27import android.view.animation.PathInterpolator;
28
29import com.android.systemui.Interpolators;
30import com.android.systemui.R;
31import com.android.systemui.recents.Recents;
32import com.android.systemui.recents.RecentsActivityLaunchState;
33import com.android.systemui.recents.RecentsConfiguration;
34import com.android.systemui.recents.misc.ReferenceCountedTrigger;
35import com.android.systemui.recents.model.Task;
36import com.android.systemui.recents.model.TaskStack;
37
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
43 * but not the contents of the {@link TaskView}s.
44 */
45public class TaskStackAnimationHelper {
46
47    /**
48     * Callbacks from the helper to coordinate view-content animations with view animations.
49     */
50    public interface Callbacks {
51        /**
52         * Callback to prepare for the start animation for the launch target {@link TaskView}.
53         */
54        void onPrepareLaunchTargetForEnterAnimation();
55
56        /**
57         * Callback to start the animation for the launch target {@link TaskView}.
58         */
59        void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
60                boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
61
62        /**
63         * Callback to start the animation for the launch target {@link TaskView} when it is
64         * launched from Recents.
65         */
66        void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
67                ReferenceCountedTrigger postAnimationTrigger);
68    }
69
70    private static final int FRAME_OFFSET_MS = 16;
71
72    public static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
73    public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 333;
74    private static final PathInterpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR =
75            new PathInterpolator(0, 0, 0, 1f);
76    private static final PathInterpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR =
77            new PathInterpolator(0, 0, 0.2f, 1f);
78
79    public static final int EXIT_TO_HOME_ALPHA_DURATION = 100;
80    public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 150;
81    private static final PathInterpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
82            new PathInterpolator(0.8f, 0, 0.6f, 1f);
83    private static final PathInterpolator EXIT_TO_HOME_ALPHA_INTERPOLATOR =
84            new PathInterpolator(0.4f, 0, 1f, 1f);
85
86    private static final PathInterpolator FOCUS_NEXT_TASK_INTERPOLATOR =
87            new PathInterpolator(0.4f, 0, 0, 1f);
88    private static final PathInterpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
89            new PathInterpolator(0, 0, 0, 1f);
90    private static final PathInterpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
91            new PathInterpolator(0.4f, 0, 0.2f, 1f);
92
93    private TaskStackView mStackView;
94
95    private TaskViewTransform mTmpTransform = new TaskViewTransform();
96    private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
97    private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
98
99    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
100        mStackView = stackView;
101    }
102
103    /**
104     * Prepares the stack views and puts them in their initial animation state while visible, before
105     * the in-app enter animations start (after the window-transition completes).
106     */
107    public void prepareForEnterAnimation() {
108        RecentsConfiguration config = Recents.getConfiguration();
109        RecentsActivityLaunchState launchState = config.getLaunchState();
110        Resources res = mStackView.getResources();
111
112        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
113        TaskStackViewScroller stackScroller = mStackView.getScroller();
114        TaskStack stack = mStackView.getStack();
115        Task launchTargetTask = stack.getLaunchTarget();
116
117        // Break early if there are no tasks
118        if (stack.getTaskCount() == 0) {
119            return;
120        }
121
122        int offscreenYOffset = stackLayout.mStackRect.height();
123        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
124                R.dimen.recents_task_view_affiliate_group_enter_offset);
125
126        // Prepare each of the task views for their enter animation from front to back
127        List<TaskView> taskViews = mStackView.getTaskViews();
128        for (int i = taskViews.size() - 1; i >= 0; i--) {
129            TaskView tv = taskViews.get(i);
130            Task task = tv.getTask();
131            boolean currentTaskOccludesLaunchTarget = (launchTargetTask != null &&
132                    launchTargetTask.group.isTaskAboveTask(task, launchTargetTask));
133            boolean hideTask = (launchTargetTask != null &&
134                    launchTargetTask.isFreeformTask() && task.isFreeformTask());
135
136            // Get the current transform for the task, which will be used to position it offscreen
137            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
138                    null);
139
140            if (hideTask) {
141                tv.setVisibility(View.INVISIBLE);
142            } else if (launchState.launchedHasConfigurationChanged) {
143                // Just load the views as-is
144            } else if (launchState.launchedFromApp) {
145                if (task.isLaunchTarget) {
146                    tv.onPrepareLaunchTargetForEnterAnimation();
147                } else if (currentTaskOccludesLaunchTarget) {
148                    // Move the task view slightly lower so we can animate it in
149                    RectF bounds = new RectF(mTmpTransform.rect);
150                    bounds.offset(0, taskViewAffiliateGroupEnterOffset);
151                    tv.setClipViewInStack(false);
152                    tv.setAlpha(0f);
153                    tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top,
154                            (int) bounds.right, (int) bounds.bottom);
155                }
156            } else if (launchState.launchedFromHome) {
157                // Move the task view off screen (below) so we can animate it in
158                RectF bounds = new RectF(mTmpTransform.rect);
159                bounds.offset(0, offscreenYOffset);
160                tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
161                        (int) bounds.bottom);
162            }
163        }
164    }
165
166    /**
167     * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
168     * depending on how Recents was triggered.
169     */
170    public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
171        RecentsConfiguration config = Recents.getConfiguration();
172        RecentsActivityLaunchState launchState = config.getLaunchState();
173        Resources res = mStackView.getResources();
174
175        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
176        TaskStackViewScroller stackScroller = mStackView.getScroller();
177        TaskStack stack = mStackView.getStack();
178        Task launchTargetTask = stack.getLaunchTarget();
179
180        // Break early if there are no tasks
181        if (stack.getTaskCount() == 0) {
182            return;
183        }
184
185        int taskViewEnterFromAppDuration = res.getInteger(
186                R.integer.recents_task_enter_from_app_duration);
187        int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
188                R.integer.recents_task_enter_from_affiliated_app_duration);
189
190        // Create enter animations for each of the views from front to back
191        List<TaskView> taskViews = mStackView.getTaskViews();
192        int taskViewCount = taskViews.size();
193        for (int i = taskViewCount - 1; i >= 0; i--) {
194            int taskIndexFromFront = taskViewCount - i - 1;
195            final TaskView tv = taskViews.get(i);
196            Task task = tv.getTask();
197            boolean currentTaskOccludesLaunchTarget = false;
198            if (launchTargetTask != null) {
199                currentTaskOccludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
200                        launchTargetTask);
201            }
202
203            // Get the current transform for the task, which will be updated to the final transform
204            // to animate to depending on how recents was invoked
205            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
206                    null);
207
208            if (launchState.launchedFromApp) {
209                if (task.isLaunchTarget) {
210                    tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
211                            taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
212                            postAnimationTrigger);
213                } else {
214                    // Animate the task up if it was occluding the launch target
215                    if (currentTaskOccludesLaunchTarget) {
216                        AnimationProps taskAnimation = new AnimationProps(
217                                taskViewEnterFromAffiliatedAppDuration, Interpolators.ALPHA_IN,
218                                new AnimatorListenerAdapter() {
219                                    @Override
220                                    public void onAnimationEnd(Animator animation) {
221                                        postAnimationTrigger.decrement();
222                                        tv.setClipViewInStack(false);
223                                    }
224                                });
225                        postAnimationTrigger.increment();
226                        mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
227                    }
228                }
229
230            } else if (launchState.launchedFromHome) {
231                // Animate the tasks up
232                AnimationProps taskAnimation = new AnimationProps()
233                        .setStartDelay(AnimationProps.ALPHA, taskIndexFromFront * FRAME_OFFSET_MS)
234                        .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION)
235                        .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION -
236                                (taskIndexFromFront * FRAME_OFFSET_MS))
237                        .setInterpolator(AnimationProps.BOUNDS,
238                                ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR)
239                        .setInterpolator(AnimationProps.ALPHA,
240                                ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
241                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
242                postAnimationTrigger.increment();
243                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
244            }
245        }
246    }
247
248    /**
249     * Starts an in-app animation to hide all the task views so that we can transition back home.
250     */
251    public void startExitToHomeAnimation(boolean animated,
252            ReferenceCountedTrigger postAnimationTrigger) {
253        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
254        TaskStackViewScroller stackScroller = mStackView.getScroller();
255        TaskStack stack = mStackView.getStack();
256
257        // Break early if there are no tasks
258        if (stack.getTaskCount() == 0) {
259            return;
260        }
261
262        int offscreenYOffset = stackLayout.mStackRect.height();
263
264        // Create the animations for each of the tasks
265        List<TaskView> taskViews = mStackView.getTaskViews();
266        int taskViewCount = taskViews.size();
267        for (int i = 0; i < taskViewCount; i++) {
268            int taskIndexFromFront = taskViewCount - i - 1;
269            TaskView tv = taskViews.get(i);
270            Task task = tv.getTask();
271
272            // Animate the tasks down
273            AnimationProps taskAnimation;
274            if (animated) {
275                taskAnimation = new AnimationProps()
276                        .setStartDelay(AnimationProps.ALPHA, i * FRAME_OFFSET_MS)
277                        .setDuration(AnimationProps.ALPHA, EXIT_TO_HOME_ALPHA_DURATION)
278                        .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION +
279                                (taskIndexFromFront * FRAME_OFFSET_MS))
280                        .setInterpolator(AnimationProps.BOUNDS,
281                                EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
282                        .setInterpolator(AnimationProps.ALPHA,
283                                EXIT_TO_HOME_ALPHA_INTERPOLATOR)
284                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
285                postAnimationTrigger.increment();
286            } else {
287                taskAnimation = AnimationProps.IMMEDIATE;
288            }
289
290            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
291                    null);
292            mTmpTransform.rect.offset(0, offscreenYOffset);
293            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
294        }
295    }
296
297    /**
298     * Starts the animation for the launching task view, hiding any tasks that might occlude the
299     * window transition for the launching task.
300     */
301    public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
302            final ReferenceCountedTrigger postAnimationTrigger) {
303        Resources res = mStackView.getResources();
304        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
305        TaskStackViewScroller stackScroller = mStackView.getScroller();
306
307        int taskViewExitToAppDuration = res.getInteger(
308                R.integer.recents_task_exit_to_app_duration);
309        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
310                R.dimen.recents_task_view_affiliate_group_enter_offset);
311
312        Task launchingTask = launchingTaskView.getTask();
313        List<TaskView> taskViews = mStackView.getTaskViews();
314        int taskViewCount = taskViews.size();
315        for (int i = 0; i < taskViewCount; i++) {
316            TaskView tv = taskViews.get(i);
317            Task task = tv.getTask();
318            boolean currentTaskOccludesLaunchTarget = (launchingTask != null &&
319                    launchingTask.group.isTaskAboveTask(task, launchingTask));
320
321            if (tv == launchingTaskView) {
322                tv.setClipViewInStack(false);
323                tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
324                        screenPinningRequested, postAnimationTrigger);
325            } else if (currentTaskOccludesLaunchTarget) {
326                // Animate this task out of view
327                AnimationProps taskAnimation = new AnimationProps(
328                        taskViewExitToAppDuration, Interpolators.ALPHA_OUT,
329                        postAnimationTrigger.decrementOnAnimationEnd());
330                postAnimationTrigger.increment();
331
332                stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
333                        null);
334                mTmpTransform.alpha = 0f;
335                mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
336                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
337            }
338        }
339    }
340
341    /**
342     * Starts the delete animation for the specified {@link TaskView}.
343     */
344    public void startDeleteTaskAnimation(Task deleteTask, final TaskView deleteTaskView,
345            final ReferenceCountedTrigger postAnimationTrigger) {
346        Resources res = mStackView.getResources();
347        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
348        TaskStackViewScroller stackScroller = mStackView.getScroller();
349
350        int taskViewRemoveAnimDuration = res.getInteger(
351                R.integer.recents_animate_task_view_remove_duration);
352        int taskViewRemoveAnimTranslationXPx = res.getDimensionPixelSize(
353                R.dimen.recents_task_view_remove_anim_translation_x);
354
355        // Disabling clipping with the stack while the view is animating away
356        deleteTaskView.setClipViewInStack(false);
357
358        // Compose the new animation and transform and star the animation
359        AnimationProps taskAnimation = new AnimationProps(taskViewRemoveAnimDuration,
360                Interpolators.ALPHA_OUT, new AnimatorListenerAdapter() {
361            @Override
362            public void onAnimationEnd(Animator animation) {
363                postAnimationTrigger.decrement();
364
365                // Re-enable clipping with the stack (we will reuse this view)
366                deleteTaskView.setClipViewInStack(true);
367            }
368        });
369        postAnimationTrigger.increment();
370
371        stackLayout.getStackTransform(deleteTask, stackScroller.getStackScroll(), mTmpTransform,
372                null);
373        mTmpTransform.alpha = 0f;
374        mTmpTransform.rect.offset(taskViewRemoveAnimTranslationXPx, 0);
375        mStackView.updateTaskViewToTransform(deleteTaskView, mTmpTransform, taskAnimation);
376    }
377
378    /**
379     * Starts the animation to hide the {@link TaskView}s when the history is shown.
380     */
381    public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
382        Resources res = mStackView.getResources();
383        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
384        TaskStackViewScroller stackScroller = mStackView.getScroller();
385
386        int offscreenY = stackLayout.mStackRect.bottom;
387        int historyTransitionDuration = res.getInteger(
388                R.integer.recents_history_transition_duration);
389        int startDelayIncr = 16;
390
391        List<TaskView> taskViews = mStackView.getTaskViews();
392        int taskViewCount = taskViews.size();
393        for (int i = taskViewCount - 1; i >= 0; i--) {
394            TaskView tv = taskViews.get(i);
395            Task task = tv.getTask();
396            AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
397                    historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN,
398                    postAnimationTrigger.decrementOnAnimationEnd());
399            postAnimationTrigger.increment();
400
401            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
402                    null);
403            mTmpTransform.alpha = 0f;
404            mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
405            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
406        }
407    }
408
409    /**
410     * Starts the animation to show the {@link TaskView}s when the history is hidden.
411     */
412    public void startHideHistoryAnimation() {
413        Resources res = mStackView.getResources();
414        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
415        TaskStackViewScroller stackScroller = mStackView.getScroller();
416
417        int historyTransitionDuration = res.getInteger(
418                R.integer.recents_history_transition_duration);
419        int startDelayIncr = 16;
420
421        List<TaskView> taskViews = mStackView.getTaskViews();
422        int taskViewCount = taskViews.size();
423        for (int i = taskViewCount - 1; i >= 0; i--) {
424            TaskView tv = taskViews.get(i);
425            AnimationProps taskAnimation = new AnimationProps(startDelayIncr * i,
426                    historyTransitionDuration, Interpolators.FAST_OUT_SLOW_IN);
427            stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
428                    mTmpTransform, null);
429            mTmpTransform.alpha = 1f;
430            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
431        }
432    }
433
434    /**
435     * Starts the animation to focus the next {@link TaskView} when paging through recents.
436     *
437     * @return whether or not this will trigger a scroll in the stack
438     */
439    public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
440            boolean requestViewFocus) {
441        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
442        TaskStackViewScroller stackScroller = mStackView.getScroller();
443        TaskStack stack = mStackView.getStack();
444
445        final float curScroll = stackScroller.getStackScroll();
446        final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
447        boolean willScrollToFront = newScroll > curScroll;
448        boolean willScroll = Float.compare(newScroll, curScroll) != 0;
449
450        // Get the current set of task transforms
451        int taskViewCount = mStackView.getTaskViews().size();
452        ArrayList<Task> stackTasks = stack.getStackTasks();
453        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
454
455        // Pick up the newly visible views after the scroll
456        mStackView.bindVisibleTaskViews(newScroll);
457
458        // Update the internal state
459        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
460        stackScroller.setStackScroll(newScroll, null /* animation */);
461        mStackView.cancelDeferredTaskViewLayoutAnimation();
462
463        // Get the final set of task transforms
464        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
465                mTmpFinalTaskTransforms);
466
467        // Focus the task view
468        TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
469        if (newFocusedTaskView == null) {
470            // Log the error if we have no task view, and skip the animation
471            Log.e("TaskStackAnimationHelper", "b/27389156 null-task-view prebind:" + taskViewCount +
472                    " postbind:" + mStackView.getTaskViews().size() + " prescroll:" + curScroll +
473                    " postscroll: " + newScroll);
474            return false;
475        }
476        newFocusedTaskView.setFocusedState(true, requestViewFocus);
477
478        // Setup the end listener to return all the hidden views to the view pool after the
479        // focus animation
480        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
481        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
482            @Override
483            public void run() {
484                mStackView.bindVisibleTaskViews(newScroll);
485            }
486        });
487
488        List<TaskView> taskViews = mStackView.getTaskViews();
489        taskViewCount = taskViews.size();
490        int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
491        for (int i = 0; i < taskViewCount; i++) {
492            TaskView tv = taskViews.get(i);
493            Task task = tv.getTask();
494
495            if (mStackView.isIgnoredTask(task)) {
496                continue;
497            }
498
499            int taskIndex = stackTasks.indexOf(task);
500            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
501            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
502
503            // Update the task to the initial state (for the newly picked up tasks)
504            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
505
506            int duration;
507            Interpolator interpolator;
508            if (willScrollToFront) {
509                duration = Math.max(100, 100 + ((i - 1) * 50));
510                interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
511            } else {
512                if (i < newFocusTaskViewIndex) {
513                    duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
514                    interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
515                } else if (i > newFocusTaskViewIndex) {
516                    duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
517                    interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
518                } else {
519                    duration = 200;
520                    interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
521                }
522            }
523
524            AnimationProps anim = new AnimationProps()
525                    .setDuration(AnimationProps.BOUNDS, duration)
526                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
527                    .setListener(postAnimTrigger.decrementOnAnimationEnd());
528            postAnimTrigger.increment();
529            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
530        }
531        return willScroll;
532    }
533}
534