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.drawable.Drawable;
     26 import android.os.UserHandle;
     27 import android.os.UserManager;
     28 import android.provider.Settings;
     29 import android.provider.Settings.Secure;
     30 import android.util.ArraySet;
     31 import android.util.SparseArray;
     32 import android.util.SparseBooleanArray;
     33 import android.util.SparseIntArray;
     34 
     35 import com.android.systemui.Prefs;
     36 import com.android.systemui.R;
     37 import com.android.systemui.recents.Recents;
     38 import com.android.systemui.recents.RecentsDebugFlags;
     39 import com.android.systemui.recents.misc.SystemServicesProxy;
     40 import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
     41 import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
     42 
     43 import java.util.ArrayList;
     44 import java.util.Collections;
     45 import java.util.List;
     46 
     47 
     48 /**
     49  * This class stores the loading state as it goes through multiple stages of loading:
     50  *   1) preloadRawTasks() will load the raw set of recents tasks from the system
     51  *   2) preloadPlan() will construct a new task stack with all metadata and only icons and
     52  *      thumbnails that are currently in the cache
     53  *   3) executePlan() will actually load and fill in the icons and thumbnails according to the load
     54  *      options specified, such that we can transition into the Recents activity seamlessly
     55  */
     56 public class RecentsTaskLoadPlan {
     57 
     58     private static int MIN_NUM_TASKS = 5;
     59     private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
     60             6 /* hrs */;
     61 
     62     /** The set of conditions to load tasks. */
     63     public static class Options {
     64         public int runningTaskId = -1;
     65         public boolean loadIcons = true;
     66         public boolean loadThumbnails = false;
     67         public boolean onlyLoadForCache = false;
     68         public boolean onlyLoadPausedActivities = false;
     69         public int numVisibleTasks = 0;
     70         public int numVisibleTaskThumbnails = 0;
     71     }
     72 
     73     Context mContext;
     74 
     75     int mPreloadedUserId;
     76     List<ActivityManager.RecentTaskInfo> mRawTasks;
     77     TaskStack mStack;
     78     ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
     79 
     80     /** Package level ctor */
     81     RecentsTaskLoadPlan(Context context) {
     82         mContext = context;
     83     }
     84 
     85     private void updateCurrentQuietProfilesCache(int currentUserId) {
     86         mCurrentQuietProfiles.clear();
     87 
     88         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
     89         List<UserInfo> profiles = userManager.getProfiles(currentUserId);
     90         if (profiles != null) {
     91             for (int i = 0; i < profiles.size(); i++) {
     92                 UserInfo user  = profiles.get(i);
     93                 if (user.isManagedProfile() && user.isQuietModeEnabled()) {
     94                     mCurrentQuietProfiles.add(user.id);
     95                 }
     96             }
     97         }
     98     }
     99 
    100     /**
    101      * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
    102      * to most-recent order.
    103      *
    104      * Note: Do not lock, callers should synchronize on the loader before making this call.
    105      */
    106     void preloadRawTasks(boolean includeFrontMostExcludedTask) {
    107         SystemServicesProxy ssp = Recents.getSystemServices();
    108         int currentUserId = ssp.getCurrentUser();
    109         updateCurrentQuietProfilesCache(currentUserId);
    110         mPreloadedUserId = currentUserId;
    111         mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
    112                 currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
    113 
    114         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
    115         Collections.reverse(mRawTasks);
    116     }
    117 
    118     /**
    119      * Preloads the list of recent tasks from the system. After this call, the TaskStack will
    120      * have a list of all the recent tasks with their metadata, not including icons or
    121      * thumbnails which were not cached and have to be loaded.
    122      *
    123      * The tasks will be ordered by:
    124      * - least-recent to most-recent stack tasks
    125      * - least-recent to most-recent freeform tasks
    126      *
    127      * Note: Do not lock, since this can be calling back to the loader, which separately also drives
    128      * this call (callers should synchronize on the loader before making this call).
    129      */
    130     void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
    131             boolean includeFrontMostExcludedTask) {
    132         Resources res = mContext.getResources();
    133         ArrayList<Task> allTasks = new ArrayList<>();
    134         if (mRawTasks == null) {
    135             preloadRawTasks(includeFrontMostExcludedTask);
    136         }
    137 
    138         SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
    139         SparseIntArray affiliatedTaskCounts = new SparseIntArray();
    140         SparseBooleanArray lockedUsers = new SparseBooleanArray();
    141         String dismissDescFormat = mContext.getString(
    142                 R.string.accessibility_recents_item_will_be_dismissed);
    143         String appInfoDescFormat = mContext.getString(
    144                 R.string.accessibility_recents_item_open_app_info);
    145         int currentUserId = mPreloadedUserId;
    146         long legacyLastStackActiveTime = migrateLegacyLastStackActiveTime(currentUserId);
    147         long lastStackActiveTime = Settings.Secure.getLongForUser(mContext.getContentResolver(),
    148                 Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, legacyLastStackActiveTime, currentUserId);
    149         if (RecentsDebugFlags.Static.EnableMockTasks) {
    150             lastStackActiveTime = 0;
    151         }
    152         long newLastStackActiveTime = -1;
    153         int taskCount = mRawTasks.size();
    154         for (int i = 0; i < taskCount; i++) {
    155             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
    156 
    157             // Compose the task key
    158             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
    159                     t.userId, t.firstActiveTime, t.lastActiveTime);
    160 
    161             // This task is only shown in the stack if it satisfies the historical time or min
    162             // number of tasks constraints. Freeform tasks are also always shown.
    163             boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
    164             boolean isStackTask;
    165             if (Recents.getConfiguration().isGridEnabled) {
    166                 // When grid layout is enabled, we only show the first
    167                 // TaskGridLayoutAlgorithm.MAX_LAYOUT_FROM_HOME_TASK_COUNT} tasks.
    168                 isStackTask = t.lastActiveTime >= lastStackActiveTime &&
    169                     i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
    170             } else if (Recents.getConfiguration().isLowRamDevice) {
    171                 // Show a max of 3 items
    172                 isStackTask = t.lastActiveTime >= lastStackActiveTime &&
    173                         i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
    174             } else {
    175                 isStackTask = isFreeformTask || !isHistoricalTask(t) ||
    176                     (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
    177             }
    178             boolean isLaunchTarget = taskKey.id == runningTaskId;
    179 
    180             // The last stack active time is the baseline for which we show visible tasks.  Since
    181             // the system will store all the tasks, we don't want to show the tasks prior to the
    182             // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
    183             // the other stack-task constraints.
    184             if (isStackTask && newLastStackActiveTime < 0) {
    185                 newLastStackActiveTime = t.lastActiveTime;
    186             }
    187 
    188             // Load the title, icon, and color
    189             ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
    190             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
    191             String titleDescription = loader.getAndUpdateContentDescription(taskKey,
    192                     t.taskDescription, res);
    193             String dismissDescription = String.format(dismissDescFormat, titleDescription);
    194             String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
    195             Drawable icon = isStackTask
    196                     ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
    197                     : null;
    198             ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
    199                     false /* loadIfNotCached */, false /* storeInCache */);
    200             int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
    201             int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
    202             boolean isSystemApp = (info != null) &&
    203                     ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
    204             if (lockedUsers.indexOfKey(t.userId) < 0) {
    205                 lockedUsers.put(t.userId, Recents.getSystemServices().isDeviceLocked(t.userId));
    206             }
    207             boolean isLocked = lockedUsers.get(t.userId);
    208 
    209             // Add the task to the stack
    210             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
    211                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
    212                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
    213                     t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
    214                     isLocked);
    215 
    216             allTasks.add(task);
    217             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
    218             affiliatedTasks.put(taskKey.id, taskKey);
    219         }
    220         if (newLastStackActiveTime != -1) {
    221             Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
    222                     newLastStackActiveTime, currentUserId);
    223         }
    224 
    225         // Initialize the stacks
    226         mStack = new TaskStack();
    227         mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
    228     }
    229 
    230     /**
    231      * Called to apply the actual loading based on the specified conditions.
    232      *
    233      * Note: Do not lock, since this can be calling back to the loader, which separately also drives
    234      * this call (callers should synchronize on the loader before making this call).
    235      */
    236     void executePlan(Options opts, RecentsTaskLoader loader) {
    237         Resources res = mContext.getResources();
    238 
    239         // Iterate through each of the tasks and load them according to the load conditions.
    240         ArrayList<Task> tasks = mStack.getStackTasks();
    241         int taskCount = tasks.size();
    242         for (int i = 0; i < taskCount; i++) {
    243             Task task = tasks.get(i);
    244             Task.TaskKey taskKey = task.key;
    245 
    246             boolean isRunningTask = (task.key.id == opts.runningTaskId);
    247             boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
    248             boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
    249 
    250             // If requested, skip the running task
    251             if (opts.onlyLoadPausedActivities && isRunningTask) {
    252                 continue;
    253             }
    254 
    255             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
    256                 if (task.icon == null) {
    257                     task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
    258                             true);
    259                 }
    260             }
    261             if (opts.loadThumbnails && isVisibleThumbnail) {
    262                 task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
    263                         true /* loadIfNotCached */, true /* storeInCache */);
    264             }
    265         }
    266     }
    267 
    268     /**
    269      * Returns the TaskStack from the preloaded list of recent tasks.
    270      */
    271     public TaskStack getTaskStack() {
    272         return mStack;
    273     }
    274 
    275     /**
    276      * Returns the raw list of recent tasks.
    277      */
    278     public List<ActivityManager.RecentTaskInfo> getRawTasks() {
    279         return mRawTasks;
    280     }
    281 
    282     /** Returns whether there are any tasks in any stacks. */
    283     public boolean hasTasks() {
    284         if (mStack != null) {
    285             return mStack.getTaskCount() > 0;
    286         }
    287         return false;
    288     }
    289 
    290     /**
    291      * Returns whether this task is too old to be shown.
    292      */
    293     private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
    294         return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
    295     }
    296 
    297 
    298     /**
    299      * Migrate the last active time from the prefs to the secure settings.
    300      *
    301      * The first time this runs, it will:
    302      * 1) fetch the last stack active time from the prefs
    303      * 2) set the prefs to the last stack active time for all users
    304      * 3) clear the pref
    305      * 4) return the last stack active time
    306      *
    307      * Subsequent calls to this will return zero.
    308      */
    309     private long migrateLegacyLastStackActiveTime(int currentUserId) {
    310         long legacyLastStackActiveTime = Prefs.getLong(mContext,
    311                 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
    312         if (legacyLastStackActiveTime != -1) {
    313             Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME);
    314             UserManager userMgr = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    315             List<UserInfo> users = userMgr.getUsers();
    316             for (int i = 0; i < users.size(); i++) {
    317                 int userId = users.get(i).id;
    318                 if (userId != currentUserId) {
    319                     Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
    320                             legacyLastStackActiveTime, userId);
    321                 }
    322             }
    323             return legacyLastStackActiveTime;
    324         }
    325         return 0;
    326     }
    327 }
    328