Home | History | Annotate | Download | only in model
      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