summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
blob: f40c58dfdfeb0a2b32127bbdd1baed683e22cce7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
 * 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 android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
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 {
    static String TAG = "RecentsTaskLoadPlan";
    static boolean DEBUG = false;

    /** The set of conditions to load tasks. */
    public static class Options {
        public int runningTaskId = -1;
        public boolean loadIcons = true;
        public boolean loadThumbnails = true;
        public boolean onlyLoadForCache = false;
        public boolean onlyLoadPausedActivities = false;
        public int numVisibleTasks = 0;
        public int numVisibleTaskThumbnails = 0;
    }

    Context mContext;
    RecentsConfiguration mConfig;
    SystemServicesProxy mSystemServicesProxy;

    List<ActivityManager.RecentTaskInfo> mRawTasks;
    SparseArray<TaskStack> mStacks = new SparseArray<>();
    HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
            new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();

    /** Package level ctor */
    RecentsTaskLoadPlan(Context context, RecentsConfiguration config, SystemServicesProxy ssp) {
        mContext = context;
        mConfig = config;
        mSystemServicesProxy = ssp;
    }

    /**
     * An optimization to preload the raw list of tasks.
     */
    public synchronized void preloadRawTasks(boolean isTopTaskHome) {
        mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,
                UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
        Collections.reverse(mRawTasks);

        if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
    }

    /**
     * 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.
     */
    synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
        if (DEBUG) Log.d(TAG, "preloadPlan");

        // This activity info cache will be used for both preloadPlan() and executePlan()
        mActivityInfoCache.clear();

        // TODO (multi-display): Currently assume the primary display
        Rect displayBounds = mSystemServicesProxy.getWindowRect();

        Resources res = mContext.getResources();
        SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
        if (mRawTasks == null) {
            preloadRawTasks(isTopTaskHome);
        }
        int taskCount = mRawTasks.size();
        for (int i = 0; i < taskCount; i++) {
            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);

            // Compose the task key
            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
                    t.userId, t.firstActiveTime, t.lastActiveTime);

            // Get an existing activity info handle if possible
            Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
            ActivityInfoHandle infoHandle;
            boolean hadCachedActivityInfo = false;
            if (mActivityInfoCache.containsKey(cnKey)) {
                infoHandle = mActivityInfoCache.get(cnKey);
                hadCachedActivityInfo = true;
            } else {
                infoHandle = new ActivityInfoHandle();
            }

            // Load the label, icon, and color
            String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
                    mSystemServicesProxy, infoHandle);
            String contentDescription = loader.getAndUpdateContentDescription(taskKey,
                    activityLabel, mSystemServicesProxy, res);
            Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
                    mSystemServicesProxy, res, infoHandle, false);
            int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);

            // Update the activity info cache
            if (!hadCachedActivityInfo && infoHandle.info != null) {
                mActivityInfoCache.put(cnKey, infoHandle);
            }

            Bitmap icon = t.taskDescription != null
                    ? t.taskDescription.getInMemoryIcon()
                    : null;
            String iconFilename = t.taskDescription != null
                    ? t.taskDescription.getIconFilename()
                    : null;

            // Add the task to the stack
            Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID),
                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription,
                    activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled,
                    icon, iconFilename);
            task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
            if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);

            if (!mConfig.multiStackEnabled ||
                    Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
                int firstStackId = 0;
                ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
                if (stackTasks == null) {
                    stackTasks = new ArrayList<>();
                    stacksTasks.put(firstStackId, stackTasks);
                }
                stackTasks.add(task);
            } else {
                ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
                if (stackTasks == null) {
                    stackTasks = new ArrayList<>();
                    stacksTasks.put(t.stackId, stackTasks);
                }
                stackTasks.add(task);
            }
        }

        // Initialize the stacks
        SparseArray<ActivityManager.StackInfo> stackInfos = mSystemServicesProxy.getAllStackInfos();
        mStacks.clear();
        int stackCount = stacksTasks.size();
        for (int i = 0; i < stackCount; i++) {
            int stackId = stacksTasks.keyAt(i);
            ActivityManager.StackInfo info = stackInfos.get(stackId);
            ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
            TaskStack stack = new TaskStack(stackId);
            if (Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
                stack.setBounds(displayBounds, displayBounds);
            } else {
                stack.setBounds(info.bounds, displayBounds);
            }
            stack.setTasks(stackTasks);
            stack.createAffiliatedGroupings(mConfig);
            mStacks.put(stackId, stack);
        }
    }

    /**
     * Called to apply the actual loading based on the specified conditions.
     */
    synchronized void executePlan(Options opts, RecentsTaskLoader loader,
            TaskResourceLoadQueue loadQueue) {
        if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks +
                ", # thumbnails: " + opts.numVisibleTaskThumbnails +
                ", running task id: " + opts.runningTaskId);

        Resources res = mContext.getResources();

        // Iterate through each of the tasks and load them according to the load conditions.
        int stackCount = mStacks.size();
        for (int j = 0; j < stackCount; j++) {
            ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
            int taskCount = tasks.size();
            for (int i = 0; i < taskCount; i++) {
                ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
                Task task = tasks.get(i);
                Task.TaskKey taskKey = task.key;

                // Get an existing activity info handle if possible
                Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
                ActivityInfoHandle infoHandle;
                boolean hadCachedActivityInfo = false;
                if (mActivityInfoCache.containsKey(cnKey)) {
                    infoHandle = mActivityInfoCache.get(cnKey);
                    hadCachedActivityInfo = true;
                } else {
                    infoHandle = new ActivityInfoHandle();
                }

                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.activityIcon == null) {
                        if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
                        task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
                                t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
                    }
                }
                if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
                    if (task.thumbnail == null || isRunningTask) {
                        if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
                        if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
                            task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
                                    mSystemServicesProxy, true);
                        } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                            loadQueue.addTask(task);
                        }
                    }
                }

                // Update the activity info cache
                if (!hadCachedActivityInfo && infoHandle.info != null) {
                    mActivityInfoCache.put(cnKey, infoHandle);
                }
            }
        }
    }

    /**
     * Returns all TaskStacks from the preloaded list of recent tasks.
     */
    public ArrayList<TaskStack> getAllTaskStacks() {
        ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
        int stackCount = mStacks.size();
        for (int i = 0; i < stackCount; i++) {
            stacks.add(mStacks.valueAt(i));
        }
        // Ensure that we have at least one stack
        if (stacks.isEmpty()) {
            stacks.add(new TaskStack());
        }
        return stacks;
    }

    /**
     * Returns a specific TaskStack from the preloaded list of recent tasks.
     */
    public TaskStack getTaskStack(int stackId) {
        return mStacks.get(stackId);
    }

    /** Returns whether there are any tasks in any stacks. */
    public boolean hasTasks() {
        int stackCount = mStacks.size();
        for (int i = 0; i < stackCount; i++) {
            if (mStacks.valueAt(i).getTaskCount() > 0) {
                return true;
            }
        }
        return false;
    }
}