1303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung/*
2303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * Copyright (C) 2014 The Android Open Source Project
3303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung *
4303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * you may not use this file except in compliance with the License.
6303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * You may obtain a copy of the License at
7303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung *
8303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung *
10303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * Unless required by applicable law or agreed to in writing, software
11303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * See the License for the specific language governing permissions and
14303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * limitations under the License.
15303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung */
16303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
17303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chungpackage com.android.systemui.recents.model;
18303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
1959924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
2059924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
2159924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
2259924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
2359924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.view.WindowManager.DOCKED_BOTTOM;
2459924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.view.WindowManager.DOCKED_INVALID;
2559924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.view.WindowManager.DOCKED_LEFT;
2659924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.view.WindowManager.DOCKED_RIGHT;
2759924fe0d9136cf349759bea1e06b661603f95feWinsonimport static android.view.WindowManager.DOCKED_TOP;
2859924fe0d9136cf349759bea1e06b661603f95feWinson
293e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.animation.Animator;
303e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.animation.AnimatorSet;
31882072baacaee4ecd43f0209b691a9af746462f2Winsonimport android.animation.ObjectAnimator;
323e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.animation.PropertyValuesHolder;
3399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport android.annotation.IntDef;
34e7f138c7f0a190c86cec10fb32fa106aacae4093Winsonimport android.content.ComponentName;
3535f3050959e43bf378f9a0adcaef13729206c7e4Winsonimport android.content.Context;
363e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.content.res.Configuration;
373e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.content.res.Resources;
3899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport android.graphics.Canvas;
39ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chungimport android.graphics.Color;
4099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport android.graphics.Paint;
4199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport android.graphics.Point;
42be7607af8875236b9cf7bdb5f5aa089c207529afWinsonimport android.graphics.Rect;
43be7607af8875236b9cf7bdb5f5aa089c207529afWinsonimport android.graphics.RectF;
44882072baacaee4ecd43f0209b691a9af746462f2Winsonimport android.graphics.drawable.ColorDrawable;
455500390a006f2bbea565068234774a36cea076c0Winsonimport android.util.ArrayMap;
465500390a006f2bbea565068234774a36cea076c0Winsonimport android.util.ArraySet;
4799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport android.util.IntProperty;
482b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chungimport android.util.SparseArray;
493e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport android.view.animation.Interpolator;
50c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
513e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winsonimport com.android.internal.policy.DockedDividerUtils;
523f32e7eba7aec02edbf542cc11eee13264a5abf2Winsonimport com.android.systemui.Interpolators;
532536c7ed446203ea12b38cf05a88e603f8d1b768Winsonimport com.android.systemui.R;
54e7f138c7f0a190c86cec10fb32fa106aacae4093Winsonimport com.android.systemui.recents.Recents;
55c742f973b1e506732911c156c5869fd377afc5bfWinsonimport com.android.systemui.recents.RecentsDebugFlags;
56ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chungimport com.android.systemui.recents.misc.NamedCounter;
57e7f138c7f0a190c86cec10fb32fa106aacae4093Winsonimport com.android.systemui.recents.misc.SystemServicesProxy;
58a0e88b5013d708ac6ed6518817d83c64c87ae4b1Winson Chungimport com.android.systemui.recents.misc.Utilities;
59be8e696a1f40a61c52a74022f4f084fdc0600f90Winsonimport com.android.systemui.recents.views.AnimationProps;
6059924fe0d9136cf349759bea1e06b661603f95feWinsonimport com.android.systemui.recents.views.DropTarget;
6159924fe0d9136cf349759bea1e06b661603f95feWinsonimport com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
62303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
63d72c315d5eed3a684eee303364b97129cb821689Winsonimport java.io.PrintWriter;
6499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport java.lang.annotation.Retention;
6599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winsonimport java.lang.annotation.RetentionPolicy;
66303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chungimport java.util.ArrayList;
67ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chungimport java.util.Collections;
68ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chungimport java.util.Comparator;
69303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chungimport java.util.List;
70a433fa9c17772f563163ff7db177d091d6aebd5bWinson Chungimport java.util.Random;
71303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
72303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
73303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung/**
74303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * An interface for a task filter to query whether a particular task should show in a stack.
75303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung */
76303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chunginterface TaskFilter {
77303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns whether the filter accepts the specified task */
782b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung    public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
79303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung}
80303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
81303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung/**
82303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * A list of filtered tasks.
83303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung */
84303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chungclass FilteredTaskList {
85eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson
86eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    ArrayList<Task> mTasks = new ArrayList<>();
87eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    ArrayList<Task> mFilteredTasks = new ArrayList<>();
885500390a006f2bbea565068234774a36cea076c0Winson    ArrayMap<Task.TaskKey, Integer> mTaskIndices = new ArrayMap<>();
89303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    TaskFilter mFilter;
90303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
91303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Sets the task filter, saving the current touch state */
92c6a1623cc48581380b698ae87b43bfafb9c935baWinson Chung    boolean setFilter(TaskFilter filter) {
935500390a006f2bbea565068234774a36cea076c0Winson        ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
94303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mFilter = filter;
95303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        updateFilteredTasks();
96b44c24fb50845dfbc1f49e78085cf5e01a32067fWinson Chung        if (!prevFilteredTasks.equals(mFilteredTasks)) {
97b44c24fb50845dfbc1f49e78085cf5e01a32067fWinson Chung            return true;
98b44c24fb50845dfbc1f49e78085cf5e01a32067fWinson Chung        } else {
99b44c24fb50845dfbc1f49e78085cf5e01a32067fWinson Chung            return false;
100b44c24fb50845dfbc1f49e78085cf5e01a32067fWinson Chung        }
101303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
102303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
103303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Removes the task filter and returns the previous touch state */
104303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    void removeFilter() {
105303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mFilter = null;
106303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        updateFilteredTasks();
107303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
108303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
109303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Adds a new task to the task list */
110303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    void add(Task t) {
111303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mTasks.add(t);
112303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        updateFilteredTasks();
113303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
114303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
115eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    /**
116eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson     * Moves the given task.
117eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson     */
118eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
119eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        int taskIndex = indexOf(task);
120eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        if (taskIndex != insertIndex) {
121eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            mTasks.remove(taskIndex);
122eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            if (taskIndex < insertIndex) {
123eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson                insertIndex--;
124eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            }
125eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            mTasks.add(insertIndex, task);
126eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        }
127eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson
128eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        // Update the stack id now, after we've moved the task, and before we update the
129eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        // filtered tasks
130eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        task.setStackId(newStackId);
131eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        updateFilteredTasks();
132eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    }
133eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson
134303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Sets the list of tasks */
135303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    void set(List<Task> tasks) {
136303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mTasks.clear();
137303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mTasks.addAll(tasks);
138303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        updateFilteredTasks();
139303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
140303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
141303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Removes a task from the base list only if it is in the filtered list */
142303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    boolean remove(Task t) {
143303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        if (mFilteredTasks.contains(t)) {
144303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            boolean removed = mTasks.remove(t);
145303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            updateFilteredTasks();
146303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            return removed;
147303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        }
148303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        return false;
149303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
150303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
151303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns the index of this task in the list of filtered tasks */
152303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    int indexOf(Task t) {
15323746d51d922d3df2cdd2635d0c133366c754438Winson        if (t != null && mTaskIndices.containsKey(t.key)) {
154a4ccb86ddc8f9f486aee25fb836f4aff97bf7679Winson Chung            return mTaskIndices.get(t.key);
155a4ccb86ddc8f9f486aee25fb836f4aff97bf7679Winson Chung        }
156a4ccb86ddc8f9f486aee25fb836f4aff97bf7679Winson Chung        return -1;
157303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
158303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
159303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns the size of the list of filtered tasks */
160303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    int size() {
161303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        return mFilteredTasks.size();
162303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
163303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
164303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns whether the filtered list contains this task */
165303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    boolean contains(Task t) {
166ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        return mTaskIndices.containsKey(t.key);
167303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
168303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
169303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Updates the list of filtered tasks whenever the base task list changes */
170303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    private void updateFilteredTasks() {
171303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mFilteredTasks.clear();
172303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        if (mFilter != null) {
1732b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            // Create a sparse array from task id to Task
1742b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            SparseArray<Task> taskIdMap = new SparseArray<>();
175303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            int taskCount = mTasks.size();
176303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            for (int i = 0; i < taskCount; i++) {
177303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung                Task t = mTasks.get(i);
1782b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung                taskIdMap.put(t.key.id, t);
1792b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            }
1802b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung
1812b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            for (int i = 0; i < taskCount; i++) {
1822b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung                Task t = mTasks.get(i);
1832b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung                if (mFilter.acceptTask(taskIdMap, t, i)) {
184303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung                    mFilteredTasks.add(t);
185303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung                }
186303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            }
187303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        } else {
188303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            mFilteredTasks.addAll(mTasks);
189303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        }
190ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        updateFilteredTaskIndices();
191ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
192ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
193ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /** Updates the mapping of tasks to indices. */
194ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    private void updateFilteredTaskIndices() {
195ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        int taskCount = mFilteredTasks.size();
1965500390a006f2bbea565068234774a36cea076c0Winson        mTaskIndices.clear();
197ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        for (int i = 0; i < taskCount; i++) {
198ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            Task t = mFilteredTasks.get(i);
199ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            mTaskIndices.put(t.key, i);
200ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        }
201303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
202303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
203303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns whether this task list is filtered */
204303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    boolean hasFilter() {
205303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        return (mFilter != null);
206303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
207303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
208303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns the list of filtered tasks */
209303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    ArrayList<Task> getTasks() {
210303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        return mFilteredTasks;
211303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
212303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung}
213303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
214303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung/**
215303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung * The task stack contains a list of multiple tasks.
216303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung */
217303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chungpublic class TaskStack {
218ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
219d72c315d5eed3a684eee303364b97129cb821689Winson    private static final String TAG = "TaskStack";
220d72c315d5eed3a684eee303364b97129cb821689Winson
221ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /** Task stack callbacks */
22204dfe0d26b944324ee920001f40d74cff47281d6Winson Chung    public interface TaskStackCallbacks {
223062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        /**
224062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung         * Notifies when a new task has been added to the stack.
225062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung         */
226062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        void onStackTaskAdded(TaskStack stack, Task newTask);
227062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
228062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        /**
229062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung         * Notifies when a task has been removed from the stack.
230062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung         */
2316c8217ab3072fa65851316a922b482506b3c2a4cWinson        void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
2326c8217ab3072fa65851316a922b482506b3c2a4cWinson                AnimationProps animation, boolean fromDockGesture);
233a1ededd056d51532ab18354f17f8065ec1134535Winson
234a1ededd056d51532ab18354f17f8065ec1134535Winson        /**
2353b6ba1ab144c53752841869627a1b9f6d357c404Winson         * Notifies when all tasks have been removed from the stack.
2363b6ba1ab144c53752841869627a1b9f6d357c404Winson         */
2373b6ba1ab144c53752841869627a1b9f6d357c404Winson        void onStackTasksRemoved(TaskStack stack);
2383b6ba1ab144c53752841869627a1b9f6d357c404Winson
2393b6ba1ab144c53752841869627a1b9f6d357c404Winson        /**
240a1ededd056d51532ab18354f17f8065ec1134535Winson         * Notifies when tasks in the stack have been updated.
241a1ededd056d51532ab18354f17f8065ec1134535Winson         */
242a1ededd056d51532ab18354f17f8065ec1134535Winson        void onStackTasksUpdated(TaskStack stack);
24304dfe0d26b944324ee920001f40d74cff47281d6Winson Chung    }
24404dfe0d26b944324ee920001f40d74cff47281d6Winson Chung
245250608a5cd08862f4752a924d51710805850db8aWinson    /**
246250608a5cd08862f4752a924d51710805850db8aWinson     * The various possible dock states when dragging and dropping a task.
247250608a5cd08862f4752a924d51710805850db8aWinson     */
248f0d1c44a59a10707baa0cca8dd377302260710c1Winson    public static class DockState implements DropTarget {
249f0d1c44a59a10707baa0cca8dd377302260710c1Winson
25099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        // The rotation to apply to the hint text
25199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        @Retention(RetentionPolicy.SOURCE)
25299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        @IntDef({HORIZONTAL, VERTICAL})
25399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        public @interface TextOrientation {}
25499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        private static final int HORIZONTAL = 0;
25599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        private static final int VERTICAL = 1;
25699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
2573f32e7eba7aec02edbf542cc11eee13264a5abf2Winson        private static final int DOCK_AREA_ALPHA = 80;
25899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
25999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                null, null, null);
2603e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public static final DockState LEFT = new DockState(DOCKED_LEFT,
26199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
2623e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
2633e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0, 0.5f, 1));
2643e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public static final DockState TOP = new DockState(DOCKED_TOP,
26599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
2663e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
2673e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0, 1, 0.5f));
2683e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
26999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
2703e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
2713e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0.5f, 0, 1, 1));
2723e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
27399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
2743e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
2753e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                new RectF(0, 0.5f, 1, 1));
276eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson
277eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        @Override
2783e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
2793e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            return isCurrentTarget
2803e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
2813e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    : areaContainsPoint(touchArea, width, height, x, y);
282eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        }
283be7607af8875236b9cf7bdb5f5aa089c207529afWinson
284882072baacaee4ecd43f0209b691a9af746462f2Winson        // Represents the view state of this dock state
28599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        public static class ViewState {
28699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private static final IntProperty<ViewState> HINT_ALPHA =
28799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    new IntProperty<ViewState>("drawableAlpha") {
28899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        @Override
28999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        public void setValue(ViewState object, int alpha) {
29099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                            object.mHintTextAlpha = alpha;
29199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                            object.dockAreaOverlay.invalidateSelf();
29299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        }
29399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
29499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        @Override
29599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        public Integer get(ViewState object) {
29699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                            return object.mHintTextAlpha;
29799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        }
29899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    };
29999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
300882072baacaee4ecd43f0209b691a9af746462f2Winson            public final int dockAreaAlpha;
301882072baacaee4ecd43f0209b691a9af746462f2Winson            public final ColorDrawable dockAreaOverlay;
30299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            public final int hintTextAlpha;
30399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            public final int hintTextOrientation;
30499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
30599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private final int mHintTextResId;
30699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private String mHintText;
30799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private Paint mHintTextPaint;
30899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private Point mHintTextBounds = new Point();
30999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private int mHintTextAlpha = 255;
31099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private AnimatorSet mDockAreaOverlayAnimator;
31199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private Rect mTmpRect = new Rect();
31299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
31399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
31499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    int hintTextResId) {
31599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                dockAreaAlpha = areaAlpha;
316882072baacaee4ecd43f0209b691a9af746462f2Winson                dockAreaOverlay = new ColorDrawable(0xFFffffff);
317882072baacaee4ecd43f0209b691a9af746462f2Winson                dockAreaOverlay.setAlpha(0);
31899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                hintTextAlpha = hintAlpha;
31999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                hintTextOrientation = hintOrientation;
32099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextResId = hintTextResId;
32199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
32299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextPaint.setColor(Color.WHITE);
32399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            }
32499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
32599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            /**
32699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson             * Updates the view state with the given context.
32799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson             */
32899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            public void update(Context context) {
32999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                Resources res = context.getResources();
33099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintText = context.getString(mHintTextResId);
33199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextPaint.setTextSize(res.getDimensionPixelSize(
33299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        R.dimen.recents_drag_hint_text_size));
33399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
33499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
33599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            }
33699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
33799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            /**
33899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson             * Draws the current view state.
33999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson             */
34099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            public void draw(Canvas canvas) {
34199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                // Draw the overlay background
34299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                if (dockAreaOverlay.getAlpha() > 0) {
34399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    dockAreaOverlay.draw(canvas);
34499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                }
34599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
34699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                // Draw the hint text
34799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                if (mHintTextAlpha > 0) {
34899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    Rect bounds = dockAreaOverlay.getBounds();
34999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
35099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
35199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    mHintTextPaint.setAlpha(mHintTextAlpha);
35299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    if (hintTextOrientation == VERTICAL) {
35399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        canvas.save();
35499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
35599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    }
35699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    canvas.drawText(mHintText, x, y, mHintTextPaint);
35799ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    if (hintTextOrientation == VERTICAL) {
35899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        canvas.restore();
35999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    }
36099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                }
361882072baacaee4ecd43f0209b691a9af746462f2Winson            }
362882072baacaee4ecd43f0209b691a9af746462f2Winson
363882072baacaee4ecd43f0209b691a9af746462f2Winson            /**
3643e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson             * Creates a new bounds and alpha animation.
365882072baacaee4ecd43f0209b691a9af746462f2Winson             */
36699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
3673e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
36899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                if (mDockAreaOverlayAnimator != null) {
36999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    mDockAreaOverlayAnimator.cancel();
3703e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                }
3713e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson
3723f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                ObjectAnimator anim;
3733e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                ArrayList<Animator> animators = new ArrayList<>();
37499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                if (dockAreaOverlay.getAlpha() != areaAlpha) {
3753e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    if (animateAlpha) {
3763f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim = ObjectAnimator.ofInt(dockAreaOverlay,
3773f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
3783f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setDuration(duration);
3793f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setInterpolator(interpolator);
3803f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        animators.add(anim);
38199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    } else {
38299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        dockAreaOverlay.setAlpha(areaAlpha);
38399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    }
38499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                }
38599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                if (mHintTextAlpha != hintAlpha) {
38699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    if (animateAlpha) {
3873f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
3883f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                hintAlpha);
3893f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setDuration(150);
3903f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setInterpolator(hintAlpha > mHintTextAlpha
3913f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                ? Interpolators.ALPHA_IN
3923f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                : Interpolators.ALPHA_OUT);
3933f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        animators.add(anim);
3943e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    } else {
39599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        mHintTextAlpha = hintAlpha;
39699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                        dockAreaOverlay.invalidateSelf();
3973e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    }
3983e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                }
3993e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
4003e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    if (animateBounds) {
4013e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                        PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
40267c79578f94646bade5d336af0d4491179c1b37dWinson                                Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
4033f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                                new Rect(dockAreaOverlay.getBounds()), bounds);
4043f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
4053f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setDuration(duration);
4063f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        anim.setInterpolator(interpolator);
4073f32e7eba7aec02edbf542cc11eee13264a5abf2Winson                        animators.add(anim);
4083e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    } else {
4093e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                        dockAreaOverlay.setBounds(bounds);
410882072baacaee4ecd43f0209b691a9af746462f2Winson                    }
4113e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                }
4123e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                if (!animators.isEmpty()) {
41399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    mDockAreaOverlayAnimator = new AnimatorSet();
41499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    mDockAreaOverlayAnimator.playTogether(animators);
41599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    mDockAreaOverlayAnimator.start();
416882072baacaee4ecd43f0209b691a9af746462f2Winson                }
417882072baacaee4ecd43f0209b691a9af746462f2Winson            }
418882072baacaee4ecd43f0209b691a9af746462f2Winson        }
419882072baacaee4ecd43f0209b691a9af746462f2Winson
4203e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public final int dockSide;
421be7607af8875236b9cf7bdb5f5aa089c207529afWinson        public final int createMode;
422882072baacaee4ecd43f0209b691a9af746462f2Winson        public final ViewState viewState;
423882072baacaee4ecd43f0209b691a9af746462f2Winson        private final RectF touchArea;
4243e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        private final RectF dockArea;
4253e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        private final RectF expandedTouchDockArea;
426be7607af8875236b9cf7bdb5f5aa089c207529afWinson
427be7607af8875236b9cf7bdb5f5aa089c207529afWinson        /**
428be7607af8875236b9cf7bdb5f5aa089c207529afWinson         * @param createMode used to pass to ActivityManager to dock the task
429be7607af8875236b9cf7bdb5f5aa089c207529afWinson         * @param touchArea the area in which touch will initiate this dock state
430eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson         * @param dockArea the visible dock area
4313e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * @param expandedTouchDockArea the areain which touch will continue to dock after entering
4323e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         *                              the initial touch area.  This is also the new dock area to
4333e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         *                              draw.
434be7607af8875236b9cf7bdb5f5aa089c207529afWinson         */
43599ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
43699ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                  @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
4373e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                  RectF expandedTouchDockArea) {
4383e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            this.dockSide = dockSide;
439be7607af8875236b9cf7bdb5f5aa089c207529afWinson            this.createMode = createMode;
44099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
44199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson                    R.string.recents_drag_hint_message);
442be7607af8875236b9cf7bdb5f5aa089c207529afWinson            this.dockArea = dockArea;
443882072baacaee4ecd43f0209b691a9af746462f2Winson            this.touchArea = touchArea;
4443e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            this.expandedTouchDockArea = expandedTouchDockArea;
445be7607af8875236b9cf7bdb5f5aa089c207529afWinson        }
446be7607af8875236b9cf7bdb5f5aa089c207529afWinson
447be7607af8875236b9cf7bdb5f5aa089c207529afWinson        /**
44899ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson         * Updates the dock state with the given context.
44999ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson         */
45099ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        public void update(Context context) {
45199ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson            viewState.update(context);
45299ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        }
45399ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson
45499ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6Winson        /**
4553e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * Returns whether {@param x} and {@param y} are contained in the area scaled to the
456be7607af8875236b9cf7bdb5f5aa089c207529afWinson         * given {@param width} and {@param height}.
457be7607af8875236b9cf7bdb5f5aa089c207529afWinson         */
4583e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
4593e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int left = (int) (area.left * width);
4603e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int top = (int) (area.top * height);
4613e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int right = (int) (area.right * width);
4623e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int bottom = (int) (area.bottom * height);
463be7607af8875236b9cf7bdb5f5aa089c207529afWinson            return x >= left && y >= top && x <= right && y <= bottom;
464be7607af8875236b9cf7bdb5f5aa089c207529afWinson        }
465be7607af8875236b9cf7bdb5f5aa089c207529afWinson
466be7607af8875236b9cf7bdb5f5aa089c207529afWinson        /**
467be7607af8875236b9cf7bdb5f5aa089c207529afWinson         * Returns the docked task bounds with the given {@param width} and {@param height}.
468be7607af8875236b9cf7bdb5f5aa089c207529afWinson         */
4693e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public Rect getPreDockedBounds(int width, int height) {
470be7607af8875236b9cf7bdb5f5aa089c207529afWinson            return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
471be7607af8875236b9cf7bdb5f5aa089c207529afWinson                    (int) (dockArea.right * width), (int) (dockArea.bottom * height));
472be7607af8875236b9cf7bdb5f5aa089c207529afWinson        }
4733e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson
4743e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        /**
4753e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * Returns the expanded docked task bounds with the given {@param width} and
4763e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * {@param height}.
4773e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         */
4783e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
4793e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                Resources res) {
4803e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            // Calculate the docked task bounds
4813e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            boolean isHorizontalDivision =
4823e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
4833e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
4843e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    insets, width, height, dividerSize);
4853e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            Rect newWindowBounds = new Rect();
4863e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
4873e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    width, height, dividerSize);
4883e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            return newWindowBounds;
4893e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        }
4903e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson
4913e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        /**
4923e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * Returns the task stack bounds with the given {@param width} and
4933e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         * {@param height}.
4943e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson         */
495fc48b072e8caa62849ee305f89f38d248d4a1dedWinson        public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
496fc48b072e8caa62849ee305f89f38d248d4a1dedWinson                int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
497fc48b072e8caa62849ee305f89f38d248d4a1dedWinson                Resources res, Rect windowRectOut) {
4983e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            // Calculate the inverse docked task bounds
4993e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            boolean isHorizontalDivision =
5003e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
5013e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
5023e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    insets, width, height, dividerSize);
5033e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            DockedDividerUtils.calculateBoundsForPosition(position,
50459924fe0d9136cf349759bea1e06b661603f95feWinson                    DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
5053e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson                    dividerSize);
5063e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson
5073e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            // Calculate the task stack bounds from the new window bounds
5083e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            Rect taskStackBounds = new Rect();
509d952961977a0eb6c1fefcb0707d1c61741515f68Winson            // If the task stack bounds is specifically under the dock area, then ignore the top
510d952961977a0eb6c1fefcb0707d1c61741515f68Winson            // inset
511d952961977a0eb6c1fefcb0707d1c61741515f68Winson            int top = dockArea.bottom < 1f
512d952961977a0eb6c1fefcb0707d1c61741515f68Winson                    ? 0
513d952961977a0eb6c1fefcb0707d1c61741515f68Winson                    : insets.top;
514fc48b072e8caa62849ee305f89f38d248d4a1dedWinson            layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, insets.right,
515fc48b072e8caa62849ee305f89f38d248d4a1dedWinson                    taskStackBounds);
5163e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson            return taskStackBounds;
5173e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        }
518be7607af8875236b9cf7bdb5f5aa089c207529afWinson    }
519be7607af8875236b9cf7bdb5f5aa089c207529afWinson
5206976f7bae901d81a354287088ae232ba7236f30eWinson    // A comparator that sorts tasks by their freeform state
5216976f7bae901d81a354287088ae232ba7236f30eWinson    private Comparator<Task> FREEFORM_COMPARATOR = new Comparator<Task>() {
522509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung        @Override
523509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung        public int compare(Task o1, Task o2) {
524509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung            if (o1.isFreeformTask() && !o2.isFreeformTask()) {
525509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung                return 1;
526509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung            } else if (o2.isFreeformTask() && !o1.isFreeformTask()) {
527509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung                return -1;
528509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung            }
5296976f7bae901d81a354287088ae232ba7236f30eWinson            return Long.compare(o1.temporarySortIndexInStack, o2.temporarySortIndexInStack);
530509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung        }
531509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung    };
532509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung
533509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung
534083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung    // The task offset to apply to a task id as a group affiliation
535083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung    static final int IndividualTaskIdOffset = 1 << 16;
536083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung
537062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung    ArrayList<Task> mRawTaskList = new ArrayList<>();
538250608a5cd08862f4752a924d51710805850db8aWinson    FilteredTaskList mStackTaskList = new FilteredTaskList();
539303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    TaskStackCallbacks mCb;
540303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
5418f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson    ArrayList<TaskGrouping> mGroups = new ArrayList<>();
5425500390a006f2bbea565068234774a36cea076c0Winson    ArrayMap<Integer, TaskGrouping> mAffinitiesGroups = new ArrayMap<>();
5438f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson
5448f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson    public TaskStack() {
5458f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson        // Ensure that we only show non-docked tasks
546250608a5cd08862f4752a924d51710805850db8aWinson        mStackTaskList.setFilter(new TaskFilter() {
5478f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson            @Override
5482b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
549d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
550d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                    if (t.isAffiliatedTask()) {
551d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                        // If this task is affiliated with another parent in the stack, then the
552d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                        // historical state of this task depends on the state of the parent task
553d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                        Task parentTask = taskIdMap.get(t.affiliationTaskId);
554d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                        if (parentTask != null) {
555d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                            t = parentTask;
556d8f7431d0fe0f5449ca54a15392c3238aa645deaWinson                        }
5572b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung                    }
5582b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung                }
5598f6ee48225ad1cdf966c8f406c85113b13833c7bWinson                return t.isStackTask;
5608f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson            }
5618f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson        });
5628f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson    }
563303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
564d16c565a607de754379fe699a4def21bd0e3de2fWinson Chung    /** Sets the callbacks for this task stack. */
565303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    public void setCallbacks(TaskStackCallbacks cb) {
566303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        mCb = cb;
567303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
568303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
569eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    /**
570eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson     * Moves the given task to either the front of the freeform workspace or the stack.
571eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson     */
572eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    public void moveTaskToStack(Task task, int newStackId) {
573eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        // Find the index to insert into
574250608a5cd08862f4752a924d51710805850db8aWinson        ArrayList<Task> taskList = mStackTaskList.getTasks();
575eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        int taskCount = taskList.size();
576eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
577eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            // Insert freeform tasks at the front
578250608a5cd08862f4752a924d51710805850db8aWinson            mStackTaskList.moveTaskToStack(task, taskCount, newStackId);
579eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
580eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            // Insert after the first stacked task
581eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            int insertIndex = 0;
582eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            for (int i = taskCount - 1; i >= 0; i--) {
583eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson                if (!taskList.get(i).isFreeformTask()) {
584eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson                    insertIndex = i + 1;
585eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson                    break;
586eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson                }
587eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson            }
588250608a5cd08862f4752a924d51710805850db8aWinson            mStackTaskList.moveTaskToStack(task, insertIndex, newStackId);
589eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson        }
590eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson    }
591eca4ab6e99bcb2a7b31b8b4b1c3b5474297b6b25Winson
5926ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung    /** Does the actual work associated with removing the task. */
593250608a5cd08862f4752a924d51710805850db8aWinson    void removeTaskImpl(FilteredTaskList taskList, Task t) {
5946ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung        // Remove the task from the list
595250608a5cd08862f4752a924d51710805850db8aWinson        taskList.remove(t);
5966ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung        // Remove it from the group as well, and if it is empty, remove the group
5976ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung        TaskGrouping group = t.group;
5984e5fb2f4e1ed4eb119a201541ad89aafac8c53feWinson Chung        if (group != null) {
5994e5fb2f4e1ed4eb119a201541ad89aafac8c53feWinson Chung            group.removeTask(t);
6004e5fb2f4e1ed4eb119a201541ad89aafac8c53feWinson Chung            if (group.getTaskCount() == 0) {
6014e5fb2f4e1ed4eb119a201541ad89aafac8c53feWinson Chung                removeGroup(group);
6024e5fb2f4e1ed4eb119a201541ad89aafac8c53feWinson Chung            }
6036ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung        }
6046ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung    }
6056ac8bd6198f67b64aea2258bdb5f8ed371b5bec1Winson Chung
6068aa9959413a06c3d2ff75e0c7be9e3cb7ac7cd2eWinson    /**
6078aa9959413a06c3d2ff75e0c7be9e3cb7ac7cd2eWinson     * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
6088aa9959413a06c3d2ff75e0c7be9e3cb7ac7cd2eWinson     * how they should update themselves.
6098aa9959413a06c3d2ff75e0c7be9e3cb7ac7cd2eWinson     */
6102068408d08ca39a5b036daac01e80de45e8c1eabWinson    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
611250608a5cd08862f4752a924d51710805850db8aWinson        if (mStackTaskList.contains(t)) {
612250608a5cd08862f4752a924d51710805850db8aWinson            removeTaskImpl(mStackTaskList, t);
61335a8b04140598a5b5c4865254b942adb6a830991Winson            Task newFrontMostTask = getStackFrontMostTask(false  /* includeFreeform */);
614303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            if (mCb != null) {
615ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                // Notify that a task has been removed
6166c8217ab3072fa65851316a922b482506b3c2a4cWinson                mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
6172068408d08ca39a5b036daac01e80de45e8c1eabWinson                        fromDockGesture);
618303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung            }
619303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        }
6203e8747414520ee348cf4b9c4a6afd9ff80b5a8f8Winson        mRawTaskList.remove(t);
621250608a5cd08862f4752a924d51710805850db8aWinson    }
622250608a5cd08862f4752a924d51710805850db8aWinson
623250608a5cd08862f4752a924d51710805850db8aWinson    /**
6243b6ba1ab144c53752841869627a1b9f6d357c404Winson     * Removes all tasks from the stack.
6253b6ba1ab144c53752841869627a1b9f6d357c404Winson     */
6263b6ba1ab144c53752841869627a1b9f6d357c404Winson    public void removeAllTasks() {
6273b6ba1ab144c53752841869627a1b9f6d357c404Winson        ArrayList<Task> tasks = mStackTaskList.getTasks();
6283b6ba1ab144c53752841869627a1b9f6d357c404Winson        for (int i = tasks.size() - 1; i >= 0; i--) {
6293b6ba1ab144c53752841869627a1b9f6d357c404Winson            Task t = tasks.get(i);
6303b6ba1ab144c53752841869627a1b9f6d357c404Winson            removeTaskImpl(mStackTaskList, t);
6313b6ba1ab144c53752841869627a1b9f6d357c404Winson            mRawTaskList.remove(t);
6323b6ba1ab144c53752841869627a1b9f6d357c404Winson        }
6333b6ba1ab144c53752841869627a1b9f6d357c404Winson        if (mCb != null) {
6343b6ba1ab144c53752841869627a1b9f6d357c404Winson            // Notify that all tasks have been removed
6353b6ba1ab144c53752841869627a1b9f6d357c404Winson            mCb.onStackTasksRemoved(this);
6363b6ba1ab144c53752841869627a1b9f6d357c404Winson        }
6373b6ba1ab144c53752841869627a1b9f6d357c404Winson    }
6383b6ba1ab144c53752841869627a1b9f6d357c404Winson
6393b6ba1ab144c53752841869627a1b9f6d357c404Winson    /**
640250608a5cd08862f4752a924d51710805850db8aWinson     * Sets a few tasks in one go, without calling any callbacks.
641062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung     *
642062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung     * @param tasks the new set of tasks to replace the current set.
643062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung     * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
644250608a5cd08862f4752a924d51710805850db8aWinson     */
6458873754f66527d2cc9feab295dd4eef23298212aWinson    public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
646062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        // Compute a has set for each of the tasks
6475500390a006f2bbea565068234774a36cea076c0Winson        ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
6485500390a006f2bbea565068234774a36cea076c0Winson        ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
6498873754f66527d2cc9feab295dd4eef23298212aWinson        ArrayList<Task> addedTasks = new ArrayList<>();
6506c8217ab3072fa65851316a922b482506b3c2a4cWinson        ArrayList<Task> removedTasks = new ArrayList<>();
6518873754f66527d2cc9feab295dd4eef23298212aWinson        ArrayList<Task> allTasks = new ArrayList<>();
652062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
653062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        // Disable notifications if there are no callbacks
654062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        if (mCb == null) {
655062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            notifyStackChanges = false;
656062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        }
657062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
658062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        // Remove any tasks that no longer exist
659062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        int taskCount = mRawTaskList.size();
6608873754f66527d2cc9feab295dd4eef23298212aWinson        for (int i = taskCount - 1; i >= 0; i--) {
661062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            Task task = mRawTaskList.get(i);
662062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            if (!newTasksMap.containsKey(task.key)) {
663062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung                if (notifyStackChanges) {
6646c8217ab3072fa65851316a922b482506b3c2a4cWinson                    removedTasks.add(task);
665062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung                }
666062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            }
6679756755db76aeda2065322aa3c26e1a19578d45fWinson Chung            task.setGroup(null);
668062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        }
669062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
670062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        // Add any new tasks
671062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        taskCount = tasks.size();
672062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        for (int i = 0; i < taskCount; i++) {
6738873754f66527d2cc9feab295dd4eef23298212aWinson            Task newTask = tasks.get(i);
6748873754f66527d2cc9feab295dd4eef23298212aWinson            Task currentTask = currentTasksMap.get(newTask.key);
6758873754f66527d2cc9feab295dd4eef23298212aWinson            if (currentTask == null && notifyStackChanges) {
6768873754f66527d2cc9feab295dd4eef23298212aWinson                addedTasks.add(newTask);
6778873754f66527d2cc9feab295dd4eef23298212aWinson            } else if (currentTask != null) {
6788873754f66527d2cc9feab295dd4eef23298212aWinson                // The current task has bound callbacks, so just copy the data from the new task
6798873754f66527d2cc9feab295dd4eef23298212aWinson                // state and add it back into the list
6808873754f66527d2cc9feab295dd4eef23298212aWinson                currentTask.copyFrom(newTask);
6818873754f66527d2cc9feab295dd4eef23298212aWinson                newTask = currentTask;
682062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            }
6838873754f66527d2cc9feab295dd4eef23298212aWinson            allTasks.add(newTask);
684062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        }
685062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
686062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        // Sort all the tasks to ensure they are ordered correctly
6876976f7bae901d81a354287088ae232ba7236f30eWinson        for (int i = allTasks.size() - 1; i >= 0; i--) {
6886976f7bae901d81a354287088ae232ba7236f30eWinson            allTasks.get(i).temporarySortIndexInStack = i;
6896976f7bae901d81a354287088ae232ba7236f30eWinson        }
6906976f7bae901d81a354287088ae232ba7236f30eWinson        Collections.sort(allTasks, FREEFORM_COMPARATOR);
691062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
6928f6ee48225ad1cdf966c8f406c85113b13833c7bWinson        mStackTaskList.set(allTasks);
6938873754f66527d2cc9feab295dd4eef23298212aWinson        mRawTaskList = allTasks;
6948873754f66527d2cc9feab295dd4eef23298212aWinson
695a1ededd056d51532ab18354f17f8065ec1134535Winson        // Update the affiliated groupings
696a1ededd056d51532ab18354f17f8065ec1134535Winson        createAffiliatedGroupings(context);
697a1ededd056d51532ab18354f17f8065ec1134535Winson
6986c8217ab3072fa65851316a922b482506b3c2a4cWinson        // Only callback for the removed tasks after the stack has updated
6996c8217ab3072fa65851316a922b482506b3c2a4cWinson        int removedTaskCount = removedTasks.size();
7006c8217ab3072fa65851316a922b482506b3c2a4cWinson        Task newFrontMostTask = getStackFrontMostTask(false);
7016c8217ab3072fa65851316a922b482506b3c2a4cWinson        for (int i = 0; i < removedTaskCount; i++) {
7026c8217ab3072fa65851316a922b482506b3c2a4cWinson            mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
7036c8217ab3072fa65851316a922b482506b3c2a4cWinson                    AnimationProps.IMMEDIATE, false /* fromDockGesture */);
7046c8217ab3072fa65851316a922b482506b3c2a4cWinson        }
7056c8217ab3072fa65851316a922b482506b3c2a4cWinson
7068873754f66527d2cc9feab295dd4eef23298212aWinson        // Only callback for the newly added tasks after this stack has been updated
7078873754f66527d2cc9feab295dd4eef23298212aWinson        int addedTaskCount = addedTasks.size();
7088873754f66527d2cc9feab295dd4eef23298212aWinson        for (int i = 0; i < addedTaskCount; i++) {
7098873754f66527d2cc9feab295dd4eef23298212aWinson            mCb.onStackTaskAdded(this, addedTasks.get(i));
7108873754f66527d2cc9feab295dd4eef23298212aWinson        }
7118873754f66527d2cc9feab295dd4eef23298212aWinson
712a1ededd056d51532ab18354f17f8065ec1134535Winson        // Notify that the task stack has been updated
713a1ededd056d51532ab18354f17f8065ec1134535Winson        if (notifyStackChanges) {
714a1ededd056d51532ab18354f17f8065ec1134535Winson            mCb.onStackTasksUpdated(this);
715a1ededd056d51532ab18354f17f8065ec1134535Winson        }
716303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
717303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
718931c51f54599a227422b2a1c71e922e1458e0291Winson Chung    /**
719931c51f54599a227422b2a1c71e922e1458e0291Winson Chung     * Gets the front-most task in the stack.
720931c51f54599a227422b2a1c71e922e1458e0291Winson Chung     */
72135a8b04140598a5b5c4865254b942adb6a830991Winson    public Task getStackFrontMostTask(boolean includeFreeformTasks) {
722931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        ArrayList<Task> stackTasks = mStackTaskList.getTasks();
723931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        if (stackTasks.isEmpty()) {
724931c51f54599a227422b2a1c71e922e1458e0291Winson Chung            return null;
725931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        }
726931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        for (int i = stackTasks.size() - 1; i >= 0; i--) {
727931c51f54599a227422b2a1c71e922e1458e0291Winson Chung            Task task = stackTasks.get(i);
72835a8b04140598a5b5c4865254b942adb6a830991Winson            if (!task.isFreeformTask() || includeFreeformTasks) {
729931c51f54599a227422b2a1c71e922e1458e0291Winson Chung                return task;
730931c51f54599a227422b2a1c71e922e1458e0291Winson Chung            }
731931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        }
732931c51f54599a227422b2a1c71e922e1458e0291Winson Chung        return null;
733ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
734ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
73504400672962d2e12132f9465928cbf7615c147c4Winson Chung    /** Gets the task keys */
73604400672962d2e12132f9465928cbf7615c147c4Winson Chung    public ArrayList<Task.TaskKey> getTaskKeys() {
7378f0e3a68cdd3b2403ff8a1677f90b998f4175f40Winson        ArrayList<Task.TaskKey> taskKeys = new ArrayList<>();
738250608a5cd08862f4752a924d51710805850db8aWinson        ArrayList<Task> tasks = computeAllTasksList();
73904400672962d2e12132f9465928cbf7615c147c4Winson Chung        int taskCount = tasks.size();
74004400672962d2e12132f9465928cbf7615c147c4Winson Chung        for (int i = 0; i < taskCount; i++) {
741250608a5cd08862f4752a924d51710805850db8aWinson            Task task = tasks.get(i);
742250608a5cd08862f4752a924d51710805850db8aWinson            taskKeys.add(task.key);
74304400672962d2e12132f9465928cbf7615c147c4Winson Chung        }
74404400672962d2e12132f9465928cbf7615c147c4Winson Chung        return taskKeys;
74504400672962d2e12132f9465928cbf7615c147c4Winson Chung    }
74604400672962d2e12132f9465928cbf7615c147c4Winson Chung
747250608a5cd08862f4752a924d51710805850db8aWinson    /**
748250608a5cd08862f4752a924d51710805850db8aWinson     * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
749250608a5cd08862f4752a924d51710805850db8aWinson     */
750250608a5cd08862f4752a924d51710805850db8aWinson    public ArrayList<Task> getStackTasks() {
751250608a5cd08862f4752a924d51710805850db8aWinson        return mStackTaskList.getTasks();
752250608a5cd08862f4752a924d51710805850db8aWinson    }
753250608a5cd08862f4752a924d51710805850db8aWinson
754250608a5cd08862f4752a924d51710805850db8aWinson    /**
755f24f21695f5609d06402cf61e3500d408b99bdcbWinson     * Returns the set of "freeform" tasks in the stack.
756f24f21695f5609d06402cf61e3500d408b99bdcbWinson     */
757f24f21695f5609d06402cf61e3500d408b99bdcbWinson    public ArrayList<Task> getFreeformTasks() {
758f24f21695f5609d06402cf61e3500d408b99bdcbWinson        ArrayList<Task> freeformTasks = new ArrayList<>();
759f24f21695f5609d06402cf61e3500d408b99bdcbWinson        ArrayList<Task> tasks = mStackTaskList.getTasks();
760f24f21695f5609d06402cf61e3500d408b99bdcbWinson        int taskCount = tasks.size();
761f24f21695f5609d06402cf61e3500d408b99bdcbWinson        for (int i = 0; i < taskCount; i++) {
762f24f21695f5609d06402cf61e3500d408b99bdcbWinson            Task task = tasks.get(i);
763f24f21695f5609d06402cf61e3500d408b99bdcbWinson            if (task.isFreeformTask()) {
764f24f21695f5609d06402cf61e3500d408b99bdcbWinson                freeformTasks.add(task);
765f24f21695f5609d06402cf61e3500d408b99bdcbWinson            }
766f24f21695f5609d06402cf61e3500d408b99bdcbWinson        }
767f24f21695f5609d06402cf61e3500d408b99bdcbWinson        return freeformTasks;
768f24f21695f5609d06402cf61e3500d408b99bdcbWinson    }
769f24f21695f5609d06402cf61e3500d408b99bdcbWinson
770f24f21695f5609d06402cf61e3500d408b99bdcbWinson    /**
7716976f7bae901d81a354287088ae232ba7236f30eWinson     * Computes a set of all the active and historical tasks.
772250608a5cd08862f4752a924d51710805850db8aWinson     */
773250608a5cd08862f4752a924d51710805850db8aWinson    public ArrayList<Task> computeAllTasksList() {
774250608a5cd08862f4752a924d51710805850db8aWinson        ArrayList<Task> tasks = new ArrayList<>();
775250608a5cd08862f4752a924d51710805850db8aWinson        tasks.addAll(mStackTaskList.getTasks());
776250608a5cd08862f4752a924d51710805850db8aWinson        return tasks;
777303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
778303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
77936a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson    /**
7804b057c6787624b75613769a857ccdf51114bb7f2Winson     * Returns the number of stack and freeform tasks.
781250608a5cd08862f4752a924d51710805850db8aWinson     */
7824b057c6787624b75613769a857ccdf51114bb7f2Winson    public int getTaskCount() {
783250608a5cd08862f4752a924d51710805850db8aWinson        return mStackTaskList.size();
784250608a5cd08862f4752a924d51710805850db8aWinson    }
785250608a5cd08862f4752a924d51710805850db8aWinson
786250608a5cd08862f4752a924d51710805850db8aWinson    /**
7874b057c6787624b75613769a857ccdf51114bb7f2Winson     * Returns the number of stack tasks.
7884b057c6787624b75613769a857ccdf51114bb7f2Winson     */
7894b057c6787624b75613769a857ccdf51114bb7f2Winson    public int getStackTaskCount() {
7904b057c6787624b75613769a857ccdf51114bb7f2Winson        ArrayList<Task> tasks = mStackTaskList.getTasks();
7914b057c6787624b75613769a857ccdf51114bb7f2Winson        int stackCount = 0;
7924b057c6787624b75613769a857ccdf51114bb7f2Winson        int taskCount = tasks.size();
7934b057c6787624b75613769a857ccdf51114bb7f2Winson        for (int i = 0; i < taskCount; i++) {
7944b057c6787624b75613769a857ccdf51114bb7f2Winson            Task task = tasks.get(i);
7954b057c6787624b75613769a857ccdf51114bb7f2Winson            if (!task.isFreeformTask()) {
7964b057c6787624b75613769a857ccdf51114bb7f2Winson                stackCount++;
7974b057c6787624b75613769a857ccdf51114bb7f2Winson            }
7984b057c6787624b75613769a857ccdf51114bb7f2Winson        }
7994b057c6787624b75613769a857ccdf51114bb7f2Winson        return stackCount;
8004b057c6787624b75613769a857ccdf51114bb7f2Winson    }
8014b057c6787624b75613769a857ccdf51114bb7f2Winson
8024b057c6787624b75613769a857ccdf51114bb7f2Winson    /**
8034b057c6787624b75613769a857ccdf51114bb7f2Winson     * Returns the number of freeform tasks.
804f0d1c44a59a10707baa0cca8dd377302260710c1Winson     */
8054b057c6787624b75613769a857ccdf51114bb7f2Winson    public int getFreeformTaskCount() {
806f0d1c44a59a10707baa0cca8dd377302260710c1Winson        ArrayList<Task> tasks = mStackTaskList.getTasks();
807f0d1c44a59a10707baa0cca8dd377302260710c1Winson        int freeformCount = 0;
808f0d1c44a59a10707baa0cca8dd377302260710c1Winson        int taskCount = tasks.size();
809f0d1c44a59a10707baa0cca8dd377302260710c1Winson        for (int i = 0; i < taskCount; i++) {
810f0d1c44a59a10707baa0cca8dd377302260710c1Winson            Task task = tasks.get(i);
811f0d1c44a59a10707baa0cca8dd377302260710c1Winson            if (task.isFreeformTask()) {
812f0d1c44a59a10707baa0cca8dd377302260710c1Winson                freeformCount++;
813f0d1c44a59a10707baa0cca8dd377302260710c1Winson            }
814f0d1c44a59a10707baa0cca8dd377302260710c1Winson        }
815f0d1c44a59a10707baa0cca8dd377302260710c1Winson        return freeformCount;
816f0d1c44a59a10707baa0cca8dd377302260710c1Winson    }
817f0d1c44a59a10707baa0cca8dd377302260710c1Winson
818f0d1c44a59a10707baa0cca8dd377302260710c1Winson    /**
819250608a5cd08862f4752a924d51710805850db8aWinson     * Returns the task in stack tasks which is the launch target.
82036a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson     */
82136a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson    public Task getLaunchTarget() {
822250608a5cd08862f4752a924d51710805850db8aWinson        ArrayList<Task> tasks = mStackTaskList.getTasks();
82336a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson        int taskCount = tasks.size();
82436a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson        for (int i = 0; i < taskCount; i++) {
82536a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson            Task task = tasks.get(i);
82636a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson            if (task.isLaunchTarget) {
82736a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson                return task;
82836a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson            }
82936a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson        }
83036a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson        return null;
83136a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson    }
83236a5a2c7003ef8157f276b411c3fda47ad2f75e3Winson
833303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    /** Returns the index of this task in this current task stack */
834250608a5cd08862f4752a924d51710805850db8aWinson    public int indexOfStackTask(Task t) {
835250608a5cd08862f4752a924d51710805850db8aWinson        return mStackTaskList.indexOf(t);
836303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
837303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung
838b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung    /** Finds the task with the specified task id. */
839b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung    public Task findTaskWithId(int taskId) {
840250608a5cd08862f4752a924d51710805850db8aWinson        ArrayList<Task> tasks = computeAllTasksList();
841d72c315d5eed3a684eee303364b97129cb821689Winson        int taskCount = tasks.size();
842d72c315d5eed3a684eee303364b97129cb821689Winson        for (int i = 0; i < taskCount; i++) {
843d72c315d5eed3a684eee303364b97129cb821689Winson            Task task = tasks.get(i);
844b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung            if (task.key.id == taskId) {
845b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung                return task;
846b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung            }
847b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung        }
848b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung        return null;
849b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung    }
850b1f749906f5c27114d02ea0c3f8ce0dcea08fd3fWinson Chung
851ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /******** Grouping ********/
852ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
853ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /** Adds a group to the set */
854ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    public void addGroup(TaskGrouping group) {
855ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        mGroups.add(group);
856ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        mAffinitiesGroups.put(group.affiliation, group);
857ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
858ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
859ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    public void removeGroup(TaskGrouping group) {
860ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        mGroups.remove(group);
861ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        mAffinitiesGroups.remove(group.affiliation);
862ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
863ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
864ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /** Returns the group with the specified affiliation. */
865083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung    public TaskGrouping getGroupWithAffiliation(int affiliation) {
866ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        return mAffinitiesGroups.get(affiliation);
867ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
868ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
869ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    /**
8708873754f66527d2cc9feab295dd4eef23298212aWinson     * Temporary: This method will simulate affiliation groups
871ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung     */
8728873754f66527d2cc9feab295dd4eef23298212aWinson    void createAffiliatedGroupings(Context context) {
8738873754f66527d2cc9feab295dd4eef23298212aWinson        mGroups.clear();
8748873754f66527d2cc9feab295dd4eef23298212aWinson        mAffinitiesGroups.clear();
8758873754f66527d2cc9feab295dd4eef23298212aWinson
8766e6bd8776f850a21a3733a03dfa32b04f06163d9Winson        if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
8775500390a006f2bbea565068234774a36cea076c0Winson            ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
878ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            // Sort all tasks by increasing firstActiveTime of the task
879250608a5cd08862f4752a924d51710805850db8aWinson            ArrayList<Task> tasks = mStackTaskList.getTasks();
880ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            Collections.sort(tasks, new Comparator<Task>() {
881ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                @Override
882ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                public int compare(Task task, Task task2) {
88316bc2a7f379edd7796bb5aa4400e1b7c14376a08Winson Chung                    return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime);
884ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                }
885ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            });
886ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            // Create groups when sequential packages are the same
887ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            NamedCounter counter = new NamedCounter("task-group", "");
888ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            int taskCount = tasks.size();
889ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            String prevPackage = "";
890083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung            int prevAffiliation = -1;
891a433fa9c17772f563163ff7db177d091d6aebd5bWinson Chung            Random r = new Random();
8926e6bd8776f850a21a3733a03dfa32b04f06163d9Winson            int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
893ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            for (int i = 0; i < taskCount; i++) {
894ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                Task t = tasks.get(i);
895e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                String packageName = t.key.getComponent().getPackageName();
896a433fa9c17772f563163ff7db177d091d6aebd5bWinson Chung                packageName = "pkg";
897ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                TaskGrouping group;
898a433fa9c17772f563163ff7db177d091d6aebd5bWinson Chung                if (packageName.equals(prevPackage) && groupCountDown > 0) {
899ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    group = getGroupWithAffiliation(prevAffiliation);
900a433fa9c17772f563163ff7db177d091d6aebd5bWinson Chung                    groupCountDown--;
901ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                } else {
902083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                    int affiliation = IndividualTaskIdOffset + t.key.id;
903ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    group = new TaskGrouping(affiliation);
904ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    addGroup(group);
905ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    prevAffiliation = affiliation;
906ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    prevPackage = packageName;
9076e6bd8776f850a21a3733a03dfa32b04f06163d9Winson                    groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
908ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                }
909ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                group.addTask(t);
910ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                taskMap.put(t.key, t);
911ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            }
912ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            // Sort groups by increasing latestActiveTime of the group
913ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            Collections.sort(mGroups, new Comparator<TaskGrouping>() {
914ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                @Override
915ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
916509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung                    return Long.compare(taskGrouping.latestActiveTimeInGroup,
917ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                            taskGrouping2.latestActiveTimeInGroup);
918ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                }
919ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            });
9202b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            // Sort group tasks by increasing firstActiveTime of the task, and also build a new list
9212b9ef6548be89d36ea7629f4a3d8ba7bba1422ceWinson Chung            // of tasks
922ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            int taskIndex = 0;
923ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            int groupCount = mGroups.size();
924ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            for (int i = 0; i < groupCount; i++) {
925ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                TaskGrouping group = mGroups.get(i);
926083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() {
927ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    @Override
928ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
929509d0d0c9e2ee165d04e898fea59f8941ac7138dWinson Chung                        return Long.compare(taskKey.firstActiveTime, taskKey2.firstActiveTime);
930ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    }
931ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                });
932083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys;
933ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                int groupTaskCount = groupTasks.size();
934ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                for (int j = 0; j < groupTaskCount; j++) {
935ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
936ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                    taskIndex++;
937ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                }
938ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            }
939250608a5cd08862f4752a924d51710805850db8aWinson            mStackTaskList.set(tasks);
940ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        } else {
941083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung            // Create the task groups
9425500390a006f2bbea565068234774a36cea076c0Winson            ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
943250608a5cd08862f4752a924d51710805850db8aWinson            ArrayList<Task> tasks = mStackTaskList.getTasks();
944ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            int taskCount = tasks.size();
945ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            for (int i = 0; i < taskCount; i++) {
946ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                Task t = tasks.get(i);
947083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                TaskGrouping group;
94865c851e6e9e08656744b6f66d3da188e3283b17dWinson                if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
94965c851e6e9e08656744b6f66d3da188e3283b17dWinson                    int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
95065c851e6e9e08656744b6f66d3da188e3283b17dWinson                            IndividualTaskIdOffset + t.key.id;
95165c851e6e9e08656744b6f66d3da188e3283b17dWinson                    if (mAffinitiesGroups.containsKey(affiliation)) {
95265c851e6e9e08656744b6f66d3da188e3283b17dWinson                        group = getGroupWithAffiliation(affiliation);
95365c851e6e9e08656744b6f66d3da188e3283b17dWinson                    } else {
95465c851e6e9e08656744b6f66d3da188e3283b17dWinson                        group = new TaskGrouping(affiliation);
95565c851e6e9e08656744b6f66d3da188e3283b17dWinson                        addGroup(group);
95665c851e6e9e08656744b6f66d3da188e3283b17dWinson                    }
957083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                } else {
95865c851e6e9e08656744b6f66d3da188e3283b17dWinson                    group = new TaskGrouping(t.key.id);
959083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                    addGroup(group);
960083baf99ff1228e96ede96aac88c8200c4fdc2b2Winson Chung                }
961ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung                group.addTask(t);
962ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                tasksMap.put(t.key, t);
963ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung            }
964ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung            // Update the task colors for each of the groups
96535f3050959e43bf378f9a0adcaef13729206c7e4Winson            float minAlpha = context.getResources().getFloat(
96635f3050959e43bf378f9a0adcaef13729206c7e4Winson                    R.dimen.recents_task_affiliation_color_min_alpha_percentage);
967ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung            int taskGroupCount = mGroups.size();
968ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung            for (int i = 0; i < taskGroupCount; i++) {
969ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                TaskGrouping group = mGroups.get(i);
970ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                taskCount = group.getTaskCount();
971ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                // Ignore the groups that only have one task
972ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                if (taskCount <= 1) continue;
973ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                // Calculate the group color distribution
974296278a0679375b8c43962a9e3c9bb4e8ab201e7Winson Chung                int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
975ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                float alphaStep = (1f - minAlpha) / taskCount;
976ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                float alpha = 1f;
977ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                for (int j = 0; j < taskCount; j++) {
978ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                    Task t = tasksMap.get(group.mTaskKeys.get(j));
979a0e88b5013d708ac6ed6518817d83c64c87ae4b1Winson Chung                    t.colorPrimary = Utilities.getColorWithOverlay(affiliationColor, Color.WHITE,
980a0e88b5013d708ac6ed6518817d83c64c87ae4b1Winson Chung                            alpha);
981ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                    alpha -= alphaStep;
982ec396d6399c5c31d697d81e94aff459e9771b0c6Winson Chung                }
983ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung            }
984ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung        }
985ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung    }
986ffa2ec664479bff6b4b61d4c349d9db2cb37ca16Winson Chung
987e7f138c7f0a190c86cec10fb32fa106aacae4093Winson    /**
988e7f138c7f0a190c86cec10fb32fa106aacae4093Winson     * Computes the components of tasks in this stack that have been removed as a result of a change
989e7f138c7f0a190c86cec10fb32fa106aacae4093Winson     * in the specified package.
990e7f138c7f0a190c86cec10fb32fa106aacae4093Winson     */
9915500390a006f2bbea565068234774a36cea076c0Winson    public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
992e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        // Identify all the tasks that should be removed as a result of the package being removed.
993e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        // Using a set to ensure that we callback once per unique component.
994e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        SystemServicesProxy ssp = Recents.getSystemServices();
9955500390a006f2bbea565068234774a36cea076c0Winson        ArraySet<ComponentName> existingComponents = new ArraySet<>();
9965500390a006f2bbea565068234774a36cea076c0Winson        ArraySet<ComponentName> removedComponents = new ArraySet<>();
997e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
998d72c315d5eed3a684eee303364b97129cb821689Winson        int taskKeyCount = taskKeys.size();
999d72c315d5eed3a684eee303364b97129cb821689Winson        for (int i = 0; i < taskKeyCount; i++) {
1000d72c315d5eed3a684eee303364b97129cb821689Winson            Task.TaskKey t = taskKeys.get(i);
1001d72c315d5eed3a684eee303364b97129cb821689Winson
1002e7f138c7f0a190c86cec10fb32fa106aacae4093Winson            // Skip if this doesn't apply to the current user
1003e7f138c7f0a190c86cec10fb32fa106aacae4093Winson            if (t.userId != userId) continue;
1004e7f138c7f0a190c86cec10fb32fa106aacae4093Winson
1005e7f138c7f0a190c86cec10fb32fa106aacae4093Winson            ComponentName cn = t.getComponent();
1006e7f138c7f0a190c86cec10fb32fa106aacae4093Winson            if (cn.getPackageName().equals(packageName)) {
1007e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                if (existingComponents.contains(cn)) {
1008e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                    // If we know that the component still exists in the package, then skip
1009e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                    continue;
1010e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                }
1011e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                if (ssp.getActivityInfo(cn, userId) != null) {
1012e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                    existingComponents.add(cn);
1013e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                } else {
1014e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                    removedComponents.add(cn);
1015e7f138c7f0a190c86cec10fb32fa106aacae4093Winson                }
1016e7f138c7f0a190c86cec10fb32fa106aacae4093Winson            }
1017e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        }
1018e7f138c7f0a190c86cec10fb32fa106aacae4093Winson        return removedComponents;
1019e7f138c7f0a190c86cec10fb32fa106aacae4093Winson    }
1020e7f138c7f0a190c86cec10fb32fa106aacae4093Winson
1021303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    @Override
1022303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    public String toString() {
10238873754f66527d2cc9feab295dd4eef23298212aWinson        String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
1024d72c315d5eed3a684eee303364b97129cb821689Winson        ArrayList<Task> tasks = mStackTaskList.getTasks();
1025d72c315d5eed3a684eee303364b97129cb821689Winson        int taskCount = tasks.size();
1026d72c315d5eed3a684eee303364b97129cb821689Winson        for (int i = 0; i < taskCount; i++) {
1027d72c315d5eed3a684eee303364b97129cb821689Winson            str += "    " + tasks.get(i).toString() + "\n";
1028250608a5cd08862f4752a924d51710805850db8aWinson        }
1029303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung        return str;
1030303e1ff1fec8b240b587bb18b981247a99833aa8Winson Chung    }
1031062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung
1032062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung    /**
1033062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung     * Given a list of tasks, returns a map of each task's key to the task.
1034062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung     */
10355500390a006f2bbea565068234774a36cea076c0Winson    private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
10365500390a006f2bbea565068234774a36cea076c0Winson        ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size());
1037062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        int taskCount = tasks.size();
1038062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        for (int i = 0; i < taskCount; i++) {
1039062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            Task task = tasks.get(i);
1040062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung            map.put(task.key, task);
1041062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        }
1042062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung        return map;
1043062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung    }
1044d72c315d5eed3a684eee303364b97129cb821689Winson
1045d72c315d5eed3a684eee303364b97129cb821689Winson    public void dump(String prefix, PrintWriter writer) {
1046d72c315d5eed3a684eee303364b97129cb821689Winson        String innerPrefix = prefix + "  ";
1047d72c315d5eed3a684eee303364b97129cb821689Winson
1048d72c315d5eed3a684eee303364b97129cb821689Winson        writer.print(prefix); writer.print(TAG);
1049d72c315d5eed3a684eee303364b97129cb821689Winson        writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
1050d72c315d5eed3a684eee303364b97129cb821689Winson        writer.println();
1051d72c315d5eed3a684eee303364b97129cb821689Winson        ArrayList<Task> tasks = mStackTaskList.getTasks();
1052d72c315d5eed3a684eee303364b97129cb821689Winson        int taskCount = tasks.size();
1053d72c315d5eed3a684eee303364b97129cb821689Winson        for (int i = 0; i < taskCount; i++) {
1054d72c315d5eed3a684eee303364b97129cb821689Winson            tasks.get(i).dump(innerPrefix, writer);
1055d72c315d5eed3a684eee303364b97129cb821689Winson        }
1056d72c315d5eed3a684eee303364b97129cb821689Winson    }
1057062667710edcad7a01d7ece3e2bc4a83ee2a2ca3Winson Chung}
1058