| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.recents.model; |
| |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; |
| |
| import android.app.ActivityManager; |
| import android.app.ActivityTaskManager; |
| import android.app.KeyguardManager; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.util.SparseBooleanArray; |
| |
| import com.android.systemui.shared.recents.model.Task; |
| import com.android.systemui.shared.recents.model.Task.TaskKey; |
| import com.android.systemui.shared.recents.model.ThumbnailData; |
| import com.android.systemui.shared.system.ActivityManagerWrapper; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| |
| /** |
| * This class stores the loading state as it goes through multiple stages of loading: |
| * 1) preloadRawTasks() will load the raw set of recents tasks from the system |
| * 2) preloadPlan() will construct a new task stack with all metadata and only icons and |
| * thumbnails that are currently in the cache |
| * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load |
| * options specified, such that we can transition into the Recents activity seamlessly |
| */ |
| public class RecentsTaskLoadPlan { |
| |
| /** The set of conditions to preload tasks. */ |
| public static class PreloadOptions { |
| public boolean loadTitles = true; |
| } |
| |
| /** The set of conditions to load tasks. */ |
| public static class Options { |
| public int runningTaskId = -1; |
| public boolean loadIcons = true; |
| public boolean loadThumbnails = false; |
| public boolean onlyLoadForCache = false; |
| public boolean onlyLoadPausedActivities = false; |
| public int numVisibleTasks = 0; |
| public int numVisibleTaskThumbnails = 0; |
| } |
| |
| private final Context mContext; |
| private final KeyguardManager mKeyguardManager; |
| |
| private List<ActivityManager.RecentTaskInfo> mRawTasks; |
| private TaskStack mStack; |
| |
| private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray(); |
| |
| public RecentsTaskLoadPlan(Context context) { |
| mContext = context; |
| mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); |
| } |
| |
| /** |
| * Preloads the list of recent tasks from the system. After this call, the TaskStack will |
| * have a list of all the recent tasks with their metadata, not including icons or |
| * thumbnails which were not cached and have to be loaded. |
| * |
| * The tasks will be ordered by: |
| * - least-recent to most-recent stack tasks |
| * |
| * Note: Do not lock, since this can be calling back to the loader, which separately also drives |
| * this call (callers should synchronize on the loader before making this call). |
| */ |
| public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId, |
| int currentUserId) { |
| Resources res = mContext.getResources(); |
| ArrayList<Task> allTasks = new ArrayList<>(); |
| if (mRawTasks == null) { |
| mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks( |
| ActivityTaskManager.getMaxRecentTasksStatic(), currentUserId); |
| |
| // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it |
| Collections.reverse(mRawTasks); |
| } |
| |
| int taskCount = mRawTasks.size(); |
| for (int i = 0; i < taskCount; i++) { |
| ActivityManager.RecentTaskInfo t = mRawTasks.get(i); |
| |
| // Compose the task key |
| final ComponentName sourceComponent = t.origActivity != null |
| // Activity alias if there is one |
| ? t.origActivity |
| // The real activity if there is no alias (or the target if there is one) |
| : t.realActivity; |
| final int windowingMode = t.configuration.windowConfiguration.getWindowingMode(); |
| TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent, |
| sourceComponent, t.userId, t.lastActiveTime, t.displayId); |
| |
| boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM; |
| boolean isStackTask = !isFreeformTask; |
| boolean isLaunchTarget = taskKey.id == runningTaskId; |
| |
| ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); |
| if (info == null) { |
| continue; |
| } |
| |
| // Load the title, icon, and color |
| String title = opts.loadTitles |
| ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription) |
| : ""; |
| String titleDescription = opts.loadTitles |
| ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription) |
| : ""; |
| Drawable icon = isStackTask |
| ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false) |
| : null; |
| ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey, |
| false /* loadIfNotCached */, false /* storeInCache */); |
| int activityColor = loader.getActivityPrimaryColor(t.taskDescription); |
| int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription); |
| boolean isSystemApp = (info != null) && |
| ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); |
| |
| // TODO: Refactor to not do this every preload |
| if (mTmpLockedUsers.indexOfKey(t.userId) < 0) { |
| mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId)); |
| } |
| boolean isLocked = mTmpLockedUsers.get(t.userId); |
| |
| // Add the task to the stack |
| Task task = new Task(taskKey, icon, |
| thumbnail, title, titleDescription, activityColor, backgroundColor, |
| isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow, |
| t.taskDescription, t.resizeMode, t.topActivity, isLocked); |
| |
| allTasks.add(task); |
| } |
| |
| // Initialize the stacks |
| mStack = new TaskStack(); |
| mStack.setTasks(allTasks, false /* notifyStackChanges */); |
| } |
| |
| /** |
| * Called to apply the actual loading based on the specified conditions. |
| * |
| * Note: Do not lock, since this can be calling back to the loader, which separately also drives |
| * this call (callers should synchronize on the loader before making this call). |
| */ |
| public void executePlan(Options opts, RecentsTaskLoader loader) { |
| Resources res = mContext.getResources(); |
| |
| // Iterate through each of the tasks and load them according to the load conditions. |
| ArrayList<Task> tasks = mStack.getTasks(); |
| int taskCount = tasks.size(); |
| for (int i = 0; i < taskCount; i++) { |
| Task task = tasks.get(i); |
| TaskKey taskKey = task.key; |
| |
| boolean isRunningTask = (task.key.id == opts.runningTaskId); |
| boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks); |
| boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails); |
| |
| // If requested, skip the running task |
| if (opts.onlyLoadPausedActivities && isRunningTask) { |
| continue; |
| } |
| |
| if (opts.loadIcons && (isRunningTask || isVisibleTask)) { |
| if (task.icon == null) { |
| task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, |
| true); |
| } |
| } |
| if (opts.loadThumbnails && isVisibleThumbnail) { |
| task.thumbnail = loader.getAndUpdateThumbnail(taskKey, |
| true /* loadIfNotCached */, true /* storeInCache */); |
| } |
| } |
| } |
| |
| /** |
| * Returns the TaskStack from the preloaded list of recent tasks. |
| */ |
| public TaskStack getTaskStack() { |
| return mStack; |
| } |
| |
| /** Returns whether there are any tasks in any stacks. */ |
| public boolean hasTasks() { |
| if (mStack != null) { |
| return mStack.getTaskCount() > 0; |
| } |
| return false; |
| } |
| } |