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.shared.recents.model;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import android.app.ActivityManager;
22 import android.app.ActivityManager.TaskDescription;
23 import android.app.TaskInfo;
24 import android.content.ComponentName;
25 import android.content.Intent;
26 import android.content.pm.ActivityInfo;
27 import android.graphics.Color;
28 import android.graphics.drawable.Drawable;
29 import android.view.ViewDebug;
30 
31 import com.android.systemui.shared.recents.utilities.Utilities;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.Objects;
36 
37 /**
38  * A task in the recent tasks list.
39  */
40 public class Task {
41 
42     public static final String TAG = "Task";
43 
44     /* Task callbacks */
45     @Deprecated
46     public interface TaskCallbacks {
47         /* Notifies when a task has been bound */
onTaskDataLoaded(Task task, ThumbnailData thumbnailData)48         void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
49         /* Notifies when a task has been unbound */
onTaskDataUnloaded()50         void onTaskDataUnloaded();
51         /* Notifies when a task's windowing mode has changed. */
onTaskWindowingModeChanged()52         void onTaskWindowingModeChanged();
53     }
54 
55     /* The Task Key represents the unique primary key for the task */
56     public static class TaskKey {
57         @ViewDebug.ExportedProperty(category="recents")
58         public final int id;
59         @ViewDebug.ExportedProperty(category="recents")
60         public int windowingMode;
61         @ViewDebug.ExportedProperty(category="recents")
62         public final Intent baseIntent;
63         @ViewDebug.ExportedProperty(category="recents")
64         public final int userId;
65         @ViewDebug.ExportedProperty(category="recents")
66         public long lastActiveTime;
67 
68         /**
69          * The id of the task was running from which display.
70          */
71         @ViewDebug.ExportedProperty(category = "recents")
72         public final int displayId;
73 
74         // The source component name which started this task
75         public final ComponentName sourceComponent;
76 
77         private int mHashCode;
78 
TaskKey(TaskInfo t)79         public TaskKey(TaskInfo t) {
80             ComponentName sourceComponent = t.origActivity != null
81                     // Activity alias if there is one
82                     ? t.origActivity
83                     // The real activity if there is no alias (or the target if there is one)
84                     : t.realActivity;
85             this.id = t.taskId;
86             this.windowingMode = t.configuration.windowConfiguration.getWindowingMode();
87             this.baseIntent = t.baseIntent;
88             this.sourceComponent = sourceComponent;
89             this.userId = t.userId;
90             this.lastActiveTime = t.lastActiveTime;
91             this.displayId = t.displayId;
92             updateHashCode();
93         }
94 
TaskKey(int id, int windowingMode, Intent intent, ComponentName sourceComponent, int userId, long lastActiveTime)95         public TaskKey(int id, int windowingMode, Intent intent,
96                 ComponentName sourceComponent, int userId, long lastActiveTime) {
97             this.id = id;
98             this.windowingMode = windowingMode;
99             this.baseIntent = intent;
100             this.sourceComponent = sourceComponent;
101             this.userId = userId;
102             this.lastActiveTime = lastActiveTime;
103             this.displayId = DEFAULT_DISPLAY;
104             updateHashCode();
105         }
106 
TaskKey(int id, int windowingMode, Intent intent, ComponentName sourceComponent, int userId, long lastActiveTime, int displayId)107         public TaskKey(int id, int windowingMode, Intent intent,
108                 ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
109             this.id = id;
110             this.windowingMode = windowingMode;
111             this.baseIntent = intent;
112             this.sourceComponent = sourceComponent;
113             this.userId = userId;
114             this.lastActiveTime = lastActiveTime;
115             this.displayId = displayId;
116             updateHashCode();
117         }
118 
setWindowingMode(int windowingMode)119         public void setWindowingMode(int windowingMode) {
120             this.windowingMode = windowingMode;
121             updateHashCode();
122         }
123 
getComponent()124         public ComponentName getComponent() {
125             return this.baseIntent.getComponent();
126         }
127 
getPackageName()128         public String getPackageName() {
129             if (this.baseIntent.getComponent() != null) {
130                 return this.baseIntent.getComponent().getPackageName();
131             }
132             return this.baseIntent.getPackage();
133         }
134 
135         @Override
equals(Object o)136         public boolean equals(Object o) {
137             if (!(o instanceof TaskKey)) {
138                 return false;
139             }
140             TaskKey otherKey = (TaskKey) o;
141             return id == otherKey.id
142                     && windowingMode == otherKey.windowingMode
143                     && userId == otherKey.userId;
144         }
145 
146         @Override
hashCode()147         public int hashCode() {
148             return mHashCode;
149         }
150 
151         @Override
toString()152         public String toString() {
153             return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
154                     + " lastActiveTime=" + lastActiveTime;
155         }
156 
updateHashCode()157         private void updateHashCode() {
158             mHashCode = Objects.hash(id, windowingMode, userId);
159         }
160     }
161 
162     @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
163     public TaskKey key;
164 
165     /**
166      * The temporary sort index in the stack, used when ordering the stack.
167      */
168     @Deprecated
169     public int temporarySortIndexInStack;
170 
171     /**
172      * The icon is the task description icon (if provided), which falls back to the activity icon,
173      * which can then fall back to the application icon.
174      */
175     public Drawable icon;
176     public ThumbnailData thumbnail;
177     @ViewDebug.ExportedProperty(category="recents")
178     @Deprecated
179     public String title;
180     @ViewDebug.ExportedProperty(category="recents")
181     public String titleDescription;
182     @ViewDebug.ExportedProperty(category="recents")
183     public int colorPrimary;
184     @ViewDebug.ExportedProperty(category="recents")
185     public int colorBackground;
186     @ViewDebug.ExportedProperty(category="recents")
187     @Deprecated
188     public boolean useLightOnPrimaryColor;
189 
190     /**
191      * The task description for this task, only used to reload task icons.
192      */
193     public TaskDescription taskDescription;
194 
195     /**
196      * The state isLaunchTarget will be set for the correct task upon launching Recents.
197      */
198     @ViewDebug.ExportedProperty(category="recents")
199     @Deprecated
200     public boolean isLaunchTarget;
201     @ViewDebug.ExportedProperty(category="recents")
202     @Deprecated
203     public boolean isStackTask;
204     @ViewDebug.ExportedProperty(category="recents")
205     @Deprecated
206     public boolean isSystemApp;
207     @ViewDebug.ExportedProperty(category="recents")
208     public boolean isDockable;
209 
210     /**
211      * Resize mode. See {@link ActivityInfo#resizeMode}.
212      */
213     @ViewDebug.ExportedProperty(category="recents")
214     @Deprecated
215     public int resizeMode;
216 
217     @ViewDebug.ExportedProperty(category="recents")
218     public ComponentName topActivity;
219 
220     @ViewDebug.ExportedProperty(category="recents")
221     public boolean isLocked;
222 
223     @Deprecated
224     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
225 
Task()226     public Task() {
227         // Do nothing
228     }
229 
230     /**
231      * Creates a task object from the provided task info
232      */
from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked)233     public static Task from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked) {
234         ActivityManager.TaskDescription td = taskInfo.taskDescription;
235         return new Task(taskKey,
236                 td != null ? td.getPrimaryColor() : 0,
237                 td != null ? td.getBackgroundColor() : 0,
238                 taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);
239     }
240 
Task(TaskKey key)241     public Task(TaskKey key) {
242         this.key = key;
243         this.taskDescription = new TaskDescription();
244     }
245 
Task(TaskKey key, int colorPrimary, int colorBackground, boolean isDockable, boolean isLocked, TaskDescription taskDescription, ComponentName topActivity)246     public Task(TaskKey key, int colorPrimary, int colorBackground,
247             boolean isDockable, boolean isLocked, TaskDescription taskDescription,
248             ComponentName topActivity) {
249         this.key = key;
250         this.colorPrimary = colorPrimary;
251         this.colorBackground = colorBackground;
252         this.taskDescription = taskDescription;
253         this.isDockable = isDockable;
254         this.isLocked = isLocked;
255         this.topActivity = topActivity;
256     }
257 
258     @Deprecated
Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title, String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp, boolean isDockable, TaskDescription taskDescription, int resizeMode, ComponentName topActivity, boolean isLocked)259     public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
260             String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
261             boolean isStackTask, boolean isSystemApp, boolean isDockable,
262             TaskDescription taskDescription, int resizeMode, ComponentName topActivity,
263             boolean isLocked) {
264         this.key = key;
265         this.icon = icon;
266         this.thumbnail = thumbnail;
267         this.title = title;
268         this.titleDescription = titleDescription;
269         this.colorPrimary = colorPrimary;
270         this.colorBackground = colorBackground;
271         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
272                 Color.WHITE) > 3f;
273         this.taskDescription = taskDescription;
274         this.isLaunchTarget = isLaunchTarget;
275         this.isStackTask = isStackTask;
276         this.isSystemApp = isSystemApp;
277         this.isDockable = isDockable;
278         this.resizeMode = resizeMode;
279         this.topActivity = topActivity;
280         this.isLocked = isLocked;
281     }
282 
283     /**
284      * Copies the metadata from another task, but retains the current callbacks.
285      */
286     @Deprecated
copyFrom(Task o)287     public void copyFrom(Task o) {
288         this.key = o.key;
289         this.icon = o.icon;
290         this.thumbnail = o.thumbnail;
291         this.title = o.title;
292         this.titleDescription = o.titleDescription;
293         this.colorPrimary = o.colorPrimary;
294         this.colorBackground = o.colorBackground;
295         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
296         this.taskDescription = o.taskDescription;
297         this.isLaunchTarget = o.isLaunchTarget;
298         this.isStackTask = o.isStackTask;
299         this.isSystemApp = o.isSystemApp;
300         this.isDockable = o.isDockable;
301         this.resizeMode = o.resizeMode;
302         this.isLocked = o.isLocked;
303         this.topActivity = o.topActivity;
304     }
305 
306     /**
307      * Add a callback.
308      */
309     @Deprecated
addCallback(TaskCallbacks cb)310     public void addCallback(TaskCallbacks cb) {
311         if (!mCallbacks.contains(cb)) {
312             mCallbacks.add(cb);
313         }
314     }
315 
316     /**
317      * Remove a callback.
318      */
319     @Deprecated
removeCallback(TaskCallbacks cb)320     public void removeCallback(TaskCallbacks cb) {
321         mCallbacks.remove(cb);
322     }
323 
324     /** Updates the task's windowing mode. */
325     @Deprecated
setWindowingMode(int windowingMode)326     public void setWindowingMode(int windowingMode) {
327         key.setWindowingMode(windowingMode);
328         int callbackCount = mCallbacks.size();
329         for (int i = 0; i < callbackCount; i++) {
330             mCallbacks.get(i).onTaskWindowingModeChanged();
331         }
332     }
333 
334     /** Notifies the callback listeners that this task has been loaded */
335     @Deprecated
notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon)336     public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
337         this.icon = applicationIcon;
338         this.thumbnail = thumbnailData;
339         int callbackCount = mCallbacks.size();
340         for (int i = 0; i < callbackCount; i++) {
341             mCallbacks.get(i).onTaskDataLoaded(this, thumbnailData);
342         }
343     }
344 
345     /** Notifies the callback listeners that this task has been unloaded */
346     @Deprecated
notifyTaskDataUnloaded(Drawable defaultApplicationIcon)347     public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) {
348         icon = defaultApplicationIcon;
349         thumbnail = null;
350         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
351             mCallbacks.get(i).onTaskDataUnloaded();
352         }
353     }
354 
355     /**
356      * Returns the top activity component.
357      */
getTopComponent()358     public ComponentName getTopComponent() {
359         return topActivity != null
360                 ? topActivity
361                 : key.baseIntent.getComponent();
362     }
363 
364     @Override
equals(Object o)365     public boolean equals(Object o) {
366         // Check that the id matches
367         Task t = (Task) o;
368         return key.equals(t.key);
369     }
370 
371     @Override
toString()372     public String toString() {
373         return "[" + key.toString() + "] " + title;
374     }
375 
dump(String prefix, PrintWriter writer)376     public void dump(String prefix, PrintWriter writer) {
377         writer.print(prefix); writer.print(key);
378         if (!isDockable) {
379             writer.print(" dockable=N");
380         }
381         if (isLaunchTarget) {
382             writer.print(" launchTarget=Y");
383         }
384         if (isLocked) {
385             writer.print(" locked=Y");
386         }
387         writer.print(" "); writer.print(title);
388         writer.println();
389     }
390 }
391