Merge "Preload only visible thumbnails and task icons. (Bug 17672056, Bug 18291345)" into lmp-mr1-dev
automerge: 147de3a
* commit '147de3a9766e205af068d1462beaea7a4a9fb9bc':
Preload only visible thumbnails and task icons. (Bug 17672056, Bug 18291345)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6713944..2561fae 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -776,7 +776,7 @@
This needs to match the constants in
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
-->
- <integer name="config_longPressOnHomeBehavior">1</integer>
+ <integer name="config_longPressOnHomeBehavior">0</integer>
<!-- Control the behavior when the user double-taps the home button.
0 - Nothing
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 2bfdb69..b5a6dac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -41,6 +41,7 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
@@ -64,6 +65,8 @@
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
final public static String EXTRA_REUSE_TASK_STACK_VIEWS = "recents.reuseTaskStackViews";
+ final public static String EXTRA_NUM_VISIBLE_TASKS = "recents.numVisibleTasks";
+ final public static String EXTRA_NUM_VISIBLE_THUMBNAILS = "recents.numVisibleThumbnails";
final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
@@ -76,6 +79,7 @@
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
+ static RecentsTaskLoadPlan sInstanceLoadPlan;
Context mContext;
LayoutInflater mInflater;
@@ -134,8 +138,15 @@
}
}
- // When we start, preload the metadata associated with the previous tasks
- RecentsTaskLoader.getInstance().preload(mContext, RecentsTaskLoader.ALL_TASKS);
+ // When we start, preload the metadata and icons associated with the recent tasks.
+ // We can use a new plan since the caches will be the same.
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+ launchOpts.loadThumbnails = false;
+ loader.loadTasks(mContext, plan, launchOpts);
}
public void onBootCompleted() {
@@ -183,9 +194,11 @@
}
public void onPreloadRecents() {
- // When we start, preload the metadata associated with the previous tasks
- RecentsTaskLoader.getInstance().preload(mContext,
- Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+ // Preload only the raw task list into a new load plan (which will be consumed by the
+ // RecentsActivity)
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ sInstanceLoadPlan.preloadRawTasks(true);
}
public void onCancelPreloadingRecents() {
@@ -194,8 +207,10 @@
void showRelativeAffiliatedTask(boolean showNextTask) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
- -1, -1, RecentsTaskLoader.ALL_TASKS, false, true, null, null);
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ TaskStack stack = plan.getTaskStack();
+
// Return early if there are no tasks
if (stack.getTaskCount() == 0) return;
@@ -411,11 +426,11 @@
* Creates the activity options for an app->recents transition.
*/
ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
- boolean isTopTaskHome) {
+ TaskStack stack, TaskStackView stackView) {
// Update the destination rect
Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(topTask.id, isTopTaskHome,
- toTask);
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
if (toTransform != null && toTask.key != null) {
Rect toTaskRect = toTransform.rect;
int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
@@ -443,16 +458,8 @@
}
/** Returns the transition rect for the given task id. */
- TaskViewTransform getThumbnailTransitionTransform(int runningTaskId, boolean isTopTaskHome,
- Task runningTaskOut) {
- // Get the stack of tasks that we are animating into
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
- runningTaskId, -1, RecentsTaskLoader.ALL_TASKS, false, isTopTaskHome, null, null);
- if (stack.getTaskCount() == 0) {
- return null;
- }
-
+ TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
+ int runningTaskId, Task runningTaskOut) {
// Find the running task in the TaskStack
Task task = null;
ArrayList<Task> tasks = stack.getTasks();
@@ -474,30 +481,42 @@
}
// Get the transform for the running task
- mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
- mDummyStackView.getScroller().setStackScrollToInitialState();
- mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task,
- mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null);
+ stackView.getScroller().setStackScrollToInitialState();
+ mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+ stackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
}
/** Starts the recents activity */
void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
- // If Recents is not the front-most activity and we should animate into it. If
- // the activity at the root of the top task stack in the home stack, then we just do a
- // simple transition. Otherwise, we animate to the rects defined by the Recents service,
- // which can differ depending on the number of items in the list.
- SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> recentTasks =
- ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
- boolean useThumbnailTransition = !isTopTaskHome;
- boolean hasRecentTasks = !recentTasks.isEmpty();
+ if (sInstanceLoadPlan == null) {
+ // Create a new load plan if onPreloadRecents() was never triggered
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ }
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+ // Prepare the dummy stack for the transition
+ mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+ TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = !isTopTaskHome && hasRecentTasks;
if (useThumbnailTransition) {
+ // Ensure that we load the running task's icon
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = topTask.id;
+ launchOpts.loadThumbnails = false;
+ loader.loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, isTopTaskHome);
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
+ mDummyStackView);
if (opts != null) {
- startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL, stackVr);
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
@@ -531,11 +550,11 @@
ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
startAlternateRecentsActivity(topTask, opts,
- fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME);
+ fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME, stackVr);
} else {
// Otherwise we do the normal fade from an unknown source
ActivityOptions opts = getUnknownTransitionActivityOptions();
- startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
}
}
mLastToggleTime = System.currentTimeMillis();
@@ -543,7 +562,8 @@
/** Starts the recents activity */
void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, String extraFlag) {
+ ActivityOptions opts, String extraFlag,
+ TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -555,6 +575,8 @@
intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews);
+ intent.putExtra(EXTRA_NUM_VISIBLE_TASKS, vr.numVisibleTasks);
+ intent.putExtra(EXTRA_NUM_VISIBLE_THUMBNAILS, vr.numVisibleThumbnails);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
} else {
@@ -575,6 +597,15 @@
}
}
+ /**
+ * Returns the preloaded load plan and invalidates it.
+ */
+ public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
+ RecentsTaskLoadPlan plan = sInstanceLoadPlan;
+ sInstanceLoadPlan = null;
+ return plan;
+ }
+
/**** OnAnimationStartedListener Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 9b84d2e..4c76af7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -64,11 +64,6 @@
public static String DebugModeVersion = "A";
}
- public static class RecentsTaskLoader {
- // XXX: This should be calculated on the first load
- public static final int PreloadFirstTasksCount = 6;
- }
-
public static class TaskStackView {
public static final int TaskStackOverscrollRange = 150;
public static final int FilterStartDelay = 25;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index de95ae8..9a7fec4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -27,10 +27,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.util.Pair;
import android.view.KeyEvent;
@@ -43,6 +40,7 @@
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
@@ -168,9 +166,10 @@
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
// When the screen turns off, dismiss Recents to Home
dismissRecentsToHome(false);
- // Start preloading some tasks in the background
- RecentsTaskLoader.getInstance().preload(RecentsActivity.this,
- Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+ // Preload the metadata for all tasks in the background
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(context);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
// When the search activity changes, update the Search widget
refreshSearchWidget();
@@ -193,6 +192,10 @@
// Update the configuration based on the launch intent
boolean fromSearchHome = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false);
+ int numVisibleTasks = launchIntent.getIntExtra(
+ AlternateRecentsComponent.EXTRA_NUM_VISIBLE_TASKS, 0);
+ int numVisibleThumbnails = launchIntent.getIntExtra(
+ AlternateRecentsComponent.EXTRA_NUM_VISIBLE_THUMBNAILS, 0);
mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
@@ -204,16 +207,29 @@
mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false);
- // Load all the tasks
+ // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
+ // reconstructing the task stack
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SpaceNode root = loader.reload(this,
- Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
- mConfig.launchedFromHome);
- ArrayList<TaskStack> stacks = root.getStacks();
- if (!stacks.isEmpty()) {
- mRecentsView.setTaskStacks(root.getStacks());
+ RecentsTaskLoadPlan plan = AlternateRecentsComponent.consumeInstanceLoadPlan();
+ if (plan == null) {
+ plan = loader.createLoadPlan(this);
+ loader.preloadTasks(plan, mConfig.launchedFromHome);
}
- mConfig.launchedWithNoRecentTasks = !root.hasTasks();
+
+ // Start loading tasks according to the load plan
+ RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+ loadOpts.runningTaskId = mConfig.launchedToTaskId;
+ loadOpts.numVisibleTasks = numVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = numVisibleThumbnails;
+ loader.loadTasks(this, plan, loadOpts);
+
+ SpaceNode root = plan.getSpaceNode();
+ ArrayList<TaskStack> stacks = root.getStacks();
+ boolean hasTasks = root.hasTasks();
+ if (hasTasks) {
+ mRecentsView.setTaskStacks(stacks);
+ }
+ mConfig.launchedWithNoRecentTasks = !hasTasks;
// Create the home intent runnable
Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -442,6 +458,8 @@
/** Inflates the debug overlay if debug mode is enabled. */
void inflateDebugOverlay() {
+ if (!Constants.DebugFlags.App.EnableDebugMode) return;
+
if (mConfig.debugModeEnabled && mDebugOverlay == null) {
// Inflate the overlay and seek bars
mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
@@ -600,13 +618,17 @@
settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
mConfig.debugModeEnabled = false;
inflateDebugOverlay();
- mDebugOverlay.disable();
+ if (mDebugOverlay != null) {
+ mDebugOverlay.disable();
+ }
} else {
// Enable the debug mode
settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
mConfig.debugModeEnabled = true;
inflateDebugOverlay();
- mDebugOverlay.enable();
+ if (mDebugOverlay != null) {
+ mDebugOverlay.enable();
+ }
}
Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
(mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 51b3fb5..9a4bd08 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -244,6 +244,7 @@
Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
if (thumbnail != null) {
+ thumbnail.setHasAlpha(false);
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
// screenshots are always composed onto a bitmap that has no alpha.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
new file mode 100644
index 0000000..41251c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -0,0 +1,224 @@
+/*
+ * 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.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+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:
+ * - preloadRawTasks() will load the raw set of recents tasks from the system
+ * - preloadPlan() will construct a new task stack with all metadata and only icons and thumbnails
+ * that are currently in the cache
+ * - 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 int numVisibleTasks = 0;
+ public int numVisibleTaskThumbnails = 0;
+ }
+
+ Context mContext;
+ RecentsConfiguration mConfig;
+ SystemServicesProxy mSystemServicesProxy;
+
+ List<ActivityManager.RecentTaskInfo> mRawTasks;
+ TaskStack mStack;
+ 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");
+
+ mActivityInfoCache.clear();
+ mStack = new TaskStack();
+
+ Resources res = mContext.getResources();
+ ArrayList<Task> loadedTasks = new ArrayList<Task>();
+ 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.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);
+ 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, activityIcon,
+ activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon,
+ iconFilename);
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
+ loadedTasks.add(task);
+ }
+ mStack.setTasks(loadedTasks);
+ mStack.createAffiliatedGroupings(mConfig);
+
+ // Assertion
+ if (mStack.getTaskCount() != mRawTasks.size()) {
+ throw new RuntimeException("Loading failed");
+ }
+ }
+
+ /**
+ * Called to apply the actual loading based on the specified conditions.
+ */
+ synchronized void executePlan(Options opts, RecentsTaskLoader loader) {
+ 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.
+ ArrayList<Task> tasks = mStack.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 (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) {
+ if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy,
+ true);
+ }
+ }
+
+ // Update the activity info cache
+ if (!hadCachedActivityInfo && infoHandle.info != null) {
+ mActivityInfoCache.put(cnKey, infoHandle);
+ }
+ }
+ }
+
+ /**
+ * Composes and returns a TaskStack from the preloaded list of recent tasks.
+ */
+ public TaskStack getTaskStack() {
+ return mStack;
+ }
+
+ /**
+ * Composes and returns a SpaceNode from the preloaded list of recent tasks.
+ */
+ public SpaceNode getSpaceNode() {
+ SpaceNode node = new SpaceNode();
+ node.setStack(mStack);
+ return node;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 390507f..6fd738d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -26,7 +26,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.UserHandle;
import android.util.Log;
import com.android.systemui.R;
@@ -34,11 +33,7 @@
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -205,9 +200,7 @@
// Load the thumbnail if it is stale or we haven't cached one yet
if (cachedThumbnail == null) {
cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
- if (cachedThumbnail != null) {
- cachedThumbnail.setHasAlpha(false);
- } else {
+ if (cachedThumbnail == null) {
cachedThumbnail = mDefaultThumbnail;
}
mThumbnailCache.put(t.key, cachedThumbnail);
@@ -260,7 +253,7 @@
private static final String TAG = "RecentsTaskLoader";
static RecentsTaskLoader sInstance;
- public static final int ALL_TASKS = -1;
+ static int INVALID_TASK_ID = -1;
SystemServicesProxy mSystemServicesProxy;
DrawableLruCache mApplicationIconCache;
@@ -273,6 +266,7 @@
int mMaxThumbnailCacheSize;
int mMaxIconCacheSize;
+ int mNumVisibleTasksLoaded;
BitmapDrawable mDefaultApplicationIcon;
Bitmap mDefaultThumbnail;
@@ -325,55 +319,6 @@
return mSystemServicesProxy;
}
- /** Gets the list of recent tasks, ordered from back to front. */
- private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp,
- int numTasksToLoad, boolean isTopTaskHome) {
- List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(numTasksToLoad, UserHandle.CURRENT.getIdentifier(),
- isTopTaskHome);
- Collections.reverse(tasks);
- return tasks;
- }
-
- /** Returns the activity icon using as many cached values as we can. */
- public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
- ActivityManager.TaskDescription td, SystemServicesProxy ssp,
- Resources res, ActivityInfoHandle infoHandle, boolean preloadTask) {
- // Return the cached activity icon if it exists
- Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
- if (icon != null) {
- return icon;
- }
-
- // If we are preloading this task, continue to load the task description icon or the
- // activity icon
- if (preloadTask) {
-
- // Return and cache the task description icon if it exists
- Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
- td.getIconFilename(), ssp, res);
- if (tdDrawable != null) {
- mApplicationIconCache.put(taskKey, tdDrawable);
- return tdDrawable;
- }
-
- // Load the icon from the activity info and cache it
- if (infoHandle.info == null) {
- infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
- taskKey.userId);
- }
- if (infoHandle.info != null) {
- icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
- if (icon != null) {
- mApplicationIconCache.put(taskKey, icon);
- return icon;
- }
- }
- }
- // If we couldn't load any icon, return null
- return null;
- }
-
/** Returns the activity label using as many cached values as we can. */
public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
ActivityManager.TaskDescription td, SystemServicesProxy ssp,
@@ -402,6 +347,63 @@
return label;
}
+ /** Returns the activity icon using as many cached values as we can. */
+ public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
+ ActivityManager.TaskDescription td, SystemServicesProxy ssp,
+ Resources res, ActivityInfoHandle infoHandle, boolean loadIfNotCached) {
+ // Return the cached activity icon if it exists
+ Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
+ if (icon != null) {
+ return icon;
+ }
+
+ if (loadIfNotCached) {
+ // Return and cache the task description icon if it exists
+ Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
+ td.getIconFilename(), ssp, res);
+ if (tdDrawable != null) {
+ mApplicationIconCache.put(taskKey, tdDrawable);
+ return tdDrawable;
+ }
+
+ // Load the icon from the activity info and cache it
+ if (infoHandle.info == null) {
+ infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
+ taskKey.userId);
+ }
+ if (infoHandle.info != null) {
+ icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
+ if (icon != null) {
+ mApplicationIconCache.put(taskKey, icon);
+ return icon;
+ }
+ }
+ }
+ // We couldn't load any icon
+ return null;
+ }
+
+ /** Returns the bitmap using as many cached values as we can. */
+ public Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
+ boolean loadIfNotCached) {
+ // Return the cached thumbnail if it exists
+ Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+ if (thumbnail != null) {
+ return thumbnail;
+ }
+
+ if (loadIfNotCached) {
+ // Load the thumbnail from the system
+ thumbnail = ssp.getTaskThumbnail(taskKey.id);
+ if (thumbnail != null) {
+ mThumbnailCache.put(taskKey, thumbnail);
+ return thumbnail;
+ }
+ }
+ // We couldn't load any thumbnail
+ return null;
+ }
+
/** Returns the activity's primary color. */
public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
RecentsConfiguration config) {
@@ -411,127 +413,33 @@
return config.taskBarViewDefaultBackgroundColor;
}
- /** Reload the set of recent tasks */
- public SpaceNode reload(Context context, int preloadCount, boolean isTopTaskHome) {
- ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
- ArrayList<Task> tasksToLoad = new ArrayList<Task>();
- TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(),
- -1, preloadCount, RecentsTaskLoader.ALL_TASKS, true, isTopTaskHome, taskKeys,
- tasksToLoad);
- SpaceNode root = new SpaceNode();
- root.setStack(stack);
-
- // Start the task loader and add all the tasks we need to load
- mLoadQueue.addTasks(tasksToLoad);
- mLoader.start(context);
-
- return root;
+ /** Returns the size of the app icon cache. */
+ public int getApplicationIconCacheSize() {
+ return mMaxIconCacheSize;
}
- /** Preloads the set of recent tasks (not including thumbnails). */
- public void preload(Context context, int numTasksToPreload) {
- ArrayList<Task> tasksToLoad = new ArrayList<Task>();
- getTaskStack(mSystemServicesProxy, context.getResources(),
- -1, -1, numTasksToPreload, true, true, null, tasksToLoad);
-
- // Start the task loader and add all the tasks we need to load
- mLoadQueue.addTasks(tasksToLoad);
- mLoader.start(context);
- }
-
- /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
- public synchronized TaskStack getTaskStack(SystemServicesProxy ssp, Resources res,
- int preloadTaskId, int preloadTaskCount, int loadTaskCount,
- boolean loadTaskThumbnails, boolean isTopTaskHome,
- List<Task.TaskKey> taskKeysOut, List<Task> tasksToLoadOut) {
+ public RecentsTaskLoadPlan createLoadPlan(Context context) {
RecentsConfiguration config = RecentsConfiguration.getInstance();
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp,
- (loadTaskCount == ALL_TASKS ? config.maxNumTasksToLoad : loadTaskCount),
- isTopTaskHome);
- HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
- new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
- ArrayList<Task> tasksToAdd = new ArrayList<Task>();
- TaskStack stack = new TaskStack();
+ RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context, config, mSystemServicesProxy);
+ return plan;
+ }
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = tasks.get(i);
+ public void preloadTasks(RecentsTaskLoadPlan plan, boolean isTopTaskHome) {
+ plan.preloadPlan(this, isTopTaskHome);
+ }
- // Compose the task key
- Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
- t.firstActiveTime, t.lastActiveTime);
-
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hasCachedActivityInfo = false;
- if (activityInfoCache.containsKey(cnKey)) {
- infoHandle = activityInfoCache.get(cnKey);
- hasCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
- }
-
- // Determine whether to preload this task
- boolean preloadTask = false;
- if (preloadTaskId > 0) {
- preloadTask = (t.id == preloadTaskId);
- } else if (preloadTaskCount > 0) {
- preloadTask = (i >= (taskCount - preloadTaskCount));
- }
-
- // Load the label, icon, and color
- String activityLabel = getAndUpdateActivityLabel(taskKey, t.taskDescription,
- ssp, infoHandle);
- Drawable activityIcon = getAndUpdateActivityIcon(taskKey, t.taskDescription,
- ssp, res, infoHandle, preloadTask);
- int activityColor = getActivityPrimaryColor(t.taskDescription, config);
-
- // Update the activity info cache
- if (!hasCachedActivityInfo && infoHandle.info != null) {
- activityInfoCache.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 > -1), t.affiliatedTaskId, t.affiliatedTaskColor,
- activityLabel, activityIcon, activityColor, (i == (taskCount - 1)),
- config.lockToAppEnabled, icon, iconFilename);
-
- if (preloadTask && loadTaskThumbnails) {
- // Load the thumbnail from the cache if possible
- task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (task.thumbnail == null) {
- // Load the thumbnail from the system
- task.thumbnail = ssp.getTaskThumbnail(taskKey.id);
- if (task.thumbnail != null) {
- task.thumbnail.setHasAlpha(false);
- mThumbnailCache.put(taskKey, task.thumbnail);
- }
- }
- if (task.thumbnail == null && tasksToLoadOut != null) {
- // Either the task has changed since the last active time, or it was not
- // previously cached, so try and load the task anew.
- tasksToLoadOut.add(task);
- }
- }
-
- // Add to the list of task keys
- if (taskKeysOut != null) {
- taskKeysOut.add(taskKey);
- }
- // Add the task to the stack
- tasksToAdd.add(task);
+ public void loadTasks(Context context, RecentsTaskLoadPlan plan,
+ RecentsTaskLoadPlan.Options opts) {
+ if (opts == null) {
+ throw new RuntimeException("Requires load options");
}
- stack.setTasks(tasksToAdd);
- stack.createAffiliatedGroupings(config);
- return stack;
+ plan.executePlan(opts, this);
+ if (opts.numVisibleTasks > 0) {
+ mNumVisibleTasksLoaded = opts.numVisibleTasks;
+ }
+
+ // Start the loader
+ mLoader.start(context);
}
/** Acquires the task resource data directly from the pool. */
@@ -591,24 +499,22 @@
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
// Stop the loader immediately when the UI is no longer visible
stopLoader();
- mThumbnailCache.trimToSize(Math.max(
- Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
+ mThumbnailCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
mMaxThumbnailCacheSize / 2));
- mApplicationIconCache.trimToSize(Math.max(
- Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
+ mApplicationIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
mMaxIconCacheSize / 2));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
// We are leaving recents, so trim the data a bit
- mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
- mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
+ mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
+ mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
// We are going to be low on memory
- mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
- mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
+ mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
+ mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6093584..77a050a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -216,6 +216,9 @@
/** Requests all task stacks to start their exit-recents animation */
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // We have to increment/decrement the post animation trigger in case there are no children
+ // to ensure that it runs
+ ctx.postAnimationTrigger.increment();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
@@ -224,6 +227,7 @@
stackView.startExitToHomeAnimation(ctx);
}
}
+ ctx.postAnimationTrigger.decrement();
// Notify of the exit animation
mCb.onExitToHomeAnimationTriggered();
@@ -503,7 +507,7 @@
// Launch the app right away if there is no task view, otherwise, animate the icon out first
if (tv == null) {
- post(launchRunnable);
+ launchRunnable.run();
} else {
if (!task.group.isFrontMostTask(task)) {
// For affiliated tasks that are behind other tasks, we must animate the front cards
@@ -512,7 +516,7 @@
} else {
// Otherwise, we can start the task transition immediately
stackView.startLaunchTaskAnimation(tv, null, lockToTask);
- postDelayed(launchRunnable, 17);
+ launchRunnable.run();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index bef4cd1..c3077b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -624,6 +624,14 @@
}
/**
+ * Computes the maximum number of visible tasks and thumbnails. Requires that
+ * updateMinMaxScrollForStack() is called first.
+ */
+ public TaskStackViewLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
+ return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
+ }
+
+ /**
* This is called with the full window width and height to allow stack view children to
* perform the full screen transition down.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index c549d2b..5767e18 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -35,6 +35,18 @@
// These are all going to change
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
+ // A report of the visibility state of the stack
+ public class VisibilityReport {
+ public int numVisibleTasks;
+ public int numVisibleThumbnails;
+
+ /** Package level ctor */
+ VisibilityReport(int tasks, int thumbnails) {
+ numVisibleTasks = tasks;
+ numVisibleThumbnails = thumbnails;
+ }
+ }
+
RecentsConfiguration mConfig;
// The various rects that define the stack view
@@ -117,7 +129,8 @@
float pTaskHeightOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
float pNavBarOffset = pAtBottomOfStackRect -
- screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
+ screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom -
+ mStackRect.bottom));
// Update the task offsets
float pAtBackMostCardTop = 0.5f;
@@ -130,8 +143,8 @@
if (i < (taskCount - 1)) {
// Increment the peek height
- float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset :
- pWithinAffiliateOffset;
+ float pPeek = task.group.isFrontMostTask(task) ?
+ pBetweenAffiliateOffset : pWithinAffiliateOffset;
pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
pAtFrontMostCardTop += pPeek;
}
@@ -153,19 +166,72 @@
mInitialScrollP = Math.max(0, mInitialScrollP);
}
+ /**
+ * Computes the maximum number of visible tasks and thumbnails. Requires that
+ * computeMinMaxScroll() is called first.
+ */
+ public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+ if (tasks.size() <= 1) {
+ return new VisibilityReport(1, 1);
+ }
+
+ // Walk backwards in the task stack and count the number of tasks and visible thumbnails
+ int taskHeight = mTaskRect.height();
+ int numVisibleTasks = 1;
+ int numVisibleThumbnails = 1;
+ float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
+ int prevScreenY = curveProgressToScreenY(progress);
+ for (int i = tasks.size() - 2; i >= 0; i--) {
+ Task task = tasks.get(i);
+ progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
+ if (progress < 0) {
+ break;
+ }
+ boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task);
+ if (isFrontMostTaskInGroup) {
+ float scaleAtP = curveProgressToScale(progress);
+ int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
+ int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
+ boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
+ if (hasVisibleThumbnail) {
+ numVisibleThumbnails++;
+ numVisibleTasks++;
+ prevScreenY = screenY;
+ } else {
+ // Once we hit the next front most task that does not have a visible thumbnail,
+ // walk through remaining visible set
+ for (int j = i; j >= 0; j--) {
+ numVisibleTasks++;
+ progress = mTaskProgressMap.get(tasks.get(j).key) - mInitialScrollP;
+ if (progress < 0) {
+ break;
+ }
+ }
+ break;
+ }
+ } else if (!isFrontMostTaskInGroup) {
+ // Affiliated task, no thumbnail
+ numVisibleTasks++;
+ }
+ }
+ return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
+ }
+
/** Update/get the transform */
- public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut,
- TaskViewTransform prevTransform) {
+ public TaskViewTransform getStackTransform(Task task, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform prevTransform) {
// Return early if we have an invalid index
if (task == null || !mTaskProgressMap.containsKey(task.key)) {
transformOut.reset();
return transformOut;
}
- return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut, prevTransform);
+ return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
+ prevTransform);
}
/** Update/get the transform */
- public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform prevTransform) {
float pTaskRelative = taskProgress - stackScroll;
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
// If the task top is outside of the bounds below the screen, then immediately reset it