1f24f21695f5609d06402cf61e3500d408b99bdcbWinson/*
2f24f21695f5609d06402cf61e3500d408b99bdcbWinson * Copyright (C) 2015 The Android Open Source Project
3f24f21695f5609d06402cf61e3500d408b99bdcbWinson *
4f24f21695f5609d06402cf61e3500d408b99bdcbWinson * Licensed under the Apache License, Version 2.0 (the "License");
5f24f21695f5609d06402cf61e3500d408b99bdcbWinson * you may not use this file except in compliance with the License.
6f24f21695f5609d06402cf61e3500d408b99bdcbWinson * You may obtain a copy of the License at
7f24f21695f5609d06402cf61e3500d408b99bdcbWinson *
8f24f21695f5609d06402cf61e3500d408b99bdcbWinson *      http://www.apache.org/licenses/LICENSE-2.0
9f24f21695f5609d06402cf61e3500d408b99bdcbWinson *
10f24f21695f5609d06402cf61e3500d408b99bdcbWinson * Unless required by applicable law or agreed to in writing, software
11f24f21695f5609d06402cf61e3500d408b99bdcbWinson * distributed under the License is distributed on an "AS IS" BASIS,
12f24f21695f5609d06402cf61e3500d408b99bdcbWinson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f24f21695f5609d06402cf61e3500d408b99bdcbWinson * See the License for the specific language governing permissions and
14f24f21695f5609d06402cf61e3500d408b99bdcbWinson * limitations under the License.
15f24f21695f5609d06402cf61e3500d408b99bdcbWinson */
16f24f21695f5609d06402cf61e3500d408b99bdcbWinson
17f24f21695f5609d06402cf61e3500d408b99bdcbWinsonpackage com.android.systemui.recents.views;
18f24f21695f5609d06402cf61e3500d408b99bdcbWinson
19f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport android.animation.Animator;
20f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport android.animation.AnimatorListenerAdapter;
213f32e7eba7aec02edbf542cc11eee13264a5abf2Winsonimport android.animation.TimeInterpolator;
22aa0dea7a4a5b0861bc040e3f5db633a272146fb7Winsonimport android.animation.ValueAnimator;
23f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport android.content.Context;
24545c5c20d3b1f424b2f1917a262a5e8130dab6d0Jorim Jaggiimport android.content.res.Configuration;
25f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport android.content.res.Resources;
263f3d744d5c51d3ecc06af22ac7d69f4437671915Winsonimport android.util.Log;
27f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport android.view.View;
2805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinsonimport android.view.animation.Interpolator;
2950448630a49a9602eb53561d1c022890d07449d0Winsonimport android.view.animation.PathInterpolator;
30c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
31c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators;
32f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.R;
33f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.Recents;
34f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.RecentsActivityLaunchState;
35f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.RecentsConfiguration;
36f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.misc.ReferenceCountedTrigger;
37f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.model.Task;
38f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport com.android.systemui.recents.model.TaskStack;
39f24f21695f5609d06402cf61e3500d408b99bdcbWinson
4005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinsonimport java.util.ArrayList;
41f24f21695f5609d06402cf61e3500d408b99bdcbWinsonimport java.util.List;
42f24f21695f5609d06402cf61e3500d408b99bdcbWinson
43f24f21695f5609d06402cf61e3500d408b99bdcbWinson/**
44f24f21695f5609d06402cf61e3500d408b99bdcbWinson * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
45f24f21695f5609d06402cf61e3500d408b99bdcbWinson * but not the contents of the {@link TaskView}s.
46f24f21695f5609d06402cf61e3500d408b99bdcbWinson */
47f24f21695f5609d06402cf61e3500d408b99bdcbWinsonpublic class TaskStackAnimationHelper {
48f24f21695f5609d06402cf61e3500d408b99bdcbWinson
49f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
50f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Callbacks from the helper to coordinate view-content animations with view animations.
51f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
52f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public interface Callbacks {
53f24f21695f5609d06402cf61e3500d408b99bdcbWinson        /**
54f24f21695f5609d06402cf61e3500d408b99bdcbWinson         * Callback to prepare for the start animation for the launch target {@link TaskView}.
55f24f21695f5609d06402cf61e3500d408b99bdcbWinson         */
56f24f21695f5609d06402cf61e3500d408b99bdcbWinson        void onPrepareLaunchTargetForEnterAnimation();
57f24f21695f5609d06402cf61e3500d408b99bdcbWinson
58f24f21695f5609d06402cf61e3500d408b99bdcbWinson        /**
59f24f21695f5609d06402cf61e3500d408b99bdcbWinson         * Callback to start the animation for the launch target {@link TaskView}.
60f24f21695f5609d06402cf61e3500d408b99bdcbWinson         */
61e693aafe0511c2a7ffc571b22abeefba44046225Winson        void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
62e693aafe0511c2a7ffc571b22abeefba44046225Winson                boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
63f24f21695f5609d06402cf61e3500d408b99bdcbWinson
64f24f21695f5609d06402cf61e3500d408b99bdcbWinson        /**
65f24f21695f5609d06402cf61e3500d408b99bdcbWinson         * Callback to start the animation for the launch target {@link TaskView} when it is
66f24f21695f5609d06402cf61e3500d408b99bdcbWinson         * launched from Recents.
67f24f21695f5609d06402cf61e3500d408b99bdcbWinson         */
68f24f21695f5609d06402cf61e3500d408b99bdcbWinson        void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
69f24f21695f5609d06402cf61e3500d408b99bdcbWinson                ReferenceCountedTrigger postAnimationTrigger);
7059924fe0d9136cf349759bea1e06b661603f95feWinson
7159924fe0d9136cf349759bea1e06b661603f95feWinson        /**
7259924fe0d9136cf349759bea1e06b661603f95feWinson         * Callback to start the animation for the front {@link TaskView} if there is no launch
7359924fe0d9136cf349759bea1e06b661603f95feWinson         * target.
7459924fe0d9136cf349759bea1e06b661603f95feWinson         */
7559924fe0d9136cf349759bea1e06b661603f95feWinson        void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled);
76f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
77f24f21695f5609d06402cf61e3500d408b99bdcbWinson
783f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final int DOUBLE_FRAME_OFFSET_MS = 33;
7950448630a49a9602eb53561d1c022890d07449d0Winson    private static final int FRAME_OFFSET_MS = 16;
8050448630a49a9602eb53561d1c022890d07449d0Winson
813f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final int ENTER_EXIT_NUM_ANIMATING_TASKS = 5;
82899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi
833f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
843f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 300;
853f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR =
863f32e7eba7aec02edbf542cc11eee13264a5abf2Winson            Interpolators.LINEAR_OUT_SLOW_IN;
873f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR = Interpolators.LINEAR;
883f32e7eba7aec02edbf542cc11eee13264a5abf2Winson
893f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 200;
903f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
913f32e7eba7aec02edbf542cc11eee13264a5abf2Winson            new PathInterpolator(0.4f, 0, 0.6f, 1f);
923f32e7eba7aec02edbf542cc11eee13264a5abf2Winson
933f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final int DISMISS_TASK_DURATION = 175;
943f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final int DISMISS_ALL_TASKS_DURATION = 200;
953f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator DISMISS_ALL_TRANSLATION_INTERPOLATOR =
9650448630a49a9602eb53561d1c022890d07449d0Winson            new PathInterpolator(0.4f, 0, 1f, 1f);
9750448630a49a9602eb53561d1c022890d07449d0Winson
983f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator FOCUS_NEXT_TASK_INTERPOLATOR =
9905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            new PathInterpolator(0.4f, 0, 0, 1f);
1003f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
10105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            new PathInterpolator(0, 0, 0, 1f);
1023f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
1033f32e7eba7aec02edbf542cc11eee13264a5abf2Winson            Interpolators.LINEAR_OUT_SLOW_IN;
10405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
1053f32e7eba7aec02edbf542cc11eee13264a5abf2Winson    private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
1063f32e7eba7aec02edbf542cc11eee13264a5abf2Winson            Interpolators.LINEAR_OUT_SLOW_IN;
107899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi
10845c8048f4f8996f085c78621efaf710c907ce213Manu Cornet    private final int mEnterAndExitFromHomeTranslationOffset;
109f24f21695f5609d06402cf61e3500d408b99bdcbWinson    private TaskStackView mStackView;
110f24f21695f5609d06402cf61e3500d408b99bdcbWinson
111f24f21695f5609d06402cf61e3500d408b99bdcbWinson    private TaskViewTransform mTmpTransform = new TaskViewTransform();
11205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson    private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
11305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson    private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
114f24f21695f5609d06402cf61e3500d408b99bdcbWinson
115f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
116f24f21695f5609d06402cf61e3500d408b99bdcbWinson        mStackView = stackView;
11745c8048f4f8996f085c78621efaf710c907ce213Manu Cornet        mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
11845c8048f4f8996f085c78621efaf710c907ce213Manu Cornet                ? 0 : DOUBLE_FRAME_OFFSET_MS;
119f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
120f24f21695f5609d06402cf61e3500d408b99bdcbWinson
121f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
122f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Prepares the stack views and puts them in their initial animation state while visible, before
123f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * the in-app enter animations start (after the window-transition completes).
124f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
125f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public void prepareForEnterAnimation() {
126f24f21695f5609d06402cf61e3500d408b99bdcbWinson        RecentsConfiguration config = Recents.getConfiguration();
127f24f21695f5609d06402cf61e3500d408b99bdcbWinson        RecentsActivityLaunchState launchState = config.getLaunchState();
128f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Resources res = mStackView.getResources();
129e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi        Resources appResources = mStackView.getContext().getApplicationContext().getResources();
130f24f21695f5609d06402cf61e3500d408b99bdcbWinson
131f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
132f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStackViewScroller stackScroller = mStackView.getScroller();
133f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStack stack = mStackView.getStack();
134f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Task launchTargetTask = stack.getLaunchTarget();
135f24f21695f5609d06402cf61e3500d408b99bdcbWinson
136f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Break early if there are no tasks
1374b057c6787624b75613769a857ccdf51114bb7f2Winson        if (stack.getTaskCount() == 0) {
138f24f21695f5609d06402cf61e3500d408b99bdcbWinson            return;
139f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
140f24f21695f5609d06402cf61e3500d408b99bdcbWinson
14140a227337712a881cca4d7dd04323380287b9e40Winson        int offscreenYOffset = stackLayout.mStackRect.height();
142f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
14359924fe0d9136cf349759bea1e06b661603f95feWinson                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
144899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi        int launchedWhileDockingOffset = res.getDimensionPixelSize(
14559924fe0d9136cf349759bea1e06b661603f95feWinson                R.dimen.recents_task_stack_animation_launched_while_docking_offset);
146e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi        boolean isLandscape = appResources.getConfiguration().orientation
147e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi                == Configuration.ORIENTATION_LANDSCAPE;
148f24f21695f5609d06402cf61e3500d408b99bdcbWinson
149f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Prepare each of the task views for their enter animation from front to back
150f24f21695f5609d06402cf61e3500d408b99bdcbWinson        List<TaskView> taskViews = mStackView.getTaskViews();
151f24f21695f5609d06402cf61e3500d408b99bdcbWinson        for (int i = taskViews.size() - 1; i >= 0; i--) {
152f24f21695f5609d06402cf61e3500d408b99bdcbWinson            TaskView tv = taskViews.get(i);
153f24f21695f5609d06402cf61e3500d408b99bdcbWinson            Task task = tv.getTask();
1548a3ef375ee299e4a84936c87f66098fa9fb84258Winson            boolean currentTaskOccludesLaunchTarget = launchTargetTask != null &&
1558a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchTargetTask.group != null &&
1568a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
1578a3ef375ee299e4a84936c87f66098fa9fb84258Winson            boolean hideTask = launchTargetTask != null &&
1588a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchTargetTask.isFreeformTask() &&
1598a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    task.isFreeformTask();
160f24f21695f5609d06402cf61e3500d408b99bdcbWinson
161f24f21695f5609d06402cf61e3500d408b99bdcbWinson            // Get the current transform for the task, which will be used to position it offscreen
162f24f21695f5609d06402cf61e3500d408b99bdcbWinson            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
16326f637b5553facca5f7873c28d19af36ffccd634Jiaquan He                    null);
164f24f21695f5609d06402cf61e3500d408b99bdcbWinson
165f24f21695f5609d06402cf61e3500d408b99bdcbWinson            if (hideTask) {
166f24f21695f5609d06402cf61e3500d408b99bdcbWinson                tv.setVisibility(View.INVISIBLE);
167c69249fd6f30398780342baed8752ef0eeff7d22Winson            } else if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
168f24f21695f5609d06402cf61e3500d408b99bdcbWinson                if (task.isLaunchTarget) {
169f24f21695f5609d06402cf61e3500d408b99bdcbWinson                    tv.onPrepareLaunchTargetForEnterAnimation();
170f24f21695f5609d06402cf61e3500d408b99bdcbWinson                } else if (currentTaskOccludesLaunchTarget) {
171f24f21695f5609d06402cf61e3500d408b99bdcbWinson                    // Move the task view slightly lower so we can animate it in
172c69249fd6f30398780342baed8752ef0eeff7d22Winson                    mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
173c69249fd6f30398780342baed8752ef0eeff7d22Winson                    mTmpTransform.alpha = 0f;
174c69249fd6f30398780342baed8752ef0eeff7d22Winson                    mStackView.updateTaskViewToTransform(tv, mTmpTransform,
175c69249fd6f30398780342baed8752ef0eeff7d22Winson                            AnimationProps.IMMEDIATE);
17665c851e6e9e08656744b6f66d3da188e3283b17dWinson                    tv.setClipViewInStack(false);
177f24f21695f5609d06402cf61e3500d408b99bdcbWinson                }
178f24f21695f5609d06402cf61e3500d408b99bdcbWinson            } else if (launchState.launchedFromHome) {
179f24f21695f5609d06402cf61e3500d408b99bdcbWinson                // Move the task view off screen (below) so we can animate it in
180c69249fd6f30398780342baed8752ef0eeff7d22Winson                mTmpTransform.rect.offset(0, offscreenYOffset);
181c69249fd6f30398780342baed8752ef0eeff7d22Winson                mTmpTransform.alpha = 0f;
182c69249fd6f30398780342baed8752ef0eeff7d22Winson                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
183c69249fd6f30398780342baed8752ef0eeff7d22Winson            } else if (launchState.launchedViaDockGesture) {
184545c5c20d3b1f424b2f1917a262a5e8130dab6d0Jorim Jaggi                int offset = isLandscape
185545c5c20d3b1f424b2f1917a262a5e8130dab6d0Jorim Jaggi                        ? launchedWhileDockingOffset
186545c5c20d3b1f424b2f1917a262a5e8130dab6d0Jorim Jaggi                        : (int) (offscreenYOffset * 0.9f);
187545c5c20d3b1f424b2f1917a262a5e8130dab6d0Jorim Jaggi                mTmpTransform.rect.offset(0, offset);
188c69249fd6f30398780342baed8752ef0eeff7d22Winson                mTmpTransform.alpha = 0f;
189c69249fd6f30398780342baed8752ef0eeff7d22Winson                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
190f24f21695f5609d06402cf61e3500d408b99bdcbWinson            }
191f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
192f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
193f24f21695f5609d06402cf61e3500d408b99bdcbWinson
194f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
195f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
196f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * depending on how Recents was triggered.
197f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
198f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
199f24f21695f5609d06402cf61e3500d408b99bdcbWinson        RecentsConfiguration config = Recents.getConfiguration();
200f24f21695f5609d06402cf61e3500d408b99bdcbWinson        RecentsActivityLaunchState launchState = config.getLaunchState();
201f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Resources res = mStackView.getResources();
202e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi        Resources appRes = mStackView.getContext().getApplicationContext().getResources();
203f24f21695f5609d06402cf61e3500d408b99bdcbWinson
204f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
205f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStackViewScroller stackScroller = mStackView.getScroller();
206f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStack stack = mStackView.getStack();
207f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Task launchTargetTask = stack.getLaunchTarget();
208f24f21695f5609d06402cf61e3500d408b99bdcbWinson
209f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Break early if there are no tasks
2104b057c6787624b75613769a857ccdf51114bb7f2Winson        if (stack.getTaskCount() == 0) {
211f24f21695f5609d06402cf61e3500d408b99bdcbWinson            return;
212f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
213f24f21695f5609d06402cf61e3500d408b99bdcbWinson
214f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewEnterFromAppDuration = res.getInteger(
215f24f21695f5609d06402cf61e3500d408b99bdcbWinson                R.integer.recents_task_enter_from_app_duration);
21665c851e6e9e08656744b6f66d3da188e3283b17dWinson        int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
21765c851e6e9e08656744b6f66d3da188e3283b17dWinson                R.integer.recents_task_enter_from_affiliated_app_duration);
218e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi        int dockGestureAnimDuration = appRes.getInteger(
219e370e15056d45eba83d6ad96d9be46a17eddff29Jorim Jaggi                R.integer.long_press_dock_anim_duration);
220f24f21695f5609d06402cf61e3500d408b99bdcbWinson
221f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Create enter animations for each of the views from front to back
222f24f21695f5609d06402cf61e3500d408b99bdcbWinson        List<TaskView> taskViews = mStackView.getTaskViews();
223f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewCount = taskViews.size();
224f24f21695f5609d06402cf61e3500d408b99bdcbWinson        for (int i = taskViewCount - 1; i >= 0; i--) {
22550448630a49a9602eb53561d1c022890d07449d0Winson            int taskIndexFromFront = taskViewCount - i - 1;
226899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi            int taskIndexFromBack = i;
22765c851e6e9e08656744b6f66d3da188e3283b17dWinson            final TaskView tv = taskViews.get(i);
228f24f21695f5609d06402cf61e3500d408b99bdcbWinson            Task task = tv.getTask();
2298a3ef375ee299e4a84936c87f66098fa9fb84258Winson            boolean currentTaskOccludesLaunchTarget = launchTargetTask != null &&
2308a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchTargetTask.group != null &&
2318a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
232f24f21695f5609d06402cf61e3500d408b99bdcbWinson
233f24f21695f5609d06402cf61e3500d408b99bdcbWinson            // Get the current transform for the task, which will be updated to the final transform
234f24f21695f5609d06402cf61e3500d408b99bdcbWinson            // to animate to depending on how recents was invoked
235f24f21695f5609d06402cf61e3500d408b99bdcbWinson            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
23626f637b5553facca5f7873c28d19af36ffccd634Jiaquan He                    null);
237f24f21695f5609d06402cf61e3500d408b99bdcbWinson
238c69249fd6f30398780342baed8752ef0eeff7d22Winson            if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
239f24f21695f5609d06402cf61e3500d408b99bdcbWinson                if (task.isLaunchTarget) {
240e693aafe0511c2a7ffc571b22abeefba44046225Winson                    tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
241e693aafe0511c2a7ffc571b22abeefba44046225Winson                            taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
242e693aafe0511c2a7ffc571b22abeefba44046225Winson                            postAnimationTrigger);
243f24f21695f5609d06402cf61e3500d408b99bdcbWinson                } else {
244f24f21695f5609d06402cf61e3500d408b99bdcbWinson                    // Animate the task up if it was occluding the launch target
245f24f21695f5609d06402cf61e3500d408b99bdcbWinson                    if (currentTaskOccludesLaunchTarget) {
246be8e696a1f40a61c52a74022f4f084fdc0600f90Winson                        AnimationProps taskAnimation = new AnimationProps(
247c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson                                taskViewEnterFromAffiliatedAppDuration, Interpolators.ALPHA_IN,
24865c851e6e9e08656744b6f66d3da188e3283b17dWinson                                new AnimatorListenerAdapter() {
24965c851e6e9e08656744b6f66d3da188e3283b17dWinson                                    @Override
25065c851e6e9e08656744b6f66d3da188e3283b17dWinson                                    public void onAnimationEnd(Animator animation) {
25165c851e6e9e08656744b6f66d3da188e3283b17dWinson                                        postAnimationTrigger.decrement();
25259924fe0d9136cf349759bea1e06b661603f95feWinson                                        tv.setClipViewInStack(true);
25365c851e6e9e08656744b6f66d3da188e3283b17dWinson                                    }
25465c851e6e9e08656744b6f66d3da188e3283b17dWinson                                });
255f24f21695f5609d06402cf61e3500d408b99bdcbWinson                        postAnimationTrigger.increment();
256f24f21695f5609d06402cf61e3500d408b99bdcbWinson                        mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
257f24f21695f5609d06402cf61e3500d408b99bdcbWinson                    }
258f24f21695f5609d06402cf61e3500d408b99bdcbWinson                }
259f24f21695f5609d06402cf61e3500d408b99bdcbWinson
260f24f21695f5609d06402cf61e3500d408b99bdcbWinson            } else if (launchState.launchedFromHome) {
2613f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                // Animate the tasks up, but offset the animations to be relative to the front-most
2623f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                // task animation
26350448630a49a9602eb53561d1c022890d07449d0Winson                AnimationProps taskAnimation = new AnimationProps()
2643f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setInitialPlayTime(AnimationProps.BOUNDS,
2653f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
26645c8048f4f8996f085c78621efaf710c907ce213Manu Cornet                                        mEnterAndExitFromHomeTranslationOffset)
2673f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setStartDelay(AnimationProps.ALPHA,
2683f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
2693f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                        FRAME_OFFSET_MS)
2703f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION)
27150448630a49a9602eb53561d1c022890d07449d0Winson                        .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION)
27250448630a49a9602eb53561d1c022890d07449d0Winson                        .setInterpolator(AnimationProps.BOUNDS,
27350448630a49a9602eb53561d1c022890d07449d0Winson                                ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR)
2743f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setInterpolator(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
27550448630a49a9602eb53561d1c022890d07449d0Winson                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
276f24f21695f5609d06402cf61e3500d408b99bdcbWinson                postAnimationTrigger.increment();
277f24f21695f5609d06402cf61e3500d408b99bdcbWinson                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
27859924fe0d9136cf349759bea1e06b661603f95feWinson                if (i == taskViewCount - 1) {
27959924fe0d9136cf349759bea1e06b661603f95feWinson                    tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
28059924fe0d9136cf349759bea1e06b661603f95feWinson                }
281c69249fd6f30398780342baed8752ef0eeff7d22Winson            } else if (launchState.launchedViaDockGesture) {
282c5887ea7a0b82cc5909743ce14cbda1dcf1dfc82Jorim Jaggi                // Animate the tasks up - add some delay to match the divider animation
283899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                AnimationProps taskAnimation = new AnimationProps()
2843f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setDuration(AnimationProps.BOUNDS, dockGestureAnimDuration +
2853f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                (taskIndexFromBack * DOUBLE_FRAME_OFFSET_MS))
286899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                        .setInterpolator(AnimationProps.BOUNDS,
287899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                                ENTER_WHILE_DOCKING_INTERPOLATOR)
288c5887ea7a0b82cc5909743ce14cbda1dcf1dfc82Jorim Jaggi                        .setStartDelay(AnimationProps.BOUNDS, 48)
289899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
290899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                postAnimationTrigger.increment();
291899327f5cbbfb0eae5562b262ccea860c98f6bc4Jorim Jaggi                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
292f24f21695f5609d06402cf61e3500d408b99bdcbWinson            }
293f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
294f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
295f24f21695f5609d06402cf61e3500d408b99bdcbWinson
296f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
297f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Starts an in-app animation to hide all the task views so that we can transition back home.
298f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
299f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public void startExitToHomeAnimation(boolean animated,
300f24f21695f5609d06402cf61e3500d408b99bdcbWinson            ReferenceCountedTrigger postAnimationTrigger) {
301f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
302f24f21695f5609d06402cf61e3500d408b99bdcbWinson        TaskStack stack = mStackView.getStack();
303f24f21695f5609d06402cf61e3500d408b99bdcbWinson
304f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Break early if there are no tasks
3054b057c6787624b75613769a857ccdf51114bb7f2Winson        if (stack.getTaskCount() == 0) {
306f24f21695f5609d06402cf61e3500d408b99bdcbWinson            return;
307f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
308f24f21695f5609d06402cf61e3500d408b99bdcbWinson
30940a227337712a881cca4d7dd04323380287b9e40Winson        int offscreenYOffset = stackLayout.mStackRect.height();
310f24f21695f5609d06402cf61e3500d408b99bdcbWinson
311f24f21695f5609d06402cf61e3500d408b99bdcbWinson        // Create the animations for each of the tasks
312f24f21695f5609d06402cf61e3500d408b99bdcbWinson        List<TaskView> taskViews = mStackView.getTaskViews();
313f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewCount = taskViews.size();
314f24f21695f5609d06402cf61e3500d408b99bdcbWinson        for (int i = 0; i < taskViewCount; i++) {
31550448630a49a9602eb53561d1c022890d07449d0Winson            int taskIndexFromFront = taskViewCount - i - 1;
316f24f21695f5609d06402cf61e3500d408b99bdcbWinson            TaskView tv = taskViews.get(i);
31761560f065a334343d9c552796e1c702f4dfb664bWinson            Task task = tv.getTask();
31861560f065a334343d9c552796e1c702f4dfb664bWinson
31961560f065a334343d9c552796e1c702f4dfb664bWinson            if (mStackView.isIgnoredTask(task)) {
32061560f065a334343d9c552796e1c702f4dfb664bWinson                continue;
32161560f065a334343d9c552796e1c702f4dfb664bWinson            }
32250448630a49a9602eb53561d1c022890d07449d0Winson
32350448630a49a9602eb53561d1c022890d07449d0Winson            // Animate the tasks down
32450448630a49a9602eb53561d1c022890d07449d0Winson            AnimationProps taskAnimation;
32550448630a49a9602eb53561d1c022890d07449d0Winson            if (animated) {
3263f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
32745c8048f4f8996f085c78621efaf710c907ce213Manu Cornet                        mEnterAndExitFromHomeTranslationOffset;
32850448630a49a9602eb53561d1c022890d07449d0Winson                taskAnimation = new AnimationProps()
3293f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setStartDelay(AnimationProps.BOUNDS, delay)
3303f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
33150448630a49a9602eb53561d1c022890d07449d0Winson                        .setInterpolator(AnimationProps.BOUNDS,
33250448630a49a9602eb53561d1c022890d07449d0Winson                                EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
33350448630a49a9602eb53561d1c022890d07449d0Winson                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
33450448630a49a9602eb53561d1c022890d07449d0Winson                postAnimationTrigger.increment();
33550448630a49a9602eb53561d1c022890d07449d0Winson            } else {
33650448630a49a9602eb53561d1c022890d07449d0Winson                taskAnimation = AnimationProps.IMMEDIATE;
33750448630a49a9602eb53561d1c022890d07449d0Winson            }
338f24f21695f5609d06402cf61e3500d408b99bdcbWinson
3393b6ba1ab144c53752841869627a1b9f6d357c404Winson            mTmpTransform.fillIn(tv);
34040a227337712a881cca4d7dd04323380287b9e40Winson            mTmpTransform.rect.offset(0, offscreenYOffset);
341f24f21695f5609d06402cf61e3500d408b99bdcbWinson            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
342f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
343f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
344f24f21695f5609d06402cf61e3500d408b99bdcbWinson
345f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
346f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Starts the animation for the launching task view, hiding any tasks that might occlude the
347f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * window transition for the launching task.
348f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
349f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
350f24f21695f5609d06402cf61e3500d408b99bdcbWinson            final ReferenceCountedTrigger postAnimationTrigger) {
351f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Resources res = mStackView.getResources();
352f24f21695f5609d06402cf61e3500d408b99bdcbWinson
353f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewExitToAppDuration = res.getInteger(
354f24f21695f5609d06402cf61e3500d408b99bdcbWinson                R.integer.recents_task_exit_to_app_duration);
355f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
35659924fe0d9136cf349759bea1e06b661603f95feWinson                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
357f24f21695f5609d06402cf61e3500d408b99bdcbWinson
358f24f21695f5609d06402cf61e3500d408b99bdcbWinson        Task launchingTask = launchingTaskView.getTask();
359f24f21695f5609d06402cf61e3500d408b99bdcbWinson        List<TaskView> taskViews = mStackView.getTaskViews();
360f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskViewCount = taskViews.size();
361f24f21695f5609d06402cf61e3500d408b99bdcbWinson        for (int i = 0; i < taskViewCount; i++) {
362f24f21695f5609d06402cf61e3500d408b99bdcbWinson            TaskView tv = taskViews.get(i);
363f24f21695f5609d06402cf61e3500d408b99bdcbWinson            Task task = tv.getTask();
3648a3ef375ee299e4a84936c87f66098fa9fb84258Winson            boolean currentTaskOccludesLaunchTarget = launchingTask != null &&
3658a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchingTask.group != null &&
3668a3ef375ee299e4a84936c87f66098fa9fb84258Winson                    launchingTask.group.isTaskAboveTask(task, launchingTask);
367f24f21695f5609d06402cf61e3500d408b99bdcbWinson
368f24f21695f5609d06402cf61e3500d408b99bdcbWinson            if (tv == launchingTaskView) {
369f24f21695f5609d06402cf61e3500d408b99bdcbWinson                tv.setClipViewInStack(false);
370a1ededd056d51532ab18354f17f8065ec1134535Winson                postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
371a1ededd056d51532ab18354f17f8065ec1134535Winson                    @Override
372a1ededd056d51532ab18354f17f8065ec1134535Winson                    public void run() {
373a1ededd056d51532ab18354f17f8065ec1134535Winson                        tv.setClipViewInStack(true);
374a1ededd056d51532ab18354f17f8065ec1134535Winson                    }
375a1ededd056d51532ab18354f17f8065ec1134535Winson                });
376f24f21695f5609d06402cf61e3500d408b99bdcbWinson                tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
377f24f21695f5609d06402cf61e3500d408b99bdcbWinson                        screenPinningRequested, postAnimationTrigger);
378f24f21695f5609d06402cf61e3500d408b99bdcbWinson            } else if (currentTaskOccludesLaunchTarget) {
379f24f21695f5609d06402cf61e3500d408b99bdcbWinson                // Animate this task out of view
380be8e696a1f40a61c52a74022f4f084fdc0600f90Winson                AnimationProps taskAnimation = new AnimationProps(
381c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson                        taskViewExitToAppDuration, Interpolators.ALPHA_OUT,
382f24f21695f5609d06402cf61e3500d408b99bdcbWinson                        postAnimationTrigger.decrementOnAnimationEnd());
383f24f21695f5609d06402cf61e3500d408b99bdcbWinson                postAnimationTrigger.increment();
384f24f21695f5609d06402cf61e3500d408b99bdcbWinson
3853b6ba1ab144c53752841869627a1b9f6d357c404Winson                mTmpTransform.fillIn(tv);
386f24f21695f5609d06402cf61e3500d408b99bdcbWinson                mTmpTransform.alpha = 0f;
387f24f21695f5609d06402cf61e3500d408b99bdcbWinson                mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
388f24f21695f5609d06402cf61e3500d408b99bdcbWinson                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
389f24f21695f5609d06402cf61e3500d408b99bdcbWinson            }
390f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
391f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
392f24f21695f5609d06402cf61e3500d408b99bdcbWinson
393f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
394f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Starts the delete animation for the specified {@link TaskView}.
395f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
3965df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    public void startDeleteTaskAnimation(final TaskView deleteTaskView, boolean gridLayout,
397f24f21695f5609d06402cf61e3500d408b99bdcbWinson            final ReferenceCountedTrigger postAnimationTrigger) {
3985df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        if (gridLayout) {
3995df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            startTaskGridDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
4005df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        } else {
4015df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            startTaskStackDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
4025df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        }
403f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
404f24f21695f5609d06402cf61e3500d408b99bdcbWinson
405f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
4063b6ba1ab144c53752841869627a1b9f6d357c404Winson     * Starts the delete animation for all the {@link TaskView}s.
4073b6ba1ab144c53752841869627a1b9f6d357c404Winson     */
4085df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    public void startDeleteAllTasksAnimation(final List<TaskView> taskViews, boolean gridLayout,
4095df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            final ReferenceCountedTrigger postAnimationTrigger) {
4105df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        if (gridLayout) {
4115df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            for (int i = 0; i < taskViews.size(); i++) {
4125df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                startTaskGridDeleteTaskAnimation(taskViews.get(i), postAnimationTrigger);
4135df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            }
4145df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        } else {
4155df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            startTaskStackDeleteAllTasksAnimation(taskViews, postAnimationTrigger);
4163b6ba1ab144c53752841869627a1b9f6d357c404Winson        }
4173b6ba1ab144c53752841869627a1b9f6d357c404Winson    }
4183b6ba1ab144c53752841869627a1b9f6d357c404Winson
4193b6ba1ab144c53752841869627a1b9f6d357c404Winson    /**
42005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson     * Starts the animation to focus the next {@link TaskView} when paging through recents.
42105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson     *
42205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson     * @return whether or not this will trigger a scroll in the stack
42305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson     */
42405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson    public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
42505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            boolean requestViewFocus) {
42605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
42705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        TaskStackViewScroller stackScroller = mStackView.getScroller();
42805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        TaskStack stack = mStackView.getStack();
42905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
4303f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        final float curScroll = stackScroller.getStackScroll();
431aeb298c4db913b376fb6053ec30c132a93e04635Winson        final float newScroll = stackScroller.getBoundedStackScroll(
432aeb298c4db913b376fb6053ec30c132a93e04635Winson                stackLayout.getStackScrollForTask(newFocusedTask));
4333f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        boolean willScrollToFront = newScroll > curScroll;
4343f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        boolean willScroll = Float.compare(newScroll, curScroll) != 0;
43505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
43605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Get the current set of task transforms
4373f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        int taskViewCount = mStackView.getTaskViews().size();
43805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        ArrayList<Task> stackTasks = stack.getStackTasks();
43905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
44005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
44105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Pick up the newly visible views after the scroll
44205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        mStackView.bindVisibleTaskViews(newScroll);
44305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
44405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Update the internal state
44505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
44605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        stackScroller.setStackScroll(newScroll, null /* animation */);
44705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        mStackView.cancelDeferredTaskViewLayoutAnimation();
44805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
44905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Get the final set of task transforms
4501499150478b2836a7d2549129ccaed005b24bc06Winson        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
451d2a030613154e2007d9816e090c39e9726e7adbaWinson                true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
45205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
45305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Focus the task view
45405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
4553f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        if (newFocusedTaskView == null) {
4563f3d744d5c51d3ecc06af22ac7d69f4437671915Winson            // Log the error if we have no task view, and skip the animation
4573f3d744d5c51d3ecc06af22ac7d69f4437671915Winson            Log.e("TaskStackAnimationHelper", "b/27389156 null-task-view prebind:" + taskViewCount +
4583f3d744d5c51d3ecc06af22ac7d69f4437671915Winson                    " postbind:" + mStackView.getTaskViews().size() + " prescroll:" + curScroll +
4593f3d744d5c51d3ecc06af22ac7d69f4437671915Winson                    " postscroll: " + newScroll);
4603f3d744d5c51d3ecc06af22ac7d69f4437671915Winson            return false;
4613f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        }
46205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        newFocusedTaskView.setFocusedState(true, requestViewFocus);
46305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
46405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // Setup the end listener to return all the hidden views to the view pool after the
46505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        // focus animation
46691b225d41f0522c6c106fe2df106ecd5c1f87df9Winson        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
46791b225d41f0522c6c106fe2df106ecd5c1f87df9Winson        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
46805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            @Override
46991b225d41f0522c6c106fe2df106ecd5c1f87df9Winson            public void run() {
47005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                mStackView.bindVisibleTaskViews(newScroll);
47105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            }
47291b225d41f0522c6c106fe2df106ecd5c1f87df9Winson        });
47305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
47405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        List<TaskView> taskViews = mStackView.getTaskViews();
4753f3d744d5c51d3ecc06af22ac7d69f4437671915Winson        taskViewCount = taskViews.size();
47605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
47705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        for (int i = 0; i < taskViewCount; i++) {
47805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            TaskView tv = taskViews.get(i);
47905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            Task task = tv.getTask();
48005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
48105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            if (mStackView.isIgnoredTask(task)) {
48205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                continue;
48305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            }
48405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
48505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            int taskIndex = stackTasks.indexOf(task);
48605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
48705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
48805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
48905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            // Update the task to the initial state (for the newly picked up tasks)
49005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
49105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
49205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            int duration;
49305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            Interpolator interpolator;
49405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            if (willScrollToFront) {
495d2a030613154e2007d9816e090c39e9726e7adbaWinson                duration = calculateStaggeredAnimDuration(i);
49605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
49705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            } else {
49805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                if (i < newFocusTaskViewIndex) {
49905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
50005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
50105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                } else if (i > newFocusTaskViewIndex) {
50205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
50305e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
50405e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                } else {
50505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    duration = 200;
50605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
50705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                }
50805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            }
50905e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson
51005e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            AnimationProps anim = new AnimationProps()
51105e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    .setDuration(AnimationProps.BOUNDS, duration)
51205e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
51391b225d41f0522c6c106fe2df106ecd5c1f87df9Winson                    .setListener(postAnimTrigger.decrementOnAnimationEnd());
51491b225d41f0522c6c106fe2df106ecd5c1f87df9Winson            postAnimTrigger.increment();
51505e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
51605e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        }
51705e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson        return willScroll;
51805e46ca8f3d24cf333e745c8cebb420b2996e9eaWinson    }
519d2a030613154e2007d9816e090c39e9726e7adbaWinson
520d2a030613154e2007d9816e090c39e9726e7adbaWinson    /**
521d2a030613154e2007d9816e090c39e9726e7adbaWinson     * Starts the animation to go to the initial stack layout with a task focused.  In addition, the
522d2a030613154e2007d9816e090c39e9726e7adbaWinson     * previous task will be animated in after the scroll completes.
523d2a030613154e2007d9816e090c39e9726e7adbaWinson     */
524d2a030613154e2007d9816e090c39e9726e7adbaWinson    public void startNewStackScrollAnimation(TaskStack newStack,
525d2a030613154e2007d9816e090c39e9726e7adbaWinson            ReferenceCountedTrigger animationTrigger) {
526d2a030613154e2007d9816e090c39e9726e7adbaWinson        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
527d2a030613154e2007d9816e090c39e9726e7adbaWinson        TaskStackViewScroller stackScroller = mStackView.getScroller();
528d2a030613154e2007d9816e090c39e9726e7adbaWinson
529d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Get the current set of task transforms
530d2a030613154e2007d9816e090c39e9726e7adbaWinson        ArrayList<Task> stackTasks = newStack.getStackTasks();
531d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
532d2a030613154e2007d9816e090c39e9726e7adbaWinson
533d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Update the stack
534d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
535d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.updateLayoutAlgorithm(false /* boundScroll */);
536d2a030613154e2007d9816e090c39e9726e7adbaWinson
537d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Pick up the newly visible views after the scroll
538d2a030613154e2007d9816e090c39e9726e7adbaWinson        final float newScroll = stackLayout.mInitialScrollP;
539d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.bindVisibleTaskViews(newScroll);
540d2a030613154e2007d9816e090c39e9726e7adbaWinson
541d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Update the internal state
542d2a030613154e2007d9816e090c39e9726e7adbaWinson        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
543d2a030613154e2007d9816e090c39e9726e7adbaWinson        stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
544d2a030613154e2007d9816e090c39e9726e7adbaWinson        stackScroller.setStackScroll(newScroll);
545d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.cancelDeferredTaskViewLayoutAnimation();
546d2a030613154e2007d9816e090c39e9726e7adbaWinson
547d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Get the final set of task transforms
548d2a030613154e2007d9816e090c39e9726e7adbaWinson        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
549d2a030613154e2007d9816e090c39e9726e7adbaWinson                false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
550d2a030613154e2007d9816e090c39e9726e7adbaWinson
551d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Hide the front most task view until the scroll is complete
552d2a030613154e2007d9816e090c39e9726e7adbaWinson        Task frontMostTask = newStack.getStackFrontMostTask(false /* includeFreeform */);
553d2a030613154e2007d9816e090c39e9726e7adbaWinson        final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
554d2a030613154e2007d9816e090c39e9726e7adbaWinson        final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
555d2a030613154e2007d9816e090c39e9726e7adbaWinson                stackTasks.indexOf(frontMostTask));
556d2a030613154e2007d9816e090c39e9726e7adbaWinson        if (frontMostTaskView != null) {
557d2a030613154e2007d9816e090c39e9726e7adbaWinson            mStackView.updateTaskViewToTransform(frontMostTaskView,
558d2a030613154e2007d9816e090c39e9726e7adbaWinson                    stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
559d2a030613154e2007d9816e090c39e9726e7adbaWinson        }
560d2a030613154e2007d9816e090c39e9726e7adbaWinson
561d2a030613154e2007d9816e090c39e9726e7adbaWinson        // Setup the end listener to return all the hidden views to the view pool after the
562d2a030613154e2007d9816e090c39e9726e7adbaWinson        // focus animation
563d2a030613154e2007d9816e090c39e9726e7adbaWinson        animationTrigger.addLastDecrementRunnable(new Runnable() {
564d2a030613154e2007d9816e090c39e9726e7adbaWinson            @Override
565d2a030613154e2007d9816e090c39e9726e7adbaWinson            public void run() {
566d2a030613154e2007d9816e090c39e9726e7adbaWinson                mStackView.bindVisibleTaskViews(newScroll);
567d2a030613154e2007d9816e090c39e9726e7adbaWinson
568d2a030613154e2007d9816e090c39e9726e7adbaWinson                // Now, animate in the front-most task
569d2a030613154e2007d9816e090c39e9726e7adbaWinson                if (frontMostTaskView != null) {
570d2a030613154e2007d9816e090c39e9726e7adbaWinson                    mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
5713f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                            new AnimationProps(75, 250, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
572d2a030613154e2007d9816e090c39e9726e7adbaWinson                }
573d2a030613154e2007d9816e090c39e9726e7adbaWinson            }
574d2a030613154e2007d9816e090c39e9726e7adbaWinson        });
575d2a030613154e2007d9816e090c39e9726e7adbaWinson
576d2a030613154e2007d9816e090c39e9726e7adbaWinson        List<TaskView> taskViews = mStackView.getTaskViews();
577d2a030613154e2007d9816e090c39e9726e7adbaWinson        int taskViewCount = taskViews.size();
578d2a030613154e2007d9816e090c39e9726e7adbaWinson        for (int i = 0; i < taskViewCount; i++) {
579d2a030613154e2007d9816e090c39e9726e7adbaWinson            TaskView tv = taskViews.get(i);
580d2a030613154e2007d9816e090c39e9726e7adbaWinson            Task task = tv.getTask();
581d2a030613154e2007d9816e090c39e9726e7adbaWinson
582d2a030613154e2007d9816e090c39e9726e7adbaWinson            if (mStackView.isIgnoredTask(task)) {
583d2a030613154e2007d9816e090c39e9726e7adbaWinson                continue;
584d2a030613154e2007d9816e090c39e9726e7adbaWinson            }
585d2a030613154e2007d9816e090c39e9726e7adbaWinson            if (task == frontMostTask && frontMostTaskView != null) {
586d2a030613154e2007d9816e090c39e9726e7adbaWinson                continue;
587d2a030613154e2007d9816e090c39e9726e7adbaWinson            }
588d2a030613154e2007d9816e090c39e9726e7adbaWinson
589d2a030613154e2007d9816e090c39e9726e7adbaWinson            int taskIndex = stackTasks.indexOf(task);
590d2a030613154e2007d9816e090c39e9726e7adbaWinson            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
591d2a030613154e2007d9816e090c39e9726e7adbaWinson            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
592d2a030613154e2007d9816e090c39e9726e7adbaWinson
593d2a030613154e2007d9816e090c39e9726e7adbaWinson            // Update the task to the initial state (for the newly picked up tasks)
594d2a030613154e2007d9816e090c39e9726e7adbaWinson            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
595d2a030613154e2007d9816e090c39e9726e7adbaWinson
596d2a030613154e2007d9816e090c39e9726e7adbaWinson            int duration = calculateStaggeredAnimDuration(i);
597d2a030613154e2007d9816e090c39e9726e7adbaWinson            Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
598d2a030613154e2007d9816e090c39e9726e7adbaWinson
599d2a030613154e2007d9816e090c39e9726e7adbaWinson            AnimationProps anim = new AnimationProps()
600d2a030613154e2007d9816e090c39e9726e7adbaWinson                    .setDuration(AnimationProps.BOUNDS, duration)
601d2a030613154e2007d9816e090c39e9726e7adbaWinson                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
602d2a030613154e2007d9816e090c39e9726e7adbaWinson                    .setListener(animationTrigger.decrementOnAnimationEnd());
603d2a030613154e2007d9816e090c39e9726e7adbaWinson            animationTrigger.increment();
604d2a030613154e2007d9816e090c39e9726e7adbaWinson            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
605d2a030613154e2007d9816e090c39e9726e7adbaWinson        }
606d2a030613154e2007d9816e090c39e9726e7adbaWinson    }
607d2a030613154e2007d9816e090c39e9726e7adbaWinson
608d2a030613154e2007d9816e090c39e9726e7adbaWinson    /**
609d2a030613154e2007d9816e090c39e9726e7adbaWinson     * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
610d2a030613154e2007d9816e090c39e9726e7adbaWinson     * {@link #startNewStackScrollAnimation}.
611d2a030613154e2007d9816e090c39e9726e7adbaWinson     */
612d2a030613154e2007d9816e090c39e9726e7adbaWinson    private int calculateStaggeredAnimDuration(int i) {
613d2a030613154e2007d9816e090c39e9726e7adbaWinson        return Math.max(100, 100 + ((i - 1) * 50));
614d2a030613154e2007d9816e090c39e9726e7adbaWinson    }
6155df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6165df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    private void startTaskGridDeleteTaskAnimation(final TaskView deleteTaskView,
6175df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            final ReferenceCountedTrigger postAnimationTrigger) {
6185df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        postAnimationTrigger.increment();
6195df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        postAnimationTrigger.addLastDecrementRunnable(() -> {
6205df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            mStackView.getTouchHandler().onChildDismissed(deleteTaskView);
6215df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        });
6228ea36af22b96189a3f6482d4e5703db34bd48399Manu Cornet        deleteTaskView.animate().setDuration(300).scaleX(0.9f).scaleY(0.9f).alpha(0).setListener(
6235df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                new AnimatorListenerAdapter() {
6245df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    @Override
6255df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    public void onAnimationEnd(Animator animation) {
6265df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                        postAnimationTrigger.decrement();
6275df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    }}).start();
6285df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    }
6295df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6305df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    private void startTaskStackDeleteTaskAnimation(final TaskView deleteTaskView,
6315df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            final ReferenceCountedTrigger postAnimationTrigger) {
6325df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        TaskStackViewTouchHandler touchHandler = mStackView.getTouchHandler();
6335df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        touchHandler.onBeginManualDrag(deleteTaskView);
6345df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6355df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        postAnimationTrigger.increment();
6365df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        postAnimationTrigger.addLastDecrementRunnable(() -> {
6375df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            touchHandler.onChildDismissed(deleteTaskView);
6385df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        });
6395df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6405df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        final float dismissSize = touchHandler.getScaledDismissSize();
6415df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
6425df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        animator.setDuration(400);
6435df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        animator.addUpdateListener((animation) -> {
6445df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            float progress = (Float) animation.getAnimatedValue();
6455df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            deleteTaskView.setTranslationX(progress * dismissSize);
6465df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            touchHandler.updateSwipeProgress(deleteTaskView, true, progress);
6475df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        });
6485df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        animator.addListener(new AnimatorListenerAdapter() {
6495df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            @Override
6505df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            public void onAnimationEnd(Animator animation) {
6515df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                postAnimationTrigger.decrement();
6525df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            }
6535df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        });
6545df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        animator.start();
6555df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    }
6565df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6575df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    private void startTaskStackDeleteAllTasksAnimation(final List<TaskView> taskViews,
6585df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            final ReferenceCountedTrigger postAnimationTrigger) {
6595df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
6605df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6615df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.getTaskRect().left;
6625df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6635df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        int taskViewCount = taskViews.size();
6645df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        for (int i = taskViewCount - 1; i >= 0; i--) {
6655df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            TaskView tv = taskViews.get(i);
6665df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            int taskIndexFromFront = taskViewCount - i - 1;
6675df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            int startDelay = taskIndexFromFront * DOUBLE_FRAME_OFFSET_MS;
6685df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6695df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            // Disabling clipping with the stack while the view is animating away
6705df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            tv.setClipViewInStack(false);
6715df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6725df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            // Compose the new animation and transform and star the animation
6735df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            AnimationProps taskAnimation = new AnimationProps(startDelay,
6745df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    DISMISS_ALL_TASKS_DURATION, DISMISS_ALL_TRANSLATION_INTERPOLATOR,
6755df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    new AnimatorListenerAdapter() {
6765df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                        @Override
6775df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                        public void onAnimationEnd(Animator animation) {
6785df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                            postAnimationTrigger.decrement();
6795df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6805df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                            // Re-enable clipping with the stack (we will reuse this view)
6815df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                            tv.setClipViewInStack(true);
6825df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                        }
6835df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet                    });
6845df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            postAnimationTrigger.increment();
6855df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet
6865df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            mTmpTransform.fillIn(tv);
6875df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            mTmpTransform.rect.offset(offscreenXOffset, 0);
6885df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
6895df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet        }
6905df7667edbabdf8bf43bc08ef7df2859fd620314Manu Cornet    }
691f24f21695f5609d06402cf61e3500d408b99bdcbWinson}
692