blob: 5a2507debd7e19c56b1add3cf1fb7a5e3426a300 [file] [log] [blame]
Winson Chung303e1ff2014-03-07 15:06:19 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.model;
18
Winson59924fe2016-03-17 14:13:18 -070019import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
20import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
21import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
22import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
23import static android.view.WindowManager.DOCKED_BOTTOM;
24import static android.view.WindowManager.DOCKED_INVALID;
25import static android.view.WindowManager.DOCKED_LEFT;
26import static android.view.WindowManager.DOCKED_RIGHT;
27import static android.view.WindowManager.DOCKED_TOP;
28
Winson3e874742016-01-07 10:08:17 -080029import android.animation.Animator;
30import android.animation.AnimatorSet;
Winson882072b2015-10-12 11:26:33 -070031import android.animation.ObjectAnimator;
Winson3e874742016-01-07 10:08:17 -080032import android.animation.PropertyValuesHolder;
33import android.animation.RectEvaluator;
Winsone7f138c2015-10-22 16:15:21 -070034import android.content.ComponentName;
Winson35f30502015-09-28 11:24:36 -070035import android.content.Context;
Winson3e874742016-01-07 10:08:17 -080036import android.content.res.Configuration;
37import android.content.res.Resources;
Winson Chungec396d62014-08-06 17:08:00 -070038import android.graphics.Color;
Winsonbe7607a2015-10-01 17:24:51 -070039import android.graphics.Rect;
40import android.graphics.RectF;
Winson882072b2015-10-12 11:26:33 -070041import android.graphics.drawable.ColorDrawable;
Winson55003902016-01-12 12:00:37 -080042import android.util.ArrayMap;
43import android.util.ArraySet;
Winson Chung2b9ef652015-12-11 10:23:59 -050044import android.util.SparseArray;
Winson3e874742016-01-07 10:08:17 -080045import android.view.animation.Interpolator;
Winsonc0d70582016-01-29 10:24:39 -080046
Winson3e874742016-01-07 10:08:17 -080047import com.android.internal.policy.DockedDividerUtils;
Winson2536c7e2015-10-01 15:49:31 -070048import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070049import com.android.systemui.recents.Recents;
Winsonc742f972015-11-12 11:32:21 -080050import com.android.systemui.recents.RecentsDebugFlags;
Winson Chungffa2ec62014-07-03 15:54:42 -070051import com.android.systemui.recents.misc.NamedCounter;
Winsone7f138c2015-10-22 16:15:21 -070052import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chunga0e88b52014-08-11 19:25:42 -070053import com.android.systemui.recents.misc.Utilities;
Winsonbe8e6962016-02-01 14:27:52 -080054import com.android.systemui.recents.views.AnimationProps;
Winson59924fe2016-03-17 14:13:18 -070055import com.android.systemui.recents.views.DropTarget;
56import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
Winson Chung303e1ff2014-03-07 15:06:19 -080057
58import java.util.ArrayList;
Winson Chungffa2ec62014-07-03 15:54:42 -070059import java.util.Collections;
60import java.util.Comparator;
Winson Chung303e1ff2014-03-07 15:06:19 -080061import java.util.List;
Winson Chunga433fa92014-07-08 21:50:31 -070062import java.util.Random;
Winson Chung303e1ff2014-03-07 15:06:19 -080063
64
65/**
66 * An interface for a task filter to query whether a particular task should show in a stack.
67 */
68interface TaskFilter {
69 /** Returns whether the filter accepts the specified task */
Winson Chung2b9ef652015-12-11 10:23:59 -050070 public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
Winson Chung303e1ff2014-03-07 15:06:19 -080071}
72
73/**
74 * A list of filtered tasks.
75 */
76class FilteredTaskList {
Winsoneca4ab62015-11-04 10:50:28 -080077
Winsoneca4ab62015-11-04 10:50:28 -080078 ArrayList<Task> mTasks = new ArrayList<>();
79 ArrayList<Task> mFilteredTasks = new ArrayList<>();
Winson55003902016-01-12 12:00:37 -080080 ArrayMap<Task.TaskKey, Integer> mTaskIndices = new ArrayMap<>();
Winson Chung303e1ff2014-03-07 15:06:19 -080081 TaskFilter mFilter;
82
83 /** Sets the task filter, saving the current touch state */
Winson Chungc6a16232014-04-01 14:04:48 -070084 boolean setFilter(TaskFilter filter) {
Winson55003902016-01-12 12:00:37 -080085 ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
Winson Chung303e1ff2014-03-07 15:06:19 -080086 mFilter = filter;
87 updateFilteredTasks();
Winson Chungb44c24f2014-04-09 15:17:43 -070088 if (!prevFilteredTasks.equals(mFilteredTasks)) {
89 return true;
90 } else {
Winson Chungb44c24f2014-04-09 15:17:43 -070091 return false;
92 }
Winson Chung303e1ff2014-03-07 15:06:19 -080093 }
94
95 /** Removes the task filter and returns the previous touch state */
96 void removeFilter() {
97 mFilter = null;
98 updateFilteredTasks();
99 }
100
101 /** Adds a new task to the task list */
102 void add(Task t) {
103 mTasks.add(t);
104 updateFilteredTasks();
105 }
106
Winsoneca4ab62015-11-04 10:50:28 -0800107 /**
108 * Moves the given task.
109 */
110 public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
111 int taskIndex = indexOf(task);
112 if (taskIndex != insertIndex) {
113 mTasks.remove(taskIndex);
114 if (taskIndex < insertIndex) {
115 insertIndex--;
116 }
117 mTasks.add(insertIndex, task);
118 }
119
120 // Update the stack id now, after we've moved the task, and before we update the
121 // filtered tasks
122 task.setStackId(newStackId);
123 updateFilteredTasks();
124 }
125
Winson Chung303e1ff2014-03-07 15:06:19 -0800126 /** Sets the list of tasks */
127 void set(List<Task> tasks) {
128 mTasks.clear();
129 mTasks.addAll(tasks);
130 updateFilteredTasks();
131 }
132
133 /** Removes a task from the base list only if it is in the filtered list */
134 boolean remove(Task t) {
135 if (mFilteredTasks.contains(t)) {
136 boolean removed = mTasks.remove(t);
137 updateFilteredTasks();
138 return removed;
139 }
140 return false;
141 }
142
143 /** Returns the index of this task in the list of filtered tasks */
144 int indexOf(Task t) {
Winson23746d52015-12-03 16:13:07 -0800145 if (t != null && mTaskIndices.containsKey(t.key)) {
Winson Chunga4ccb862014-08-22 15:26:27 -0700146 return mTaskIndices.get(t.key);
147 }
148 return -1;
Winson Chung303e1ff2014-03-07 15:06:19 -0800149 }
150
151 /** Returns the size of the list of filtered tasks */
152 int size() {
153 return mFilteredTasks.size();
154 }
155
156 /** Returns whether the filtered list contains this task */
157 boolean contains(Task t) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700158 return mTaskIndices.containsKey(t.key);
Winson Chung303e1ff2014-03-07 15:06:19 -0800159 }
160
161 /** Updates the list of filtered tasks whenever the base task list changes */
162 private void updateFilteredTasks() {
163 mFilteredTasks.clear();
164 if (mFilter != null) {
Winson Chung2b9ef652015-12-11 10:23:59 -0500165 // Create a sparse array from task id to Task
166 SparseArray<Task> taskIdMap = new SparseArray<>();
Winson Chung303e1ff2014-03-07 15:06:19 -0800167 int taskCount = mTasks.size();
168 for (int i = 0; i < taskCount; i++) {
169 Task t = mTasks.get(i);
Winson Chung2b9ef652015-12-11 10:23:59 -0500170 taskIdMap.put(t.key.id, t);
171 }
172
173 for (int i = 0; i < taskCount; i++) {
174 Task t = mTasks.get(i);
175 if (mFilter.acceptTask(taskIdMap, t, i)) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800176 mFilteredTasks.add(t);
177 }
178 }
179 } else {
180 mFilteredTasks.addAll(mTasks);
181 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700182 updateFilteredTaskIndices();
183 }
184
185 /** Updates the mapping of tasks to indices. */
186 private void updateFilteredTaskIndices() {
Winson Chungffa2ec62014-07-03 15:54:42 -0700187 int taskCount = mFilteredTasks.size();
Winson55003902016-01-12 12:00:37 -0800188 mTaskIndices.clear();
Winson Chungffa2ec62014-07-03 15:54:42 -0700189 for (int i = 0; i < taskCount; i++) {
190 Task t = mFilteredTasks.get(i);
191 mTaskIndices.put(t.key, i);
192 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800193 }
194
195 /** Returns whether this task list is filtered */
196 boolean hasFilter() {
197 return (mFilter != null);
198 }
199
200 /** Returns the list of filtered tasks */
201 ArrayList<Task> getTasks() {
202 return mFilteredTasks;
203 }
204}
205
206/**
207 * The task stack contains a list of multiple tasks.
208 */
209public class TaskStack {
Winson Chungffa2ec62014-07-03 15:54:42 -0700210
211 /** Task stack callbacks */
Winson Chung04dfe0d2014-03-14 14:06:29 -0700212 public interface TaskStackCallbacks {
Winson Chung06266772015-12-11 10:24:21 -0500213 /**
214 * Notifies when a new task has been added to the stack.
215 */
216 void onStackTaskAdded(TaskStack stack, Task newTask);
217
218 /**
219 * Notifies when a task has been removed from the stack.
220 */
Winsonaaf33bc2015-12-03 12:02:38 -0800221 void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Winson20684082016-03-16 17:13:34 -0700222 Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture);
Winsona1ededd2016-03-25 12:23:12 -0700223
224 /**
225 * Notifies when tasks in the stack have been updated.
226 */
227 void onStackTasksUpdated(TaskStack stack);
Winson Chung04dfe0d2014-03-14 14:06:29 -0700228 }
229
Winson250608a2015-11-24 15:00:31 -0800230 /**
231 * The various possible dock states when dragging and dropping a task.
232 */
Winsonf0d1c442015-12-01 11:04:45 -0800233 public static class DockState implements DropTarget {
234
235 private static final int DOCK_AREA_ALPHA = 192;
Winson3e874742016-01-07 10:08:17 -0800236 public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null);
237 public static final DockState LEFT = new DockState(DOCKED_LEFT,
Winsonf0d1c442015-12-01 11:04:45 -0800238 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
Winson3e874742016-01-07 10:08:17 -0800239 new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
240 new RectF(0, 0, 0.5f, 1));
241 public static final DockState TOP = new DockState(DOCKED_TOP,
Winsonf0d1c442015-12-01 11:04:45 -0800242 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
Winson3e874742016-01-07 10:08:17 -0800243 new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
244 new RectF(0, 0, 1, 0.5f));
245 public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
Winsonf0d1c442015-12-01 11:04:45 -0800246 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
Winson3e874742016-01-07 10:08:17 -0800247 new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
248 new RectF(0.5f, 0, 1, 1));
249 public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
Winsonf0d1c442015-12-01 11:04:45 -0800250 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
Winson3e874742016-01-07 10:08:17 -0800251 new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
252 new RectF(0, 0.5f, 1, 1));
Winsoneca4ab62015-11-04 10:50:28 -0800253
254 @Override
Winson3e874742016-01-07 10:08:17 -0800255 public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
256 return isCurrentTarget
257 ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
258 : areaContainsPoint(touchArea, width, height, x, y);
Winsoneca4ab62015-11-04 10:50:28 -0800259 }
Winsonbe7607a2015-10-01 17:24:51 -0700260
Winson882072b2015-10-12 11:26:33 -0700261 // Represents the view state of this dock state
262 public class ViewState {
263 public final int dockAreaAlpha;
264 public final ColorDrawable dockAreaOverlay;
Winson3e874742016-01-07 10:08:17 -0800265 private AnimatorSet dockAreaOverlayAnimator;
Winson882072b2015-10-12 11:26:33 -0700266
267 private ViewState(int alpha) {
268 dockAreaAlpha = alpha;
269 dockAreaOverlay = new ColorDrawable(0xFFffffff);
270 dockAreaOverlay.setAlpha(0);
271 }
272
273 /**
Winson3e874742016-01-07 10:08:17 -0800274 * Creates a new bounds and alpha animation.
Winson882072b2015-10-12 11:26:33 -0700275 */
Winson3e874742016-01-07 10:08:17 -0800276 public void startAnimation(Rect bounds, int alpha, int duration,
277 Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
278 if (dockAreaOverlayAnimator != null) {
279 dockAreaOverlayAnimator.cancel();
280 }
281
282 ArrayList<Animator> animators = new ArrayList<>();
Winson882072b2015-10-12 11:26:33 -0700283 if (dockAreaOverlay.getAlpha() != alpha) {
Winson3e874742016-01-07 10:08:17 -0800284 if (animateAlpha) {
285 animators.add(ObjectAnimator.ofInt(dockAreaOverlay,
286 Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha));
287 } else {
288 dockAreaOverlay.setAlpha(alpha);
Winson882072b2015-10-12 11:26:33 -0700289 }
Winson3e874742016-01-07 10:08:17 -0800290 }
291 if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
292 if (animateBounds) {
293 PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
294 Utilities.DRAWABLE_RECT, new RectEvaluator(new Rect()),
295 dockAreaOverlay.getBounds(), bounds);
296 animators.add(ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop));
297 } else {
298 dockAreaOverlay.setBounds(bounds);
299 }
300 }
301 if (!animators.isEmpty()) {
302 dockAreaOverlayAnimator = new AnimatorSet();
303 dockAreaOverlayAnimator.playTogether(animators);
Winson882072b2015-10-12 11:26:33 -0700304 dockAreaOverlayAnimator.setDuration(duration);
Winson3e874742016-01-07 10:08:17 -0800305 dockAreaOverlayAnimator.setInterpolator(interpolator);
Winson882072b2015-10-12 11:26:33 -0700306 dockAreaOverlayAnimator.start();
307 }
308 }
309 }
310
Winson3e874742016-01-07 10:08:17 -0800311 public final int dockSide;
Winsonbe7607a2015-10-01 17:24:51 -0700312 public final int createMode;
Winson882072b2015-10-12 11:26:33 -0700313 public final ViewState viewState;
Winson882072b2015-10-12 11:26:33 -0700314 private final RectF touchArea;
Winson3e874742016-01-07 10:08:17 -0800315 private final RectF dockArea;
316 private final RectF expandedTouchDockArea;
Winsonbe7607a2015-10-01 17:24:51 -0700317
318 /**
319 * @param createMode used to pass to ActivityManager to dock the task
320 * @param touchArea the area in which touch will initiate this dock state
Winsoneca4ab62015-11-04 10:50:28 -0800321 * @param dockArea the visible dock area
Winson3e874742016-01-07 10:08:17 -0800322 * @param expandedTouchDockArea the areain which touch will continue to dock after entering
323 * the initial touch area. This is also the new dock area to
324 * draw.
Winsonbe7607a2015-10-01 17:24:51 -0700325 */
Winson3e874742016-01-07 10:08:17 -0800326 DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
327 RectF expandedTouchDockArea) {
328 this.dockSide = dockSide;
Winsonbe7607a2015-10-01 17:24:51 -0700329 this.createMode = createMode;
Winson882072b2015-10-12 11:26:33 -0700330 this.viewState = new ViewState(dockAreaAlpha);
Winsonbe7607a2015-10-01 17:24:51 -0700331 this.dockArea = dockArea;
Winson882072b2015-10-12 11:26:33 -0700332 this.touchArea = touchArea;
Winson3e874742016-01-07 10:08:17 -0800333 this.expandedTouchDockArea = expandedTouchDockArea;
Winsonbe7607a2015-10-01 17:24:51 -0700334 }
335
336 /**
Winson3e874742016-01-07 10:08:17 -0800337 * Returns whether {@param x} and {@param y} are contained in the area scaled to the
Winsonbe7607a2015-10-01 17:24:51 -0700338 * given {@param width} and {@param height}.
339 */
Winson3e874742016-01-07 10:08:17 -0800340 public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
341 int left = (int) (area.left * width);
342 int top = (int) (area.top * height);
343 int right = (int) (area.right * width);
344 int bottom = (int) (area.bottom * height);
Winsonbe7607a2015-10-01 17:24:51 -0700345 return x >= left && y >= top && x <= right && y <= bottom;
346 }
347
348 /**
349 * Returns the docked task bounds with the given {@param width} and {@param height}.
350 */
Winson3e874742016-01-07 10:08:17 -0800351 public Rect getPreDockedBounds(int width, int height) {
Winsonbe7607a2015-10-01 17:24:51 -0700352 return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
353 (int) (dockArea.right * width), (int) (dockArea.bottom * height));
354 }
Winson3e874742016-01-07 10:08:17 -0800355
356 /**
357 * Returns the expanded docked task bounds with the given {@param width} and
358 * {@param height}.
359 */
360 public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
361 Resources res) {
362 // Calculate the docked task bounds
363 boolean isHorizontalDivision =
364 res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
365 int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
366 insets, width, height, dividerSize);
367 Rect newWindowBounds = new Rect();
368 DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
369 width, height, dividerSize);
370 return newWindowBounds;
371 }
372
373 /**
374 * Returns the task stack bounds with the given {@param width} and
375 * {@param height}.
376 */
377 public Rect getDockedTaskStackBounds(int width, int height, int dividerSize, Rect insets,
Winson59924fe2016-03-17 14:13:18 -0700378 TaskStackLayoutAlgorithm layoutAlgorithm, Resources res, Rect windowRectOut) {
Winson3e874742016-01-07 10:08:17 -0800379 // Calculate the inverse docked task bounds
380 boolean isHorizontalDivision =
381 res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
382 int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
383 insets, width, height, dividerSize);
Winson3e874742016-01-07 10:08:17 -0800384 DockedDividerUtils.calculateBoundsForPosition(position,
Winson59924fe2016-03-17 14:13:18 -0700385 DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
Winson3e874742016-01-07 10:08:17 -0800386 dividerSize);
387
388 // Calculate the task stack bounds from the new window bounds
Winson3e874742016-01-07 10:08:17 -0800389 Rect taskStackBounds = new Rect();
Winsond9529612016-01-28 13:29:49 -0800390 // If the task stack bounds is specifically under the dock area, then ignore the top
391 // inset
392 int top = dockArea.bottom < 1f
393 ? 0
394 : insets.top;
Winson008ee15f2016-03-18 17:17:25 -0700395 layoutAlgorithm.getTaskStackBounds(windowRectOut, top, insets.right, taskStackBounds);
Winson3e874742016-01-07 10:08:17 -0800396 return taskStackBounds;
397 }
Winsonbe7607a2015-10-01 17:24:51 -0700398 }
399
Winson250608a2015-11-24 15:00:31 -0800400 // A comparator that sorts tasks by their last active time
401 private Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR = new Comparator<Task>() {
402 @Override
403 public int compare(Task o1, Task o2) {
404 return Long.compare(o1.key.lastActiveTime, o2.key.lastActiveTime);
405 }
406 };
407
Winson Chung509d0d02015-12-16 15:43:12 -0500408 // A comparator that sorts tasks by their last active time and freeform state
409 private Comparator<Task> FREEFORM_LAST_ACTIVE_TIME_COMPARATOR = new Comparator<Task>() {
410 @Override
411 public int compare(Task o1, Task o2) {
412 if (o1.isFreeformTask() && !o2.isFreeformTask()) {
413 return 1;
414 } else if (o2.isFreeformTask() && !o1.isFreeformTask()) {
415 return -1;
416 }
417 return Long.compare(o1.key.lastActiveTime, o2.key.lastActiveTime);
418 }
419 };
420
421
Winson Chung083baf92014-07-11 10:32:42 -0700422 // The task offset to apply to a task id as a group affiliation
423 static final int IndividualTaskIdOffset = 1 << 16;
424
Winson Chung06266772015-12-11 10:24:21 -0500425 ArrayList<Task> mRawTaskList = new ArrayList<>();
Winson250608a2015-11-24 15:00:31 -0800426 FilteredTaskList mStackTaskList = new FilteredTaskList();
Winson Chung303e1ff2014-03-07 15:06:19 -0800427 TaskStackCallbacks mCb;
428
Winson8f0e3a62015-11-23 09:15:08 -0800429 ArrayList<TaskGrouping> mGroups = new ArrayList<>();
Winson55003902016-01-12 12:00:37 -0800430 ArrayMap<Integer, TaskGrouping> mAffinitiesGroups = new ArrayMap<>();
Winson8f0e3a62015-11-23 09:15:08 -0800431
432 public TaskStack() {
433 // Ensure that we only show non-docked tasks
Winson250608a2015-11-24 15:00:31 -0800434 mStackTaskList.setFilter(new TaskFilter() {
Winson8f0e3a62015-11-23 09:15:08 -0800435 @Override
Winson Chung2b9ef652015-12-11 10:23:59 -0500436 public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
Winsond8f74312016-03-23 20:39:24 -0700437 if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
438 if (t.isAffiliatedTask()) {
439 // If this task is affiliated with another parent in the stack, then the
440 // historical state of this task depends on the state of the parent task
441 Task parentTask = taskIdMap.get(t.affiliationTaskId);
442 if (parentTask != null) {
443 t = parentTask;
444 }
Winson Chung2b9ef652015-12-11 10:23:59 -0500445 }
446 }
Winson8f6ee482016-03-18 17:51:48 -0700447 return t.isStackTask;
Winson8f0e3a62015-11-23 09:15:08 -0800448 }
449 });
450 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800451
Winson Chungd16c5652015-01-26 16:11:07 -0800452 /** Sets the callbacks for this task stack. */
Winson Chung303e1ff2014-03-07 15:06:19 -0800453 public void setCallbacks(TaskStackCallbacks cb) {
454 mCb = cb;
455 }
456
Winsoneca4ab62015-11-04 10:50:28 -0800457 /**
458 * Moves the given task to either the front of the freeform workspace or the stack.
459 */
460 public void moveTaskToStack(Task task, int newStackId) {
461 // Find the index to insert into
Winson250608a2015-11-24 15:00:31 -0800462 ArrayList<Task> taskList = mStackTaskList.getTasks();
Winsoneca4ab62015-11-04 10:50:28 -0800463 int taskCount = taskList.size();
464 if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
465 // Insert freeform tasks at the front
Winson250608a2015-11-24 15:00:31 -0800466 mStackTaskList.moveTaskToStack(task, taskCount, newStackId);
Winsoneca4ab62015-11-04 10:50:28 -0800467 } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
468 // Insert after the first stacked task
469 int insertIndex = 0;
470 for (int i = taskCount - 1; i >= 0; i--) {
471 if (!taskList.get(i).isFreeformTask()) {
472 insertIndex = i + 1;
473 break;
474 }
475 }
Winson250608a2015-11-24 15:00:31 -0800476 mStackTaskList.moveTaskToStack(task, insertIndex, newStackId);
Winsoneca4ab62015-11-04 10:50:28 -0800477 }
478 }
479
Winson Chung6ac8bd62015-01-07 16:38:35 -0800480 /** Does the actual work associated with removing the task. */
Winson250608a2015-11-24 15:00:31 -0800481 void removeTaskImpl(FilteredTaskList taskList, Task t) {
Winson Chung6ac8bd62015-01-07 16:38:35 -0800482 // Remove the task from the list
Winson250608a2015-11-24 15:00:31 -0800483 taskList.remove(t);
Winson Chung6ac8bd62015-01-07 16:38:35 -0800484 // Remove it from the group as well, and if it is empty, remove the group
485 TaskGrouping group = t.group;
Winson Chung4e5fb2f2015-12-15 14:46:23 -0500486 if (group != null) {
487 group.removeTask(t);
488 if (group.getTaskCount() == 0) {
489 removeGroup(group);
490 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800491 }
Winson Chung6ac8bd62015-01-07 16:38:35 -0800492 }
493
Winson8aa99592016-01-19 15:07:07 -0800494 /**
495 * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
496 * how they should update themselves.
497 */
Winson20684082016-03-16 17:13:34 -0700498 public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
Winson250608a2015-11-24 15:00:31 -0800499 if (mStackTaskList.contains(t)) {
Winson35a8b042016-01-22 09:41:09 -0800500 boolean wasFrontMostTask = (getStackFrontMostTask(false /* includeFreeform */) == t);
Winson250608a2015-11-24 15:00:31 -0800501 removeTaskImpl(mStackTaskList, t);
Winson35a8b042016-01-22 09:41:09 -0800502 Task newFrontMostTask = getStackFrontMostTask(false /* includeFreeform */);
Winson Chung303e1ff2014-03-07 15:06:19 -0800503 if (mCb != null) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700504 // Notify that a task has been removed
Winson20684082016-03-16 17:13:34 -0700505 mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation,
506 fromDockGesture);
Winson Chung303e1ff2014-03-07 15:06:19 -0800507 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800508 }
Winson3e874742016-01-07 10:08:17 -0800509 mRawTaskList.remove(t);
Winson250608a2015-11-24 15:00:31 -0800510 }
511
512 /**
513 * Sets a few tasks in one go, without calling any callbacks.
Winson Chung06266772015-12-11 10:24:21 -0500514 *
515 * @param tasks the new set of tasks to replace the current set.
516 * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
Winson250608a2015-11-24 15:00:31 -0800517 */
Winson88737542016-02-17 13:27:33 -0800518 public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
Winson Chung06266772015-12-11 10:24:21 -0500519 // Compute a has set for each of the tasks
Winson55003902016-01-12 12:00:37 -0800520 ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
521 ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
Winson88737542016-02-17 13:27:33 -0800522 ArrayList<Task> addedTasks = new ArrayList<>();
523 ArrayList<Task> allTasks = new ArrayList<>();
Winson Chung06266772015-12-11 10:24:21 -0500524
525 // Disable notifications if there are no callbacks
526 if (mCb == null) {
527 notifyStackChanges = false;
528 }
529
530 // Remove any tasks that no longer exist
531 int taskCount = mRawTaskList.size();
Winson88737542016-02-17 13:27:33 -0800532 for (int i = taskCount - 1; i >= 0; i--) {
Winson Chung06266772015-12-11 10:24:21 -0500533 Task task = mRawTaskList.get(i);
534 if (!newTasksMap.containsKey(task.key)) {
535 if (notifyStackChanges) {
Winson88737542016-02-17 13:27:33 -0800536 // If we are notifying, then remove the task now, otherwise the raw task list
537 // will be reset at the end of this method
Winson20684082016-03-16 17:13:34 -0700538 removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson8aa99592016-01-19 15:07:07 -0800539 mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null,
Winson20684082016-03-16 17:13:34 -0700540 AnimationProps.IMMEDIATE, false /* fromDockGesture */);
Winson Chung06266772015-12-11 10:24:21 -0500541 }
Winson Chung06266772015-12-11 10:24:21 -0500542 }
Winson Chung97567552015-12-16 17:07:19 -0500543 task.setGroup(null);
Winson Chung06266772015-12-11 10:24:21 -0500544 }
545
546 // Add any new tasks
547 taskCount = tasks.size();
548 for (int i = 0; i < taskCount; i++) {
Winson88737542016-02-17 13:27:33 -0800549 Task newTask = tasks.get(i);
550 Task currentTask = currentTasksMap.get(newTask.key);
551 if (currentTask == null && notifyStackChanges) {
552 addedTasks.add(newTask);
553 } else if (currentTask != null) {
554 // The current task has bound callbacks, so just copy the data from the new task
555 // state and add it back into the list
556 currentTask.copyFrom(newTask);
557 newTask = currentTask;
Winson Chung06266772015-12-11 10:24:21 -0500558 }
Winson88737542016-02-17 13:27:33 -0800559 allTasks.add(newTask);
Winson Chung06266772015-12-11 10:24:21 -0500560 }
561
562 // Sort all the tasks to ensure they are ordered correctly
Winson88737542016-02-17 13:27:33 -0800563 Collections.sort(allTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
Winson Chung06266772015-12-11 10:24:21 -0500564
Winson8f6ee482016-03-18 17:51:48 -0700565 mStackTaskList.set(allTasks);
Winson88737542016-02-17 13:27:33 -0800566 mRawTaskList = allTasks;
567
Winsona1ededd2016-03-25 12:23:12 -0700568 // Update the affiliated groupings
569 createAffiliatedGroupings(context);
570
Winson88737542016-02-17 13:27:33 -0800571 // Only callback for the newly added tasks after this stack has been updated
572 int addedTaskCount = addedTasks.size();
573 for (int i = 0; i < addedTaskCount; i++) {
574 mCb.onStackTaskAdded(this, addedTasks.get(i));
575 }
576
Winsona1ededd2016-03-25 12:23:12 -0700577 // Notify that the task stack has been updated
578 if (notifyStackChanges) {
579 mCb.onStackTasksUpdated(this);
580 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800581 }
582
Winson Chung931c51f2015-12-17 17:08:55 -0500583 /**
584 * Gets the front-most task in the stack.
585 */
Winson35a8b042016-01-22 09:41:09 -0800586 public Task getStackFrontMostTask(boolean includeFreeformTasks) {
Winson Chung931c51f2015-12-17 17:08:55 -0500587 ArrayList<Task> stackTasks = mStackTaskList.getTasks();
588 if (stackTasks.isEmpty()) {
589 return null;
590 }
591 for (int i = stackTasks.size() - 1; i >= 0; i--) {
592 Task task = stackTasks.get(i);
Winson35a8b042016-01-22 09:41:09 -0800593 if (!task.isFreeformTask() || includeFreeformTasks) {
Winson Chung931c51f2015-12-17 17:08:55 -0500594 return task;
595 }
596 }
597 return null;
Winson Chungffa2ec62014-07-03 15:54:42 -0700598 }
599
Winson Chung04400672014-10-17 14:53:30 -0700600 /** Gets the task keys */
601 public ArrayList<Task.TaskKey> getTaskKeys() {
Winson8f0e3a62015-11-23 09:15:08 -0800602 ArrayList<Task.TaskKey> taskKeys = new ArrayList<>();
Winson250608a2015-11-24 15:00:31 -0800603 ArrayList<Task> tasks = computeAllTasksList();
Winson Chung04400672014-10-17 14:53:30 -0700604 int taskCount = tasks.size();
605 for (int i = 0; i < taskCount; i++) {
Winson250608a2015-11-24 15:00:31 -0800606 Task task = tasks.get(i);
607 taskKeys.add(task.key);
Winson Chung04400672014-10-17 14:53:30 -0700608 }
609 return taskKeys;
610 }
611
Winson250608a2015-11-24 15:00:31 -0800612 /**
613 * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
614 */
615 public ArrayList<Task> getStackTasks() {
616 return mStackTaskList.getTasks();
Winson Chung303e1ff2014-03-07 15:06:19 -0800617 }
618
Winson36a5a2c2015-10-29 18:04:39 -0700619 /**
Winsonf24f2162016-01-05 12:11:55 -0800620 * Returns the set of "freeform" tasks in the stack.
621 */
622 public ArrayList<Task> getFreeformTasks() {
623 ArrayList<Task> freeformTasks = new ArrayList<>();
624 ArrayList<Task> tasks = mStackTaskList.getTasks();
625 int taskCount = tasks.size();
626 for (int i = 0; i < taskCount; i++) {
627 Task task = tasks.get(i);
628 if (task.isFreeformTask()) {
629 freeformTasks.add(task);
630 }
631 }
632 return freeformTasks;
633 }
634
635 /**
Winson250608a2015-11-24 15:00:31 -0800636 * Computes a set of all the active and historical tasks ordered by their last active time.
637 */
638 public ArrayList<Task> computeAllTasksList() {
639 ArrayList<Task> tasks = new ArrayList<>();
640 tasks.addAll(mStackTaskList.getTasks());
Winson250608a2015-11-24 15:00:31 -0800641 Collections.sort(tasks, LAST_ACTIVE_TIME_COMPARATOR);
642 return tasks;
643 }
644
645 /**
Winson4b057c62016-01-12 15:01:52 -0800646 * Returns the number of stack and freeform tasks.
Winson250608a2015-11-24 15:00:31 -0800647 */
Winson4b057c62016-01-12 15:01:52 -0800648 public int getTaskCount() {
Winson250608a2015-11-24 15:00:31 -0800649 return mStackTaskList.size();
650 }
651
652 /**
Winson4b057c62016-01-12 15:01:52 -0800653 * Returns the number of stack tasks.
Winsonf0d1c442015-12-01 11:04:45 -0800654 */
Winson4b057c62016-01-12 15:01:52 -0800655 public int getStackTaskCount() {
656 ArrayList<Task> tasks = mStackTaskList.getTasks();
657 int stackCount = 0;
658 int taskCount = tasks.size();
659 for (int i = 0; i < taskCount; i++) {
660 Task task = tasks.get(i);
661 if (!task.isFreeformTask()) {
662 stackCount++;
663 }
664 }
665 return stackCount;
666 }
667
668 /**
669 * Returns the number of freeform tasks.
670 */
671 public int getFreeformTaskCount() {
Winsonf0d1c442015-12-01 11:04:45 -0800672 ArrayList<Task> tasks = mStackTaskList.getTasks();
673 int freeformCount = 0;
674 int taskCount = tasks.size();
675 for (int i = 0; i < taskCount; i++) {
676 Task task = tasks.get(i);
677 if (task.isFreeformTask()) {
678 freeformCount++;
679 }
680 }
681 return freeformCount;
682 }
683
684 /**
Winson250608a2015-11-24 15:00:31 -0800685 * Returns the task in stack tasks which is the launch target.
Winson36a5a2c2015-10-29 18:04:39 -0700686 */
687 public Task getLaunchTarget() {
Winson250608a2015-11-24 15:00:31 -0800688 ArrayList<Task> tasks = mStackTaskList.getTasks();
Winson36a5a2c2015-10-29 18:04:39 -0700689 int taskCount = tasks.size();
690 for (int i = 0; i < taskCount; i++) {
691 Task task = tasks.get(i);
692 if (task.isLaunchTarget) {
693 return task;
694 }
695 }
696 return null;
697 }
698
Winson Chung303e1ff2014-03-07 15:06:19 -0800699 /** Returns the index of this task in this current task stack */
Winson250608a2015-11-24 15:00:31 -0800700 public int indexOfStackTask(Task t) {
701 return mStackTaskList.indexOf(t);
Winson Chung303e1ff2014-03-07 15:06:19 -0800702 }
703
Winson Chungb1f74992014-08-08 12:53:09 -0700704 /** Finds the task with the specified task id. */
705 public Task findTaskWithId(int taskId) {
Winson250608a2015-11-24 15:00:31 -0800706 ArrayList<Task> tasks = computeAllTasksList();
707 for (Task task : tasks) {
Winson Chungb1f74992014-08-08 12:53:09 -0700708 if (task.key.id == taskId) {
709 return task;
710 }
711 }
712 return null;
713 }
714
Winson Chungffa2ec62014-07-03 15:54:42 -0700715 /******** Grouping ********/
716
717 /** Adds a group to the set */
718 public void addGroup(TaskGrouping group) {
719 mGroups.add(group);
720 mAffinitiesGroups.put(group.affiliation, group);
Winson Chungffa2ec62014-07-03 15:54:42 -0700721 }
722
723 public void removeGroup(TaskGrouping group) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700724 mGroups.remove(group);
725 mAffinitiesGroups.remove(group.affiliation);
Winson Chungffa2ec62014-07-03 15:54:42 -0700726 }
727
728 /** Returns the group with the specified affiliation. */
Winson Chung083baf92014-07-11 10:32:42 -0700729 public TaskGrouping getGroupWithAffiliation(int affiliation) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700730 return mAffinitiesGroups.get(affiliation);
731 }
732
Winson Chungffa2ec62014-07-03 15:54:42 -0700733 /**
Winson88737542016-02-17 13:27:33 -0800734 * Temporary: This method will simulate affiliation groups
Winson Chungffa2ec62014-07-03 15:54:42 -0700735 */
Winson88737542016-02-17 13:27:33 -0800736 void createAffiliatedGroupings(Context context) {
737 mGroups.clear();
738 mAffinitiesGroups.clear();
739
Winson6e6bd8772016-01-25 10:41:40 -0800740 if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
Winson55003902016-01-12 12:00:37 -0800741 ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
Winson Chungffa2ec62014-07-03 15:54:42 -0700742 // Sort all tasks by increasing firstActiveTime of the task
Winson250608a2015-11-24 15:00:31 -0800743 ArrayList<Task> tasks = mStackTaskList.getTasks();
Winson Chungffa2ec62014-07-03 15:54:42 -0700744 Collections.sort(tasks, new Comparator<Task>() {
745 @Override
746 public int compare(Task task, Task task2) {
Winson Chung16bc2a72015-12-15 10:27:04 -0500747 return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime);
Winson Chungffa2ec62014-07-03 15:54:42 -0700748 }
749 });
750 // Create groups when sequential packages are the same
751 NamedCounter counter = new NamedCounter("task-group", "");
752 int taskCount = tasks.size();
753 String prevPackage = "";
Winson Chung083baf92014-07-11 10:32:42 -0700754 int prevAffiliation = -1;
Winson Chunga433fa92014-07-08 21:50:31 -0700755 Random r = new Random();
Winson6e6bd8772016-01-25 10:41:40 -0800756 int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
Winson Chungffa2ec62014-07-03 15:54:42 -0700757 for (int i = 0; i < taskCount; i++) {
758 Task t = tasks.get(i);
Winsone7f138c2015-10-22 16:15:21 -0700759 String packageName = t.key.getComponent().getPackageName();
Winson Chunga433fa92014-07-08 21:50:31 -0700760 packageName = "pkg";
Winson Chungffa2ec62014-07-03 15:54:42 -0700761 TaskGrouping group;
Winson Chunga433fa92014-07-08 21:50:31 -0700762 if (packageName.equals(prevPackage) && groupCountDown > 0) {
Winson Chungffa2ec62014-07-03 15:54:42 -0700763 group = getGroupWithAffiliation(prevAffiliation);
Winson Chunga433fa92014-07-08 21:50:31 -0700764 groupCountDown--;
Winson Chungffa2ec62014-07-03 15:54:42 -0700765 } else {
Winson Chung083baf92014-07-11 10:32:42 -0700766 int affiliation = IndividualTaskIdOffset + t.key.id;
Winson Chungffa2ec62014-07-03 15:54:42 -0700767 group = new TaskGrouping(affiliation);
768 addGroup(group);
769 prevAffiliation = affiliation;
770 prevPackage = packageName;
Winson6e6bd8772016-01-25 10:41:40 -0800771 groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
Winson Chungffa2ec62014-07-03 15:54:42 -0700772 }
773 group.addTask(t);
774 taskMap.put(t.key, t);
775 }
776 // Sort groups by increasing latestActiveTime of the group
777 Collections.sort(mGroups, new Comparator<TaskGrouping>() {
778 @Override
779 public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
Winson Chung509d0d02015-12-16 15:43:12 -0500780 return Long.compare(taskGrouping.latestActiveTimeInGroup,
Winson Chungffa2ec62014-07-03 15:54:42 -0700781 taskGrouping2.latestActiveTimeInGroup);
782 }
783 });
Winson Chung2b9ef652015-12-11 10:23:59 -0500784 // Sort group tasks by increasing firstActiveTime of the task, and also build a new list
785 // of tasks
Winson Chungffa2ec62014-07-03 15:54:42 -0700786 int taskIndex = 0;
787 int groupCount = mGroups.size();
788 for (int i = 0; i < groupCount; i++) {
789 TaskGrouping group = mGroups.get(i);
Winson Chung083baf92014-07-11 10:32:42 -0700790 Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() {
Winson Chungffa2ec62014-07-03 15:54:42 -0700791 @Override
792 public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
Winson Chung509d0d02015-12-16 15:43:12 -0500793 return Long.compare(taskKey.firstActiveTime, taskKey2.firstActiveTime);
Winson Chungffa2ec62014-07-03 15:54:42 -0700794 }
795 });
Winson Chung083baf92014-07-11 10:32:42 -0700796 ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys;
Winson Chungffa2ec62014-07-03 15:54:42 -0700797 int groupTaskCount = groupTasks.size();
798 for (int j = 0; j < groupTaskCount; j++) {
799 tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
800 taskIndex++;
801 }
802 }
Winson250608a2015-11-24 15:00:31 -0800803 mStackTaskList.set(tasks);
Winson Chungffa2ec62014-07-03 15:54:42 -0700804 } else {
Winson Chung083baf92014-07-11 10:32:42 -0700805 // Create the task groups
Winson55003902016-01-12 12:00:37 -0800806 ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
Winson250608a2015-11-24 15:00:31 -0800807 ArrayList<Task> tasks = mStackTaskList.getTasks();
Winson Chungffa2ec62014-07-03 15:54:42 -0700808 int taskCount = tasks.size();
809 for (int i = 0; i < taskCount; i++) {
810 Task t = tasks.get(i);
Winson Chung083baf92014-07-11 10:32:42 -0700811 TaskGrouping group;
Winson65c851e2016-01-20 12:43:35 -0800812 if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
813 int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
814 IndividualTaskIdOffset + t.key.id;
815 if (mAffinitiesGroups.containsKey(affiliation)) {
816 group = getGroupWithAffiliation(affiliation);
817 } else {
818 group = new TaskGrouping(affiliation);
819 addGroup(group);
820 }
Winson Chung083baf92014-07-11 10:32:42 -0700821 } else {
Winson65c851e2016-01-20 12:43:35 -0800822 group = new TaskGrouping(t.key.id);
Winson Chung083baf92014-07-11 10:32:42 -0700823 addGroup(group);
824 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700825 group.addTask(t);
Winson Chungec396d62014-08-06 17:08:00 -0700826 tasksMap.put(t.key, t);
827 }
828 // Update the task colors for each of the groups
Winson35f30502015-09-28 11:24:36 -0700829 float minAlpha = context.getResources().getFloat(
830 R.dimen.recents_task_affiliation_color_min_alpha_percentage);
Winson Chungec396d62014-08-06 17:08:00 -0700831 int taskGroupCount = mGroups.size();
832 for (int i = 0; i < taskGroupCount; i++) {
833 TaskGrouping group = mGroups.get(i);
834 taskCount = group.getTaskCount();
835 // Ignore the groups that only have one task
836 if (taskCount <= 1) continue;
837 // Calculate the group color distribution
Winson Chung296278a2015-12-17 12:09:02 -0500838 int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
Winson Chungec396d62014-08-06 17:08:00 -0700839 float alphaStep = (1f - minAlpha) / taskCount;
840 float alpha = 1f;
841 for (int j = 0; j < taskCount; j++) {
842 Task t = tasksMap.get(group.mTaskKeys.get(j));
Winson Chunga0e88b52014-08-11 19:25:42 -0700843 t.colorPrimary = Utilities.getColorWithOverlay(affiliationColor, Color.WHITE,
844 alpha);
Winson Chungec396d62014-08-06 17:08:00 -0700845 alpha -= alphaStep;
846 }
Winson Chungffa2ec62014-07-03 15:54:42 -0700847 }
848 }
849 }
850
Winsone7f138c2015-10-22 16:15:21 -0700851 /**
852 * Computes the components of tasks in this stack that have been removed as a result of a change
853 * in the specified package.
854 */
Winson55003902016-01-12 12:00:37 -0800855 public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
Winsone7f138c2015-10-22 16:15:21 -0700856 // Identify all the tasks that should be removed as a result of the package being removed.
857 // Using a set to ensure that we callback once per unique component.
858 SystemServicesProxy ssp = Recents.getSystemServices();
Winson55003902016-01-12 12:00:37 -0800859 ArraySet<ComponentName> existingComponents = new ArraySet<>();
860 ArraySet<ComponentName> removedComponents = new ArraySet<>();
Winsone7f138c2015-10-22 16:15:21 -0700861 ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
862 for (Task.TaskKey t : taskKeys) {
863 // Skip if this doesn't apply to the current user
864 if (t.userId != userId) continue;
865
866 ComponentName cn = t.getComponent();
867 if (cn.getPackageName().equals(packageName)) {
868 if (existingComponents.contains(cn)) {
869 // If we know that the component still exists in the package, then skip
870 continue;
871 }
872 if (ssp.getActivityInfo(cn, userId) != null) {
873 existingComponents.add(cn);
874 } else {
875 removedComponents.add(cn);
876 }
877 }
878 }
879 return removedComponents;
880 }
881
Winson Chung303e1ff2014-03-07 15:06:19 -0800882 @Override
883 public String toString() {
Winson88737542016-02-17 13:27:33 -0800884 String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
Winson250608a2015-11-24 15:00:31 -0800885 for (Task t : mStackTaskList.getTasks()) {
Winson88737542016-02-17 13:27:33 -0800886 str += " " + t.toString() + "\n";
Winson250608a2015-11-24 15:00:31 -0800887 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800888 return str;
889 }
Winson Chung06266772015-12-11 10:24:21 -0500890
891 /**
892 * Given a list of tasks, returns a map of each task's key to the task.
893 */
Winson55003902016-01-12 12:00:37 -0800894 private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
895 ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size());
Winson Chung06266772015-12-11 10:24:21 -0500896 int taskCount = tasks.size();
897 for (int i = 0; i < taskCount; i++) {
898 Task task = tasks.get(i);
899 map.put(task.key, task);
900 }
901 return map;
902 }
903}