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