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.content.Context;
     21 import android.content.pm.ActivityInfo;
     22 import android.content.pm.ApplicationInfo;
     23 import android.content.pm.UserInfo;
     24 import android.content.res.Resources;
     25 import android.graphics.Bitmap;
     26 import android.graphics.drawable.Drawable;
     27 import android.os.UserHandle;
     28 import android.os.UserManager;
     29 import android.util.ArraySet;
     30 import android.util.SparseArray;
     31 import android.util.SparseIntArray;
     32 
     33 import com.android.systemui.Prefs;
     34 import com.android.systemui.R;
     35 import com.android.systemui.recents.Recents;
     36 import com.android.systemui.recents.RecentsConfiguration;
     37 import com.android.systemui.recents.RecentsDebugFlags;
     38 import com.android.systemui.recents.misc.SystemServicesProxy;
     39 
     40 import java.util.ArrayList;
     41 import java.util.Collections;
     42 import java.util.List;
     43 
     44 
     45 /**
     46  * This class stores the loading state as it goes through multiple stages of loading:
     47  *   1) preloadRawTasks() will load the raw set of recents tasks from the system
     48  *   2) preloadPlan() will construct a new task stack with all metadata and only icons and
     49  *      thumbnails that are currently in the cache
     50  *   3) executePlan() will actually load and fill in the icons and thumbnails according to the load
     51  *      options specified, such that we can transition into the Recents activity seamlessly
     52  */
     53 public class RecentsTaskLoadPlan {
     54 
     55     private static int MIN_NUM_TASKS = 5;
     56     private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
     57             6 /* hrs */;
     58 
     59     /** The set of conditions to load tasks. */
     60     public static class Options {
     61         public int runningTaskId = -1;
     62         public boolean loadIcons = true;
     63         public boolean loadThumbnails = true;
     64         public boolean onlyLoadForCache = false;
     65         public boolean onlyLoadPausedActivities = false;
     66         public int numVisibleTasks = 0;
     67         public int numVisibleTaskThumbnails = 0;
     68     }
     69 
     70     Context mContext;
     71 
     72     List<ActivityManager.RecentTaskInfo> mRawTasks;
     73     TaskStack mStack;
     74     ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
     75 
     76     /** Package level ctor */
     77     RecentsTaskLoadPlan(Context context) {
     78         mContext = context;
     79     }
     80 
     81     private void updateCurrentQuietProfilesCache(int currentUserId) {
     82         mCurrentQuietProfiles.clear();
     83 
     84         if (currentUserId == UserHandle.USER_CURRENT) {
     85             currentUserId = ActivityManager.getCurrentUser();
     86         }
     87         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
     88         List<UserInfo> profiles = userManager.getProfiles(currentUserId);
     89         if (profiles != null) {
     90             for (int i = 0; i < profiles.size(); i++) {
     91                 UserInfo user  = profiles.get(i);
     92                 if (user.isManagedProfile() && user.isQuietModeEnabled()) {
     93                     mCurrentQuietProfiles.add(user.id);
     94                 }
     95             }
     96         }
     97     }
     98 
     99     /**
    100      * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
    101      * to most-recent order.
    102      */
    103     public synchronized void preloadRawTasks(boolean includeFrontMostExcludedTask) {
    104         int currentUserId = UserHandle.USER_CURRENT;
    105         updateCurrentQuietProfilesCache(currentUserId);
    106         SystemServicesProxy ssp = Recents.getSystemServices();
    107         mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
    108                 currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
    109 
    110         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
    111         Collections.reverse(mRawTasks);
    112     }
    113 
    114     /**
    115      * Preloads the list of recent tasks from the system. After this call, the TaskStack will
    116      * have a list of all the recent tasks with their metadata, not including icons or
    117      * thumbnails which were not cached and have to be loaded.
    118      *
    119      * The tasks will be ordered by:
    120      * - least-recent to most-recent stack tasks
    121      * - least-recent to most-recent freeform tasks
    122      */
    123     public synchronized void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
    124             boolean includeFrontMostExcludedTask) {
    125         Resources res = mContext.getResources();
    126         ArrayList<Task> allTasks = new ArrayList<>();
    127         if (mRawTasks == null) {
    128             preloadRawTasks(includeFrontMostExcludedTask);
    129         }
    130 
    131         SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
    132         SparseIntArray affiliatedTaskCounts = new SparseIntArray();
    133         String dismissDescFormat = mContext.getString(
    134                 R.string.accessibility_recents_item_will_be_dismissed);
    135         String appInfoDescFormat = mContext.getString(
    136                 R.string.accessibility_recents_item_open_app_info);
    137         long lastStackActiveTime = Prefs.getLong(mContext,
    138                 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
    139         if (RecentsDebugFlags.Static.EnableMockTasks) {
    140             lastStackActiveTime = 0;
    141         }
    142         long newLastStackActiveTime = -1;
    143         int taskCount = mRawTasks.size();
    144         for (int i = 0; i < taskCount; i++) {
    145             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
    146 
    147             // Compose the task key
    148             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
    149                     t.userId, t.firstActiveTime, t.lastActiveTime);
    150 
    151             // This task is only shown in the stack if it statisfies the historical time or min
    152             // number of tasks constraints. Freeform tasks are also always shown.
    153             boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
    154             boolean isStackTask = isFreeformTask || !isHistoricalTask(t) ||
    155                     (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
    156             boolean isLaunchTarget = taskKey.id == runningTaskId;
    157 
    158             // The last stack active time is the baseline for which we show visible tasks.  Since
    159             // the system will store all the tasks, we don't want to show the tasks prior to the
    160             // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
    161             // the other stack-task constraints.
    162             if (isStackTask && newLastStackActiveTime < 0) {
    163                 newLastStackActiveTime = t.lastActiveTime;
    164             }
    165 
    166             // Load the title, icon, and color
    167             ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
    168             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
    169             String titleDescription = loader.getAndUpdateContentDescription(taskKey, res);
    170             String dismissDescription = String.format(dismissDescFormat, titleDescription);
    171             String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
    172             Drawable icon = isStackTask
    173                     ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
    174                     : null;
    175             Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false /* loadIfNotCached */);
    176             int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
    177             int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
    178             boolean isSystemApp = (info != null) &&
    179                     ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
    180 
    181             // Add the task to the stack
    182             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
    183                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
    184                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
    185                     t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity);
    186 
    187             allTasks.add(task);
    188             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
    189             affiliatedTasks.put(taskKey.id, taskKey);
    190         }
    191         if (newLastStackActiveTime != -1) {
    192             Prefs.putLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
    193                     newLastStackActiveTime);
    194         }
    195 
    196         // Initialize the stacks
    197         mStack = new TaskStack();
    198         mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
    199     }
    200 
    201     /**
    202      * Called to apply the actual loading based on the specified conditions.
    203      */
    204     public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
    205             TaskResourceLoadQueue loadQueue) {
    206         RecentsConfiguration config = Recents.getConfiguration();
    207         Resources res = mContext.getResources();
    208 
    209         // Iterate through each of the tasks and load them according to the load conditions.
    210         ArrayList<Task> tasks = mStack.getStackTasks();
    211         int taskCount = tasks.size();
    212         for (int i = 0; i < taskCount; i++) {
    213             Task task = tasks.get(i);
    214             Task.TaskKey taskKey = task.key;
    215 
    216             boolean isRunningTask = (task.key.id == opts.runningTaskId);
    217             boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
    218             boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
    219 
    220             // If requested, skip the running task
    221             if (opts.onlyLoadPausedActivities && isRunningTask) {
    222                 continue;
    223             }
    224 
    225             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
    226                 if (task.icon == null) {
    227                     task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
    228                             true);
    229                 }
    230             }
    231             if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
    232                 if (task.thumbnail == null || isRunningTask) {
    233                     if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
    234                         task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
    235                                 true /* loadIfNotCached */);
    236                     } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
    237                         loadQueue.addTask(task);
    238                     }
    239                 }
    240             }
    241         }
    242     }
    243 
    244     /**
    245      * Returns the TaskStack from the preloaded list of recent tasks.
    246      */
    247     public TaskStack getTaskStack() {
    248         return mStack;
    249     }
    250 
    251     /** Returns whether there are any tasks in any stacks. */
    252     public boolean hasTasks() {
    253         if (mStack != null) {
    254             return mStack.getTaskCount() > 0;
    255         }
    256         return false;
    257     }
    258 
    259     /**
    260      * Returns whether this task is too old to be shown.
    261      */
    262     private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
    263         return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
    264     }
    265 }
    266