Merge "Preload only visible thumbnails and task icons. (Bug 17672056, Bug 18291345)" into lmp-mr1-dev
This commit is contained in:
@@ -776,7 +776,7 @@
|
|||||||
This needs to match the constants in
|
This needs to match the constants in
|
||||||
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
|
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.
|
<!-- Control the behavior when the user double-taps the home button.
|
||||||
0 - Nothing
|
0 - Nothing
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.RecentsComponent;
|
import com.android.systemui.RecentsComponent;
|
||||||
import com.android.systemui.recents.misc.Console;
|
import com.android.systemui.recents.misc.Console;
|
||||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
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.RecentsTaskLoader;
|
||||||
import com.android.systemui.recents.model.Task;
|
import com.android.systemui.recents.model.Task;
|
||||||
import com.android.systemui.recents.model.TaskGrouping;
|
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_ALT_TAB = "recents.triggeredFromAltTab";
|
||||||
final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
|
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_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_START_ENTER_ANIMATION = "action_start_enter_animation";
|
||||||
final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
|
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";
|
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
|
||||||
|
|
||||||
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
|
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
|
||||||
|
static RecentsTaskLoadPlan sInstanceLoadPlan;
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
LayoutInflater mInflater;
|
LayoutInflater mInflater;
|
||||||
@@ -134,8 +138,15 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we start, preload the metadata associated with the previous tasks
|
// When we start, preload the metadata and icons associated with the recent tasks.
|
||||||
RecentsTaskLoader.getInstance().preload(mContext, RecentsTaskLoader.ALL_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() {
|
public void onBootCompleted() {
|
||||||
@@ -183,9 +194,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onPreloadRecents() {
|
public void onPreloadRecents() {
|
||||||
// When we start, preload the metadata associated with the previous tasks
|
// Preload only the raw task list into a new load plan (which will be consumed by the
|
||||||
RecentsTaskLoader.getInstance().preload(mContext,
|
// RecentsActivity)
|
||||||
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
|
sInstanceLoadPlan = loader.createLoadPlan(mContext);
|
||||||
|
sInstanceLoadPlan.preloadRawTasks(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCancelPreloadingRecents() {
|
public void onCancelPreloadingRecents() {
|
||||||
@@ -194,8 +207,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
|
|
||||||
void showRelativeAffiliatedTask(boolean showNextTask) {
|
void showRelativeAffiliatedTask(boolean showNextTask) {
|
||||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
|
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
|
||||||
-1, -1, RecentsTaskLoader.ALL_TASKS, false, true, null, null);
|
loader.preloadTasks(plan, true /* isTopTaskHome */);
|
||||||
|
TaskStack stack = plan.getTaskStack();
|
||||||
|
|
||||||
// Return early if there are no tasks
|
// Return early if there are no tasks
|
||||||
if (stack.getTaskCount() == 0) return;
|
if (stack.getTaskCount() == 0) return;
|
||||||
|
|
||||||
@@ -411,11 +426,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
* Creates the activity options for an app->recents transition.
|
* Creates the activity options for an app->recents transition.
|
||||||
*/
|
*/
|
||||||
ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
|
ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
|
||||||
boolean isTopTaskHome) {
|
TaskStack stack, TaskStackView stackView) {
|
||||||
// Update the destination rect
|
// Update the destination rect
|
||||||
Task toTask = new Task();
|
Task toTask = new Task();
|
||||||
TaskViewTransform toTransform = getThumbnailTransitionTransform(topTask.id, isTopTaskHome,
|
TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
|
||||||
toTask);
|
topTask.id, toTask);
|
||||||
if (toTransform != null && toTask.key != null) {
|
if (toTransform != null && toTask.key != null) {
|
||||||
Rect toTaskRect = toTransform.rect;
|
Rect toTaskRect = toTransform.rect;
|
||||||
int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
|
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. */
|
/** Returns the transition rect for the given task id. */
|
||||||
TaskViewTransform getThumbnailTransitionTransform(int runningTaskId, boolean isTopTaskHome,
|
TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
|
||||||
Task runningTaskOut) {
|
int runningTaskId, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the running task in the TaskStack
|
// Find the running task in the TaskStack
|
||||||
Task task = null;
|
Task task = null;
|
||||||
ArrayList<Task> tasks = stack.getTasks();
|
ArrayList<Task> tasks = stack.getTasks();
|
||||||
@@ -474,30 +481,42 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the transform for the running task
|
// Get the transform for the running task
|
||||||
mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
|
stackView.getScroller().setStackScrollToInitialState();
|
||||||
mDummyStackView.getScroller().setStackScrollToInitialState();
|
mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
|
||||||
mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task,
|
stackView.getScroller().getStackScroll(), mTmpTransform, null);
|
||||||
mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null);
|
|
||||||
return mTmpTransform;
|
return mTmpTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Starts the recents activity */
|
/** Starts the recents activity */
|
||||||
void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
|
void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
|
||||||
// If Recents is not the front-most activity and we should animate into it. If
|
if (sInstanceLoadPlan == null) {
|
||||||
// the activity at the root of the top task stack in the home stack, then we just do a
|
// Create a new load plan if onPreloadRecents() was never triggered
|
||||||
// simple transition. Otherwise, we animate to the rects defined by the Recents service,
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
// which can differ depending on the number of items in the list.
|
sInstanceLoadPlan = loader.createLoadPlan(mContext);
|
||||||
SystemServicesProxy ssp = mSystemServicesProxy;
|
}
|
||||||
List<ActivityManager.RecentTaskInfo> recentTasks =
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
|
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
|
||||||
boolean useThumbnailTransition = !isTopTaskHome;
|
TaskStack stack = sInstanceLoadPlan.getTaskStack();
|
||||||
boolean hasRecentTasks = !recentTasks.isEmpty();
|
|
||||||
|
// 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) {
|
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
|
// Try starting with a thumbnail transition
|
||||||
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, isTopTaskHome);
|
ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
|
||||||
|
mDummyStackView);
|
||||||
if (opts != null) {
|
if (opts != null) {
|
||||||
startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
|
startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL, stackVr);
|
||||||
} else {
|
} else {
|
||||||
// Fall through below to the non-thumbnail transition
|
// Fall through below to the non-thumbnail transition
|
||||||
useThumbnailTransition = false;
|
useThumbnailTransition = false;
|
||||||
@@ -531,11 +550,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
|
|
||||||
ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
|
ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
|
||||||
startAlternateRecentsActivity(topTask, opts,
|
startAlternateRecentsActivity(topTask, opts,
|
||||||
fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME);
|
fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME, stackVr);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise we do the normal fade from an unknown source
|
// Otherwise we do the normal fade from an unknown source
|
||||||
ActivityOptions opts = getUnknownTransitionActivityOptions();
|
ActivityOptions opts = getUnknownTransitionActivityOptions();
|
||||||
startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
|
startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mLastToggleTime = System.currentTimeMillis();
|
mLastToggleTime = System.currentTimeMillis();
|
||||||
@@ -543,7 +562,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
|||||||
|
|
||||||
/** Starts the recents activity */
|
/** Starts the recents activity */
|
||||||
void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
|
void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
|
||||||
ActivityOptions opts, String extraFlag) {
|
ActivityOptions opts, String extraFlag,
|
||||||
|
TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
|
||||||
Intent intent = new Intent(sToggleRecentsAction);
|
Intent intent = new Intent(sToggleRecentsAction);
|
||||||
intent.setClassName(sRecentsPackage, sRecentsActivity);
|
intent.setClassName(sRecentsPackage, sRecentsActivity);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
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_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
|
||||||
intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
|
intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
|
||||||
intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews);
|
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) {
|
if (opts != null) {
|
||||||
mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
|
mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
|
||||||
} else {
|
} 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 ****/
|
/**** OnAnimationStartedListener Implementation ****/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -64,11 +64,6 @@ public class Constants {
|
|||||||
public static String DebugModeVersion = "A";
|
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 class TaskStackView {
|
||||||
public static final int TaskStackOverscrollRange = 150;
|
public static final int TaskStackOverscrollRange = 150;
|
||||||
public static final int FilterStartDelay = 25;
|
public static final int FilterStartDelay = 25;
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.KeyEvent;
|
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.ReferenceCountedTrigger;
|
||||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||||
import com.android.systemui.recents.misc.Utilities;
|
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.RecentsTaskLoader;
|
||||||
import com.android.systemui.recents.model.SpaceNode;
|
import com.android.systemui.recents.model.SpaceNode;
|
||||||
import com.android.systemui.recents.model.Task;
|
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)) {
|
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
|
||||||
// When the screen turns off, dismiss Recents to Home
|
// When the screen turns off, dismiss Recents to Home
|
||||||
dismissRecentsToHome(false);
|
dismissRecentsToHome(false);
|
||||||
// Start preloading some tasks in the background
|
// Preload the metadata for all tasks in the background
|
||||||
RecentsTaskLoader.getInstance().preload(RecentsActivity.this,
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
|
RecentsTaskLoadPlan plan = loader.createLoadPlan(context);
|
||||||
|
loader.preloadTasks(plan, true /* isTopTaskHome */);
|
||||||
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
|
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
|
||||||
// When the search activity changes, update the Search widget
|
// When the search activity changes, update the Search widget
|
||||||
refreshSearchWidget();
|
refreshSearchWidget();
|
||||||
@@ -193,6 +192,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
|||||||
// Update the configuration based on the launch intent
|
// Update the configuration based on the launch intent
|
||||||
boolean fromSearchHome = launchIntent.getBooleanExtra(
|
boolean fromSearchHome = launchIntent.getBooleanExtra(
|
||||||
AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false);
|
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(
|
mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra(
|
||||||
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
|
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
|
||||||
mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
|
mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
|
||||||
@@ -204,16 +207,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
|||||||
mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra(
|
mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra(
|
||||||
AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false);
|
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();
|
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||||
SpaceNode root = loader.reload(this,
|
RecentsTaskLoadPlan plan = AlternateRecentsComponent.consumeInstanceLoadPlan();
|
||||||
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
|
if (plan == null) {
|
||||||
mConfig.launchedFromHome);
|
plan = loader.createLoadPlan(this);
|
||||||
ArrayList<TaskStack> stacks = root.getStacks();
|
loader.preloadTasks(plan, mConfig.launchedFromHome);
|
||||||
if (!stacks.isEmpty()) {
|
|
||||||
mRecentsView.setTaskStacks(root.getStacks());
|
|
||||||
}
|
}
|
||||||
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
|
// Create the home intent runnable
|
||||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
|
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. */
|
/** Inflates the debug overlay if debug mode is enabled. */
|
||||||
void inflateDebugOverlay() {
|
void inflateDebugOverlay() {
|
||||||
|
if (!Constants.DebugFlags.App.EnableDebugMode) return;
|
||||||
|
|
||||||
if (mConfig.debugModeEnabled && mDebugOverlay == null) {
|
if (mConfig.debugModeEnabled && mDebugOverlay == null) {
|
||||||
// Inflate the overlay and seek bars
|
// Inflate the overlay and seek bars
|
||||||
mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
|
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();
|
settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
|
||||||
mConfig.debugModeEnabled = false;
|
mConfig.debugModeEnabled = false;
|
||||||
inflateDebugOverlay();
|
inflateDebugOverlay();
|
||||||
mDebugOverlay.disable();
|
if (mDebugOverlay != null) {
|
||||||
|
mDebugOverlay.disable();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Enable the debug mode
|
// Enable the debug mode
|
||||||
settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
|
settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
|
||||||
mConfig.debugModeEnabled = true;
|
mConfig.debugModeEnabled = true;
|
||||||
inflateDebugOverlay();
|
inflateDebugOverlay();
|
||||||
mDebugOverlay.enable();
|
if (mDebugOverlay != null) {
|
||||||
|
mDebugOverlay.enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
|
Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
|
||||||
(mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
|
(mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ public class SystemServicesProxy {
|
|||||||
|
|
||||||
Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
|
Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
|
||||||
if (thumbnail != null) {
|
if (thumbnail != null) {
|
||||||
|
thumbnail.setHasAlpha(false);
|
||||||
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
|
// 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
|
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
|
||||||
// screenshots are always composed onto a bitmap that has no alpha.
|
// 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.graphics.drawable.Drawable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
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.RecentsConfiguration;
|
||||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
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
|
// Load the thumbnail if it is stale or we haven't cached one yet
|
||||||
if (cachedThumbnail == null) {
|
if (cachedThumbnail == null) {
|
||||||
cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
|
cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
|
||||||
if (cachedThumbnail != null) {
|
if (cachedThumbnail == null) {
|
||||||
cachedThumbnail.setHasAlpha(false);
|
|
||||||
} else {
|
|
||||||
cachedThumbnail = mDefaultThumbnail;
|
cachedThumbnail = mDefaultThumbnail;
|
||||||
}
|
}
|
||||||
mThumbnailCache.put(t.key, cachedThumbnail);
|
mThumbnailCache.put(t.key, cachedThumbnail);
|
||||||
@@ -260,7 +253,7 @@ public class RecentsTaskLoader {
|
|||||||
private static final String TAG = "RecentsTaskLoader";
|
private static final String TAG = "RecentsTaskLoader";
|
||||||
|
|
||||||
static RecentsTaskLoader sInstance;
|
static RecentsTaskLoader sInstance;
|
||||||
public static final int ALL_TASKS = -1;
|
static int INVALID_TASK_ID = -1;
|
||||||
|
|
||||||
SystemServicesProxy mSystemServicesProxy;
|
SystemServicesProxy mSystemServicesProxy;
|
||||||
DrawableLruCache mApplicationIconCache;
|
DrawableLruCache mApplicationIconCache;
|
||||||
@@ -273,6 +266,7 @@ public class RecentsTaskLoader {
|
|||||||
|
|
||||||
int mMaxThumbnailCacheSize;
|
int mMaxThumbnailCacheSize;
|
||||||
int mMaxIconCacheSize;
|
int mMaxIconCacheSize;
|
||||||
|
int mNumVisibleTasksLoaded;
|
||||||
|
|
||||||
BitmapDrawable mDefaultApplicationIcon;
|
BitmapDrawable mDefaultApplicationIcon;
|
||||||
Bitmap mDefaultThumbnail;
|
Bitmap mDefaultThumbnail;
|
||||||
@@ -325,55 +319,6 @@ public class RecentsTaskLoader {
|
|||||||
return mSystemServicesProxy;
|
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. */
|
/** Returns the activity label using as many cached values as we can. */
|
||||||
public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
|
public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
|
||||||
ActivityManager.TaskDescription td, SystemServicesProxy ssp,
|
ActivityManager.TaskDescription td, SystemServicesProxy ssp,
|
||||||
@@ -402,6 +347,63 @@ public class RecentsTaskLoader {
|
|||||||
return label;
|
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. */
|
/** Returns the activity's primary color. */
|
||||||
public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
|
public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
|
||||||
RecentsConfiguration config) {
|
RecentsConfiguration config) {
|
||||||
@@ -411,127 +413,33 @@ public class RecentsTaskLoader {
|
|||||||
return config.taskBarViewDefaultBackgroundColor;
|
return config.taskBarViewDefaultBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reload the set of recent tasks */
|
/** Returns the size of the app icon cache. */
|
||||||
public SpaceNode reload(Context context, int preloadCount, boolean isTopTaskHome) {
|
public int getApplicationIconCacheSize() {
|
||||||
ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
|
return mMaxIconCacheSize;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Preloads the set of recent tasks (not including thumbnails). */
|
public RecentsTaskLoadPlan createLoadPlan(Context context) {
|
||||||
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) {
|
|
||||||
RecentsConfiguration config = RecentsConfiguration.getInstance();
|
RecentsConfiguration config = RecentsConfiguration.getInstance();
|
||||||
List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp,
|
RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context, config, mSystemServicesProxy);
|
||||||
(loadTaskCount == ALL_TASKS ? config.maxNumTasksToLoad : loadTaskCount),
|
return plan;
|
||||||
isTopTaskHome);
|
}
|
||||||
HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
|
|
||||||
new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
|
|
||||||
ArrayList<Task> tasksToAdd = new ArrayList<Task>();
|
|
||||||
TaskStack stack = new TaskStack();
|
|
||||||
|
|
||||||
int taskCount = tasks.size();
|
public void preloadTasks(RecentsTaskLoadPlan plan, boolean isTopTaskHome) {
|
||||||
for (int i = 0; i < taskCount; i++) {
|
plan.preloadPlan(this, isTopTaskHome);
|
||||||
ActivityManager.RecentTaskInfo t = tasks.get(i);
|
}
|
||||||
|
|
||||||
// Compose the task key
|
public void loadTasks(Context context, RecentsTaskLoadPlan plan,
|
||||||
Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
|
RecentsTaskLoadPlan.Options opts) {
|
||||||
t.firstActiveTime, t.lastActiveTime);
|
if (opts == null) {
|
||||||
|
throw new RuntimeException("Requires load options");
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
stack.setTasks(tasksToAdd);
|
plan.executePlan(opts, this);
|
||||||
stack.createAffiliatedGroupings(config);
|
if (opts.numVisibleTasks > 0) {
|
||||||
return stack;
|
mNumVisibleTasksLoaded = opts.numVisibleTasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the loader
|
||||||
|
mLoader.start(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Acquires the task resource data directly from the pool. */
|
/** Acquires the task resource data directly from the pool. */
|
||||||
@@ -591,24 +499,22 @@ public class RecentsTaskLoader {
|
|||||||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
||||||
// Stop the loader immediately when the UI is no longer visible
|
// Stop the loader immediately when the UI is no longer visible
|
||||||
stopLoader();
|
stopLoader();
|
||||||
mThumbnailCache.trimToSize(Math.max(
|
mThumbnailCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
|
||||||
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
|
|
||||||
mMaxThumbnailCacheSize / 2));
|
mMaxThumbnailCacheSize / 2));
|
||||||
mApplicationIconCache.trimToSize(Math.max(
|
mApplicationIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
|
||||||
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
|
|
||||||
mMaxIconCacheSize / 2));
|
mMaxIconCacheSize / 2));
|
||||||
break;
|
break;
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
||||||
// We are leaving recents, so trim the data a bit
|
// We are leaving recents, so trim the data a bit
|
||||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
|
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
|
||||||
mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
|
mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
|
||||||
break;
|
break;
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
||||||
// We are going to be low on memory
|
// We are going to be low on memory
|
||||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
|
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
|
||||||
mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
|
mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
|
||||||
break;
|
break;
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
||||||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
|
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 */
|
/** Requests all task stacks to start their exit-recents animation */
|
||||||
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
|
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();
|
int childCount = getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
View child = getChildAt(i);
|
View child = getChildAt(i);
|
||||||
@@ -224,6 +227,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
|||||||
stackView.startExitToHomeAnimation(ctx);
|
stackView.startExitToHomeAnimation(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx.postAnimationTrigger.decrement();
|
||||||
|
|
||||||
// Notify of the exit animation
|
// Notify of the exit animation
|
||||||
mCb.onExitToHomeAnimationTriggered();
|
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
|
// Launch the app right away if there is no task view, otherwise, animate the icon out first
|
||||||
if (tv == null) {
|
if (tv == null) {
|
||||||
post(launchRunnable);
|
launchRunnable.run();
|
||||||
} else {
|
} else {
|
||||||
if (!task.group.isFrontMostTask(task)) {
|
if (!task.group.isFrontMostTask(task)) {
|
||||||
// For affiliated tasks that are behind other tasks, we must animate the front cards
|
// 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 {
|
} else {
|
||||||
// Otherwise, we can start the task transition immediately
|
// Otherwise, we can start the task transition immediately
|
||||||
stackView.startLaunchTaskAnimation(tv, null, lockToTask);
|
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);
|
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
|
* This is called with the full window width and height to allow stack view children to
|
||||||
* perform the full screen transition down.
|
* perform the full screen transition down.
|
||||||
|
|||||||
@@ -35,6 +35,18 @@ public class TaskStackViewLayoutAlgorithm {
|
|||||||
// These are all going to change
|
// These are all going to change
|
||||||
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
|
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;
|
RecentsConfiguration mConfig;
|
||||||
|
|
||||||
// The various rects that define the stack view
|
// The various rects that define the stack view
|
||||||
@@ -117,7 +129,8 @@ public class TaskStackViewLayoutAlgorithm {
|
|||||||
float pTaskHeightOffset = pAtBottomOfStackRect -
|
float pTaskHeightOffset = pAtBottomOfStackRect -
|
||||||
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
|
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
|
||||||
float pNavBarOffset = pAtBottomOfStackRect -
|
float pNavBarOffset = pAtBottomOfStackRect -
|
||||||
screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
|
screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom -
|
||||||
|
mStackRect.bottom));
|
||||||
|
|
||||||
// Update the task offsets
|
// Update the task offsets
|
||||||
float pAtBackMostCardTop = 0.5f;
|
float pAtBackMostCardTop = 0.5f;
|
||||||
@@ -130,8 +143,8 @@ public class TaskStackViewLayoutAlgorithm {
|
|||||||
|
|
||||||
if (i < (taskCount - 1)) {
|
if (i < (taskCount - 1)) {
|
||||||
// Increment the peek height
|
// Increment the peek height
|
||||||
float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset :
|
float pPeek = task.group.isFrontMostTask(task) ?
|
||||||
pWithinAffiliateOffset;
|
pBetweenAffiliateOffset : pWithinAffiliateOffset;
|
||||||
pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
|
pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
|
||||||
pAtFrontMostCardTop += pPeek;
|
pAtFrontMostCardTop += pPeek;
|
||||||
}
|
}
|
||||||
@@ -153,19 +166,72 @@ public class TaskStackViewLayoutAlgorithm {
|
|||||||
mInitialScrollP = Math.max(0, mInitialScrollP);
|
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 */
|
/** Update/get the transform */
|
||||||
public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut,
|
public TaskViewTransform getStackTransform(Task task, float stackScroll,
|
||||||
TaskViewTransform prevTransform) {
|
TaskViewTransform transformOut, TaskViewTransform prevTransform) {
|
||||||
// Return early if we have an invalid index
|
// Return early if we have an invalid index
|
||||||
if (task == null || !mTaskProgressMap.containsKey(task.key)) {
|
if (task == null || !mTaskProgressMap.containsKey(task.key)) {
|
||||||
transformOut.reset();
|
transformOut.reset();
|
||||||
return transformOut;
|
return transformOut;
|
||||||
}
|
}
|
||||||
return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut, prevTransform);
|
return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
|
||||||
|
prevTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update/get the transform */
|
/** 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 pTaskRelative = taskProgress - stackScroll;
|
||||||
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
|
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
|
// If the task top is outside of the bounds below the screen, then immediately reset it
|
||||||
|
|||||||
Reference in New Issue
Block a user