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