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)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.android.systemui.R;
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
|
||||
|
||||
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
|
||||
static RecentsTaskLoadPlan sInstanceLoadPlan;
|
||||
|
||||
Context mContext;
|
||||
LayoutInflater mInflater;
|
||||
@@ -134,8 +138,15 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
}
|
||||
|
||||
// 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 class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
* 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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
|
||||
/** 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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
|
||||
// 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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
|
||||
/** 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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
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 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preloaded load plan and invalidates it.
|
||||
*/
|
||||
public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
|
||||
RecentsTaskLoadPlan plan = sInstanceLoadPlan;
|
||||
sInstanceLoadPlan = null;
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**** OnAnimationStartedListener Implementation ****/
|
||||
|
||||
@Override
|
||||
|
||||
@@ -64,11 +64,6 @@ public class Constants {
|
||||
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;
|
||||
|
||||
@@ -27,10 +27,7 @@ import android.content.Context;
|
||||
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.DebugTrigger;
|
||||
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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
// 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
|
||||
/** 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
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",
|
||||
|
||||
@@ -244,6 +244,7 @@ public class SystemServicesProxy {
|
||||
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
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.Constants;
|
||||
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 @@ class TaskResourceLoader implements Runnable {
|
||||
// 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 @@ public class RecentsTaskLoader {
|
||||
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 @@ public class RecentsTaskLoader {
|
||||
|
||||
int mMaxThumbnailCacheSize;
|
||||
int mMaxIconCacheSize;
|
||||
int mNumVisibleTasksLoaded;
|
||||
|
||||
BitmapDrawable mDefaultApplicationIcon;
|
||||
Bitmap mDefaultThumbnail;
|
||||
@@ -325,55 +319,6 @@ public class RecentsTaskLoader {
|
||||
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 @@ public class RecentsTaskLoader {
|
||||
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 @@ public class RecentsTaskLoader {
|
||||
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 @@ public class RecentsTaskLoader {
|
||||
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:
|
||||
|
||||
@@ -216,6 +216,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
|
||||
/** 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
stackView.startExitToHomeAnimation(ctx);
|
||||
}
|
||||
}
|
||||
ctx.postAnimationTrigger.decrement();
|
||||
|
||||
// Notify of the exit animation
|
||||
mCb.onExitToHomeAnimationTriggered();
|
||||
@@ -503,7 +507,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
|
||||
// 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
} else {
|
||||
// Otherwise, we can start the task transition immediately
|
||||
stackView.startLaunchTaskAnimation(tv, null, lockToTask);
|
||||
postDelayed(launchRunnable, 17);
|
||||
launchRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,6 +623,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
updateMinMaxScroll(false, launchedWithAltTab, launchedFromHome);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -35,6 +35,18 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
// 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 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
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 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
|
||||
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 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user