OSDN Git Service

7f7eee41bb47a251d7a1b55d85060329835c5ee2
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / recents / model / TaskStack.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.systemui.recents.model;
18
19 import android.graphics.Color;
20 import com.android.systemui.recents.Constants;
21 import com.android.systemui.recents.RecentsConfiguration;
22 import com.android.systemui.recents.misc.NamedCounter;
23 import com.android.systemui.recents.misc.Utilities;
24
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Random;
31
32
33 /**
34  * An interface for a task filter to query whether a particular task should show in a stack.
35  */
36 interface TaskFilter {
37     /** Returns whether the filter accepts the specified task */
38     public boolean acceptTask(Task t, int index);
39 }
40
41 /**
42  * A list of filtered tasks.
43  */
44 class FilteredTaskList {
45     ArrayList<Task> mTasks = new ArrayList<Task>();
46     ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
47     HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<Task.TaskKey, Integer>();
48     TaskFilter mFilter;
49
50     /** Sets the task filter, saving the current touch state */
51     boolean setFilter(TaskFilter filter) {
52         ArrayList<Task> prevFilteredTasks = new ArrayList<Task>(mFilteredTasks);
53         mFilter = filter;
54         updateFilteredTasks();
55         if (!prevFilteredTasks.equals(mFilteredTasks)) {
56             return true;
57         } else {
58             // If the tasks are exactly the same pre/post filter, then just reset it
59             mFilter = null;
60             return false;
61         }
62     }
63
64     /** Resets this FilteredTaskList. */
65     void reset() {
66         mTasks.clear();
67         mFilteredTasks.clear();
68         mTaskIndices.clear();
69         mFilter = null;
70     }
71
72     /** Removes the task filter and returns the previous touch state */
73     void removeFilter() {
74         mFilter = null;
75         updateFilteredTasks();
76     }
77
78     /** Adds a new task to the task list */
79     void add(Task t) {
80         mTasks.add(t);
81         updateFilteredTasks();
82     }
83
84     /** Sets the list of tasks */
85     void set(List<Task> tasks) {
86         mTasks.clear();
87         mTasks.addAll(tasks);
88         updateFilteredTasks();
89     }
90
91     /** Removes a task from the base list only if it is in the filtered list */
92     boolean remove(Task t) {
93         if (mFilteredTasks.contains(t)) {
94             boolean removed = mTasks.remove(t);
95             updateFilteredTasks();
96             return removed;
97         }
98         return false;
99     }
100
101     /** Returns the index of this task in the list of filtered tasks */
102     int indexOf(Task t) {
103         if (mTaskIndices.containsKey(t.key)) {
104             return mTaskIndices.get(t.key);
105         }
106         return -1;
107     }
108
109     /** Returns the size of the list of filtered tasks */
110     int size() {
111         return mFilteredTasks.size();
112     }
113
114     /** Returns whether the filtered list contains this task */
115     boolean contains(Task t) {
116         return mTaskIndices.containsKey(t.key);
117     }
118
119     /** Updates the list of filtered tasks whenever the base task list changes */
120     private void updateFilteredTasks() {
121         mFilteredTasks.clear();
122         if (mFilter != null) {
123             int taskCount = mTasks.size();
124             for (int i = 0; i < taskCount; i++) {
125                 Task t = mTasks.get(i);
126                 if (mFilter.acceptTask(t, i)) {
127                     mFilteredTasks.add(t);
128                 }
129             }
130         } else {
131             mFilteredTasks.addAll(mTasks);
132         }
133         updateFilteredTaskIndices();
134     }
135
136     /** Updates the mapping of tasks to indices. */
137     private void updateFilteredTaskIndices() {
138         mTaskIndices.clear();
139         int taskCount = mFilteredTasks.size();
140         for (int i = 0; i < taskCount; i++) {
141             Task t = mFilteredTasks.get(i);
142             mTaskIndices.put(t.key, i);
143         }
144     }
145
146     /** Returns whether this task list is filtered */
147     boolean hasFilter() {
148         return (mFilter != null);
149     }
150
151     /** Returns the list of filtered tasks */
152     ArrayList<Task> getTasks() {
153         return mFilteredTasks;
154     }
155 }
156
157 /**
158  * The task stack contains a list of multiple tasks.
159  */
160 public class TaskStack {
161
162     /** Task stack callbacks */
163     public interface TaskStackCallbacks {
164         /* Notifies when a task has been added to the stack */
165         public void onStackTaskAdded(TaskStack stack, Task t);
166         /* Notifies when a task has been removed from the stack */
167         public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask);
168         /* Notifies when all task has been removed from the stack */
169         public void onStackAllTasksRemoved(TaskStack stack, ArrayList<Task> removedTasks);
170         /** Notifies when the stack was filtered */
171         public void onStackFiltered(TaskStack newStack, ArrayList<Task> curTasks, Task t);
172         /** Notifies when the stack was un-filtered */
173         public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
174     }
175
176     /** A pair of indices representing the group and task positions in the stack and group. */
177     public static class GroupTaskIndex {
178         public int groupIndex; // Index in the stack
179         public int taskIndex;  // Index in the group
180
181         public GroupTaskIndex() {}
182
183         public GroupTaskIndex(int gi, int ti) {
184             groupIndex = gi;
185             taskIndex = ti;
186         }
187     }
188
189     // The task offset to apply to a task id as a group affiliation
190     static final int IndividualTaskIdOffset = 1 << 16;
191
192     FilteredTaskList mTaskList = new FilteredTaskList();
193     TaskStackCallbacks mCb;
194
195     ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
196     HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
197
198     /** Sets the callbacks for this task stack */
199     public void setCallbacks(TaskStackCallbacks cb) {
200         mCb = cb;
201     }
202
203     /** Resets this TaskStack. */
204     public void reset() {
205         mCb = null;
206         mTaskList.reset();
207         mGroups.clear();
208         mAffinitiesGroups.clear();
209     }
210
211     /** Adds a new task */
212     public void addTask(Task t) {
213         mTaskList.add(t);
214         if (mCb != null) {
215             mCb.onStackTaskAdded(this, t);
216         }
217     }
218
219     /** Does the actual work associated with removing the task. */
220     void removeTaskImpl(Task t) {
221         // Remove the task from the list
222         mTaskList.remove(t);
223         // Remove it from the group as well, and if it is empty, remove the group
224         TaskGrouping group = t.group;
225         group.removeTask(t);
226         if (group.getTaskCount() == 0) {
227             removeGroup(group);
228         }
229         // Update the lock-to-app state
230         t.lockToThisTask = false;
231     }
232
233     /** Removes a task */
234     public void removeTask(Task t) {
235         if (mTaskList.contains(t)) {
236             removeTaskImpl(t);
237             Task newFrontMostTask = getFrontMostTask();
238             if (newFrontMostTask != null && newFrontMostTask.lockToTaskEnabled) {
239                 newFrontMostTask.lockToThisTask = true;
240             }
241             if (mCb != null) {
242                 // Notify that a task has been removed
243                 mCb.onStackTaskRemoved(this, t, newFrontMostTask);
244             }
245         }
246     }
247
248     /** Removes all tasks */
249     public void removeAllTasks() {
250         ArrayList<Task> taskList = new ArrayList<Task>(mTaskList.getTasks());
251         int taskCount = taskList.size();
252         for (int i = taskCount - 1; i >= 0; i--) {
253             Task t = taskList.get(i);
254             removeTaskImpl(t);
255         }
256         if (mCb != null) {
257             // Notify that all tasks have been removed
258             mCb.onStackAllTasksRemoved(this, taskList);
259         }
260     }
261
262     /** Sets a few tasks in one go */
263     public void setTasks(List<Task> tasks) {
264         ArrayList<Task> taskList = mTaskList.getTasks();
265         int taskCount = taskList.size();
266         for (int i = taskCount - 1; i >= 0; i--) {
267             Task t = taskList.get(i);
268             removeTaskImpl(t);
269             if (mCb != null) {
270                 // Notify that a task has been removed
271                 mCb.onStackTaskRemoved(this, t, null);
272             }
273         }
274         mTaskList.set(tasks);
275         for (Task t : tasks) {
276             if (mCb != null) {
277                 mCb.onStackTaskAdded(this, t);
278             }
279         }
280     }
281
282     /** Gets the front task */
283     public Task getFrontMostTask() {
284         if (mTaskList.size() == 0) return null;
285         return mTaskList.getTasks().get(mTaskList.size() - 1);
286     }
287
288     /** Gets the task keys */
289     public ArrayList<Task.TaskKey> getTaskKeys() {
290         ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
291         ArrayList<Task> tasks = mTaskList.getTasks();
292         int taskCount = tasks.size();
293         for (int i = 0; i < taskCount; i++) {
294             taskKeys.add(tasks.get(i).key);
295         }
296         return taskKeys;
297     }
298
299     /** Gets the tasks */
300     public ArrayList<Task> getTasks() {
301         return mTaskList.getTasks();
302     }
303
304     /** Gets the number of tasks */
305     public int getTaskCount() {
306         return mTaskList.size();
307     }
308
309     /** Returns the index of this task in this current task stack */
310     public int indexOfTask(Task t) {
311         return mTaskList.indexOf(t);
312     }
313
314     /** Finds the task with the specified task id. */
315     public Task findTaskWithId(int taskId) {
316         ArrayList<Task> tasks = mTaskList.getTasks();
317         int taskCount = tasks.size();
318         for (int i = 0; i < taskCount; i++) {
319             Task task = tasks.get(i);
320             if (task.key.id == taskId) {
321                 return task;
322             }
323         }
324         return null;
325     }
326
327     /******** Filtering ********/
328
329     /** Filters the stack into tasks similar to the one specified */
330     public void filterTasks(final Task t) {
331         ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks());
332
333         // Set the task list filter
334         boolean filtered = mTaskList.setFilter(new TaskFilter() {
335             @Override
336             public boolean acceptTask(Task at, int i) {
337                 return t.key.baseIntent.getComponent().getPackageName().equals(
338                         at.key.baseIntent.getComponent().getPackageName());
339             }
340         });
341         if (filtered && mCb != null) {
342             mCb.onStackFiltered(this, oldStack, t);
343         }
344     }
345
346     /** Unfilters the current stack */
347     public void unfilterTasks() {
348         ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks());
349
350         // Unset the filter, then update the virtual scroll
351         mTaskList.removeFilter();
352         if (mCb != null) {
353             mCb.onStackUnfiltered(this, oldStack);
354         }
355     }
356
357     /** Returns whether tasks are currently filtered */
358     public boolean hasFilteredTasks() {
359         return mTaskList.hasFilter();
360     }
361
362     /******** Grouping ********/
363
364     /** Adds a group to the set */
365     public void addGroup(TaskGrouping group) {
366         mGroups.add(group);
367         mAffinitiesGroups.put(group.affiliation, group);
368     }
369
370     public void removeGroup(TaskGrouping group) {
371         mGroups.remove(group);
372         mAffinitiesGroups.remove(group.affiliation);
373     }
374
375     /** Returns the group with the specified affiliation. */
376     public TaskGrouping getGroupWithAffiliation(int affiliation) {
377         return mAffinitiesGroups.get(affiliation);
378     }
379
380     /**
381      * Temporary: This method will simulate affiliation groups by
382      */
383     public void createAffiliatedGroupings(RecentsConfiguration config) {
384         if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
385             HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
386             // Sort all tasks by increasing firstActiveTime of the task
387             ArrayList<Task> tasks = mTaskList.getTasks();
388             Collections.sort(tasks, new Comparator<Task>() {
389                 @Override
390                 public int compare(Task task, Task task2) {
391                     return (int) (task.key.firstActiveTime - task2.key.firstActiveTime);
392                 }
393             });
394             // Create groups when sequential packages are the same
395             NamedCounter counter = new NamedCounter("task-group", "");
396             int taskCount = tasks.size();
397             String prevPackage = "";
398             int prevAffiliation = -1;
399             Random r = new Random();
400             int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
401             for (int i = 0; i < taskCount; i++) {
402                 Task t = tasks.get(i);
403                 String packageName = t.key.baseIntent.getComponent().getPackageName();
404                 packageName = "pkg";
405                 TaskGrouping group;
406                 if (packageName.equals(prevPackage) && groupCountDown > 0) {
407                     group = getGroupWithAffiliation(prevAffiliation);
408                     groupCountDown--;
409                 } else {
410                     int affiliation = IndividualTaskIdOffset + t.key.id;
411                     group = new TaskGrouping(affiliation);
412                     addGroup(group);
413                     prevAffiliation = affiliation;
414                     prevPackage = packageName;
415                     groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
416                 }
417                 group.addTask(t);
418                 taskMap.put(t.key, t);
419             }
420             // Sort groups by increasing latestActiveTime of the group
421             Collections.sort(mGroups, new Comparator<TaskGrouping>() {
422                 @Override
423                 public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
424                     return (int) (taskGrouping.latestActiveTimeInGroup -
425                             taskGrouping2.latestActiveTimeInGroup);
426                 }
427             });
428             // Sort group tasks by increasing firstActiveTime of the task, and also build a new list of
429             // tasks
430             int taskIndex = 0;
431             int groupCount = mGroups.size();
432             for (int i = 0; i < groupCount; i++) {
433                 TaskGrouping group = mGroups.get(i);
434                 Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() {
435                     @Override
436                     public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
437                         return (int) (taskKey.firstActiveTime - taskKey2.firstActiveTime);
438                     }
439                 });
440                 ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys;
441                 int groupTaskCount = groupTasks.size();
442                 for (int j = 0; j < groupTaskCount; j++) {
443                     tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
444                     taskIndex++;
445                 }
446             }
447             mTaskList.set(tasks);
448         } else {
449             // Create the task groups
450             HashMap<Task.TaskKey, Task> tasksMap = new HashMap<Task.TaskKey, Task>();
451             ArrayList<Task> tasks = mTaskList.getTasks();
452             int taskCount = tasks.size();
453             for (int i = 0; i < taskCount; i++) {
454                 Task t = tasks.get(i);
455                 TaskGrouping group;
456                 int affiliation = t.taskAffiliation > 0 ? t.taskAffiliation :
457                         IndividualTaskIdOffset + t.key.id;
458                 if (mAffinitiesGroups.containsKey(affiliation)) {
459                     group = getGroupWithAffiliation(affiliation);
460                 } else {
461                     group = new TaskGrouping(affiliation);
462                     addGroup(group);
463                 }
464                 group.addTask(t);
465                 tasksMap.put(t.key, t);
466             }
467             // Update the task colors for each of the groups
468             float minAlpha = config.taskBarViewAffiliationColorMinAlpha;
469             int taskGroupCount = mGroups.size();
470             for (int i = 0; i < taskGroupCount; i++) {
471                 TaskGrouping group = mGroups.get(i);
472                 taskCount = group.getTaskCount();
473                 // Ignore the groups that only have one task
474                 if (taskCount <= 1) continue;
475                 // Calculate the group color distribution
476                 int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).taskAffiliationColor;
477                 float alphaStep = (1f - minAlpha) / taskCount;
478                 float alpha = 1f;
479                 for (int j = 0; j < taskCount; j++) {
480                     Task t = tasksMap.get(group.mTaskKeys.get(j));
481                     t.colorPrimary = Utilities.getColorWithOverlay(affiliationColor, Color.WHITE,
482                             alpha);
483                     alpha -= alphaStep;
484                 }
485             }
486         }
487     }
488
489     @Override
490     public String toString() {
491         String str = "Tasks:\n";
492         for (Task t : mTaskList.getTasks()) {
493             str += "  " + t.toString() + "\n";
494         }
495         return str;
496     }
497 }