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