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.graphics.drawable.Drawable;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.view.ViewDebug;
30 
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 
34 import java.io.PrintWriter;
35 import java.util.Objects;
36 
37 /**
38  * A task in the recent tasks list.
39  * TODO: Move this into Launcher or see if we can remove now
40  */
41 public class Task {
42 
43     public static final String TAG = "Task";
44 
45     /**
46      * The Task Key represents the unique primary key for the task
47      */
48     public static class TaskKey implements Parcelable {
49         @ViewDebug.ExportedProperty(category="recents")
50         public final int id;
51         @ViewDebug.ExportedProperty(category="recents")
52         public int windowingMode;
53         @ViewDebug.ExportedProperty(category="recents")
54         @NonNull
55         public final Intent baseIntent;
56         @ViewDebug.ExportedProperty(category="recents")
57         public final int userId;
58         @ViewDebug.ExportedProperty(category="recents")
59         public long lastActiveTime;
60 
61         /**
62          * The id of the task was running from which display.
63          */
64         @ViewDebug.ExportedProperty(category = "recents")
65         public final int displayId;
66 
67         // The source component name which started this task
68         public final ComponentName sourceComponent;
69 
70         private int mHashCode;
71 
TaskKey(TaskInfo t)72         public TaskKey(TaskInfo t) {
73             ComponentName sourceComponent = t.origActivity != null
74                     // Activity alias if there is one
75                     ? t.origActivity
76                     // The real activity if there is no alias (or the target if there is one)
77                     : t.realActivity;
78             this.id = t.taskId;
79             this.windowingMode = t.configuration.windowConfiguration.getWindowingMode();
80             this.baseIntent = t.baseIntent;
81             this.sourceComponent = sourceComponent;
82             this.userId = t.userId;
83             this.lastActiveTime = t.lastActiveTime;
84             this.displayId = t.displayId;
85             updateHashCode();
86         }
87 
TaskKey(int id, int windowingMode, @NonNull Intent intent, ComponentName sourceComponent, int userId, long lastActiveTime)88         public TaskKey(int id, int windowingMode, @NonNull Intent intent,
89                 ComponentName sourceComponent, int userId, long lastActiveTime) {
90             this.id = id;
91             this.windowingMode = windowingMode;
92             this.baseIntent = intent;
93             this.sourceComponent = sourceComponent;
94             this.userId = userId;
95             this.lastActiveTime = lastActiveTime;
96             this.displayId = DEFAULT_DISPLAY;
97             updateHashCode();
98         }
99 
TaskKey(int id, int windowingMode, @NonNull Intent intent, ComponentName sourceComponent, int userId, long lastActiveTime, int displayId)100         public TaskKey(int id, int windowingMode, @NonNull Intent intent,
101                 ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
102             this.id = id;
103             this.windowingMode = windowingMode;
104             this.baseIntent = intent;
105             this.sourceComponent = sourceComponent;
106             this.userId = userId;
107             this.lastActiveTime = lastActiveTime;
108             this.displayId = displayId;
109             updateHashCode();
110         }
111 
setWindowingMode(int windowingMode)112         public void setWindowingMode(int windowingMode) {
113             this.windowingMode = windowingMode;
114             updateHashCode();
115         }
116 
getComponent()117         public ComponentName getComponent() {
118             return this.baseIntent.getComponent();
119         }
120 
getPackageName()121         public String getPackageName() {
122             if (this.baseIntent.getComponent() != null) {
123                 return this.baseIntent.getComponent().getPackageName();
124             }
125             return this.baseIntent.getPackage();
126         }
127 
128         @Override
equals(Object o)129         public boolean equals(Object o) {
130             if (!(o instanceof TaskKey)) {
131                 return false;
132             }
133             TaskKey otherKey = (TaskKey) o;
134             return id == otherKey.id
135                     && windowingMode == otherKey.windowingMode
136                     && userId == otherKey.userId;
137         }
138 
139         @Override
hashCode()140         public int hashCode() {
141             return mHashCode;
142         }
143 
144         @Override
toString()145         public String toString() {
146             return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
147                     + " lastActiveTime=" + lastActiveTime;
148         }
149 
updateHashCode()150         private void updateHashCode() {
151             mHashCode = Objects.hash(id, windowingMode, userId);
152         }
153 
154         public static final Parcelable.Creator<TaskKey> CREATOR =
155                 new Parcelable.Creator<TaskKey>() {
156                     @Override
157                     public TaskKey createFromParcel(Parcel source) {
158                         return TaskKey.readFromParcel(source);
159                     }
160 
161                     @Override
162                     public TaskKey[] newArray(int size) {
163                         return new TaskKey[size];
164                     }
165                 };
166 
167         @Override
writeToParcel(Parcel parcel, int flags)168         public final void writeToParcel(Parcel parcel, int flags) {
169             parcel.writeInt(id);
170             parcel.writeInt(windowingMode);
171             parcel.writeTypedObject(baseIntent, flags);
172             parcel.writeInt(userId);
173             parcel.writeLong(lastActiveTime);
174             parcel.writeInt(displayId);
175             parcel.writeTypedObject(sourceComponent, flags);
176         }
177 
readFromParcel(Parcel parcel)178         private static TaskKey readFromParcel(Parcel parcel) {
179             int id = parcel.readInt();
180             int windowingMode = parcel.readInt();
181             Intent baseIntent = parcel.readTypedObject(Intent.CREATOR);
182             int userId = parcel.readInt();
183             long lastActiveTime = parcel.readLong();
184             int displayId = parcel.readInt();
185             ComponentName sourceComponent = parcel.readTypedObject(ComponentName.CREATOR);
186 
187             return new TaskKey(id, windowingMode, baseIntent, sourceComponent, userId,
188                     lastActiveTime, displayId);
189         }
190 
191         @Override
describeContents()192         public int describeContents() {
193             return 0;
194         }
195     }
196 
197     @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
198     public TaskKey key;
199 
200     /**
201      * The icon is the task description icon (if provided), which falls back to the activity icon,
202      * which can then fall back to the application icon.
203      */
204     @Nullable public Drawable icon;
205     @Nullable public ThumbnailData thumbnail;
206     @ViewDebug.ExportedProperty(category="recents")
207     @Deprecated
208     public String title;
209     @ViewDebug.ExportedProperty(category="recents")
210     public String titleDescription;
211     @ViewDebug.ExportedProperty(category="recents")
212     public int colorPrimary;
213     @ViewDebug.ExportedProperty(category="recents")
214     public int colorBackground;
215 
216     /**
217      * The task description for this task, only used to reload task icons.
218      */
219     public TaskDescription taskDescription;
220 
221     @ViewDebug.ExportedProperty(category="recents")
222     public boolean isDockable;
223 
224     @ViewDebug.ExportedProperty(category="recents")
225     public ComponentName topActivity;
226 
227     @ViewDebug.ExportedProperty(category="recents")
228     public boolean isLocked;
229 
230     // Last snapshot data, only used for recent tasks
231     public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
232             new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
233 
Task()234     public Task() {
235         // Do nothing
236     }
237 
238     /**
239      * Creates a task object from the provided task info
240      */
from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked)241     public static Task from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked) {
242         ActivityManager.TaskDescription td = taskInfo.taskDescription;
243         return new Task(taskKey,
244                 td != null ? td.getPrimaryColor() : 0,
245                 td != null ? td.getBackgroundColor() : 0,
246                 taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);
247     }
248 
Task(TaskKey key)249     public Task(TaskKey key) {
250         this.key = key;
251         this.taskDescription = new TaskDescription();
252     }
253 
Task(Task other)254     public Task(Task other) {
255         this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
256                 other.isLocked, other.taskDescription, other.topActivity);
257         lastSnapshotData.set(other.lastSnapshotData);
258     }
259 
260     /**
261      * Use {@link Task#Task(Task)}.
262      */
263     @Deprecated
Task(TaskKey key, int colorPrimary, int colorBackground, boolean isDockable, boolean isLocked, TaskDescription taskDescription, ComponentName topActivity)264     public Task(TaskKey key, int colorPrimary, int colorBackground,
265             boolean isDockable, boolean isLocked, TaskDescription taskDescription,
266             ComponentName topActivity) {
267         this.key = key;
268         this.colorPrimary = colorPrimary;
269         this.colorBackground = colorBackground;
270         this.taskDescription = taskDescription;
271         this.isDockable = isDockable;
272         this.isLocked = isLocked;
273         this.topActivity = topActivity;
274     }
275 
276     /**
277      * Returns the top activity component.
278      */
getTopComponent()279     public ComponentName getTopComponent() {
280         return topActivity != null
281                 ? topActivity
282                 : key.baseIntent.getComponent();
283     }
284 
setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask)285     public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) {
286         lastSnapshotData.set(rawTask.lastSnapshotData);
287     }
288 
289     /**
290      * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
291      */
getVisibleThumbnailRatio(boolean clipInsets)292     public float getVisibleThumbnailRatio(boolean clipInsets) {
293         if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
294             return 0f;
295         }
296 
297         float availableWidth = lastSnapshotData.taskSize.x;
298         float availableHeight = lastSnapshotData.taskSize.y;
299         if (clipInsets) {
300             availableWidth -=
301                     (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
302             availableHeight -=
303                     (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
304         }
305         return availableWidth / availableHeight;
306     }
307 
308     @Override
equals(Object o)309     public boolean equals(Object o) {
310         // Check that the id matches
311         Task t = (Task) o;
312         return key.equals(t.key);
313     }
314 
315     @Override
toString()316     public String toString() {
317         return "[" + key.toString() + "] " + title;
318     }
319 
dump(String prefix, PrintWriter writer)320     public void dump(String prefix, PrintWriter writer) {
321         writer.print(prefix); writer.print(key);
322         if (!isDockable) {
323             writer.print(" dockable=N");
324         }
325         if (isLocked) {
326             writer.print(" locked=Y");
327         }
328         writer.print(" "); writer.print(title);
329         writer.println();
330     }
331 }
332