1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
19import com.android.systemui.recents.Constants;
20import com.android.systemui.recents.RecentsConfiguration;
21import com.android.systemui.recents.model.Task;
22
23import java.util.ArrayList;
24import java.util.HashMap;
25
26/* The layout logic for a TaskStackView */
27public class TaskStackViewFilterAlgorithm {
28
29    RecentsConfiguration mConfig;
30    TaskStackView mStackView;
31    ViewPool<TaskView, Task> mViewPool;
32
33    public TaskStackViewFilterAlgorithm(RecentsConfiguration config, TaskStackView stackView,
34                                        ViewPool<TaskView, Task> viewPool) {
35        mConfig = config;
36        mStackView = stackView;
37        mViewPool = viewPool;
38    }
39
40    /** Orchestrates the animations of the current child views and any new views. */
41    void startFilteringAnimation(ArrayList<Task> curTasks,
42                                 ArrayList<TaskViewTransform> curTaskTransforms,
43                                 final ArrayList<Task> tasks,
44                                 final ArrayList<TaskViewTransform> taskTransforms) {
45        // Calculate the transforms to animate out all the existing views if they are not in the
46        // new visible range (or to their final positions in the stack if they are)
47        final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
48        final HashMap<TaskView, TaskViewTransform> childViewTransforms =
49                new HashMap<TaskView, TaskViewTransform>();
50        int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
51                taskTransforms, childViewTransforms, childrenToRemove);
52
53        // If all the current views are in the visible range of the new stack, then don't wait for
54        // views to animate out and animate all the new views into their place
55        final boolean unifyNewViewAnimation = childrenToRemove.isEmpty();
56        if (unifyNewViewAnimation) {
57            int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms,
58                    childViewTransforms);
59            duration = Math.max(duration, inDuration);
60        }
61
62        // Animate all the views to their final transforms
63        for (final TaskView tv : childViewTransforms.keySet()) {
64            TaskViewTransform t = childViewTransforms.get(tv);
65            tv.animate().cancel();
66            tv.animate()
67                    .withEndAction(new Runnable() {
68                        @Override
69                        public void run() {
70                            childViewTransforms.remove(tv);
71                            if (childViewTransforms.isEmpty()) {
72                                // Return all the removed children to the view pool
73                                for (TaskView tv : childrenToRemove) {
74                                    mViewPool.returnViewToPool(tv);
75                                }
76
77                                if (!unifyNewViewAnimation) {
78                                    // For views that are not already visible, animate them in
79                                    childViewTransforms.clear();
80                                    int duration = getEnterTransformsForFilterAnimation(tasks,
81                                            taskTransforms, childViewTransforms);
82                                    for (final TaskView tv : childViewTransforms.keySet()) {
83                                        TaskViewTransform t = childViewTransforms.get(tv);
84                                        tv.updateViewPropertiesToTaskTransform(t, duration);
85                                    }
86                                }
87                            }
88                        }
89                    });
90            tv.updateViewPropertiesToTaskTransform(t, duration);
91        }
92    }
93
94    /**
95     * Creates the animations for all the children views that need to be animated in when we are
96     * un/filtering a stack, and returns the duration for these animations.
97     */
98    int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
99                                             ArrayList<TaskViewTransform> taskTransforms,
100                                             HashMap<TaskView, TaskViewTransform> childViewTransformsOut) {
101        int offset = 0;
102        int movement = 0;
103        int taskCount = tasks.size();
104        for (int i = taskCount - 1; i >= 0; i--) {
105            Task task = tasks.get(i);
106            TaskViewTransform toTransform = taskTransforms.get(i);
107            if (toTransform.visible) {
108                TaskView tv = mStackView.getChildViewForTask(task);
109                if (tv == null) {
110                    // For views that are not already visible, animate them in
111                    tv = mViewPool.pickUpViewFromPool(task, task);
112
113                    // Compose a new transform to fade and slide the new task in
114                    TaskViewTransform fromTransform = new TaskViewTransform(toTransform);
115                    tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
116                    tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
117
118                    toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
119                    childViewTransformsOut.put(tv, toTransform);
120
121                    // Use the movement of the new views to calculate the duration of the animation
122                    movement = Math.max(movement,
123                            Math.abs(toTransform.translationY - fromTransform.translationY));
124                    offset++;
125                }
126            }
127        }
128        return mConfig.filteringNewViewsAnimDuration;
129    }
130
131    /**
132     * Creates the animations for all the children views that need to be removed or to move views
133     * to their un/filtered position when we are un/filtering a stack, and returns the duration
134     * for these animations.
135     */
136    int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks,
137                                            ArrayList<TaskViewTransform> curTaskTransforms,
138                                            ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
139                                            HashMap<TaskView, TaskViewTransform> childViewTransformsOut,
140                                            ArrayList<TaskView> childrenToRemoveOut) {
141        // Animate all of the existing views out of view (if they are not in the visible range in
142        // the new stack) or to their final positions in the new stack
143        int offset = 0;
144        int movement = 0;
145        int childCount = mStackView.getChildCount();
146        for (int i = 0; i < childCount; i++) {
147            TaskView tv = (TaskView) mStackView.getChildAt(i);
148            Task task = tv.getTask();
149            int taskIndex = tasks.indexOf(task);
150            TaskViewTransform toTransform;
151
152            // If the view is no longer visible, then we should just animate it out
153            boolean willBeInvisible = taskIndex < 0 || !taskTransforms.get(taskIndex).visible;
154            if (willBeInvisible) {
155                if (taskIndex < 0) {
156                    toTransform = curTaskTransforms.get(curTasks.indexOf(task));
157                } else {
158                    toTransform = new TaskViewTransform(taskTransforms.get(taskIndex));
159                }
160                tv.prepareTaskTransformForFilterTaskVisible(toTransform);
161                childrenToRemoveOut.add(tv);
162            } else {
163                toTransform = taskTransforms.get(taskIndex);
164                // Use the movement of the visible views to calculate the duration of the animation
165                movement = Math.max(movement, Math.abs(toTransform.translationY -
166                        (int) tv.getTranslationY()));
167            }
168
169            toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
170            childViewTransformsOut.put(tv, toTransform);
171            offset++;
172        }
173        return mConfig.filteringCurrentViewsAnimDuration;
174    }
175
176}