From b0a28ea5d381cd3a8477cf7fd82797199c80ca67 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 28 Oct 2014 15:21:35 -0700 Subject: [PATCH] Don't reload the layout every time we enter Recents. (Bug 18160176) Ensure that we don't reinflate new TaskViews whenever we enter Recents by keeping the existing TaskStackViews active and returning the existing TaskViews back into the view pool when Recents is no longer visible. This CL also ensures that we are preloading the appropriate task metadata when the user first touches the nav bar, and also reverts the change to use onEnterAnimationComplete() in favor of the explicitly animation-started broadcast (to ensure we always get the signal) which fixes a regression with launching Recents in landscape. Change-Id: I169fea3cf07a6da3f90b32b005819773c6049ada --- .../recents/AlternateRecentsComponent.java | 74 ++++++++++-- .../systemui/recents/RecentsActivity.java | 40 +++--- .../recents/RecentsConfiguration.java | 3 +- .../systemui/recents/misc/DozeTrigger.java | 5 + .../recents/model/RecentsTaskLoader.java | 19 +-- .../systemui/recents/views/RecentsView.java | 76 ++++++++---- .../systemui/recents/views/TaskStackView.java | 114 +++++++++++++----- .../systemui/recents/views/TaskView.java | 14 +++ .../recents/views/TaskViewHeader.java | 5 + .../systemui/recents/views/ViewPool.java | 8 ++ 10 files changed, 269 insertions(+), 89 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 76e8181407409..be7d3229cb784 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -54,7 +54,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** A proxy implementation for the recents component */ -public class AlternateRecentsComponent { +public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener { final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome"; final public static String EXTRA_FROM_SEARCH_HOME = "recents.triggeredOverSearchHome"; @@ -62,7 +62,9 @@ public class AlternateRecentsComponent { final public static String EXTRA_FROM_TASK_ID = "recents.activeTaskId"; 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 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_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity"; @@ -77,7 +79,10 @@ public class AlternateRecentsComponent { Context mContext; LayoutInflater mInflater; SystemServicesProxy mSystemServicesProxy; + Handler mHandler; boolean mBootCompleted; + boolean mStartAnimationTriggered; + boolean mCanReuseTaskStackViews = true; // Task launching RecentsConfiguration mConfig; @@ -103,6 +108,7 @@ public class AlternateRecentsComponent { mInflater = LayoutInflater.from(context); mContext = context; mSystemServicesProxy = new SystemServicesProxy(context); + mHandler = new Handler(); mTaskStackBounds = new Rect(); } @@ -128,7 +134,7 @@ public class AlternateRecentsComponent { } // When we start, preload the metadata associated with the previous tasks - RecentsTaskLoader.getInstance().preload(mContext); + RecentsTaskLoader.getInstance().preload(mContext, RecentsTaskLoader.ALL_TASKS); } public void onBootCompleted() { @@ -176,7 +182,9 @@ public class AlternateRecentsComponent { } public void onPreloadRecents() { - // Do nothing + // When we start, preload the metadata associated with the previous tasks + RecentsTaskLoader.getInstance().preload(mContext, + Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount); } public void onCancelPreloadingRecents() { @@ -186,7 +194,7 @@ public class AlternateRecentsComponent { void showRelativeAffiliatedTask(boolean showNextTask) { RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(), - -1, -1, false, true, null, null); + -1, -1, RecentsTaskLoader.ALL_TASKS, false, true, null, null); // Return early if there are no tasks if (stack.getTaskCount() == 0) return; @@ -251,6 +259,8 @@ public class AlternateRecentsComponent { } public void onConfigurationChanged(Configuration newConfig) { + // Don't reuse task stack views if the configuration changes + mCanReuseTaskStackViews = false; // Reload the header bar layout reloadHeaderBarLayout(); } @@ -364,23 +374,28 @@ public class AlternateRecentsComponent { * Creates the activity options for a unknown state->recents transition. */ ActivityOptions getUnknownTransitionActivityOptions() { + mStartAnimationTriggered = false; return ActivityOptions.makeCustomAnimation(mContext, R.anim.recents_from_unknown_enter, - R.anim.recents_from_unknown_exit); + R.anim.recents_from_unknown_exit, + mHandler, this); } /** * Creates the activity options for a home->recents transition. */ ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) { + mStartAnimationTriggered = false; if (fromSearchHome) { return ActivityOptions.makeCustomAnimation(mContext, R.anim.recents_from_search_launcher_enter, - R.anim.recents_from_search_launcher_exit); + R.anim.recents_from_search_launcher_exit, + mHandler, this); } return ActivityOptions.makeCustomAnimation(mContext, R.anim.recents_from_launcher_enter, - R.anim.recents_from_launcher_exit); + R.anim.recents_from_launcher_exit, + mHandler, this); } /** @@ -408,9 +423,10 @@ public class AlternateRecentsComponent { c.setBitmap(null); } + mStartAnimationTriggered = false; return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView, thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(), - toTaskRect.height(), null); + toTaskRect.height(), this); } // If both the screenshot and thumbnail fails, then just fall back to the default transition @@ -423,7 +439,7 @@ public class AlternateRecentsComponent { // Get the stack of tasks that we are animating into RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(), - runningTaskId, -1, false, isTopTaskHome, null, null); + runningTaskId, -1, RecentsTaskLoader.ALL_TASKS, false, isTopTaskHome, null, null); if (stack.getTaskCount() == 0) { return null; } @@ -529,11 +545,13 @@ public class AlternateRecentsComponent { } 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); if (opts != null) { mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT); } else { mContext.startActivityAsUser(intent, UserHandle.CURRENT); } + mCanReuseTaskStackViews = true; } /** Sets the RecentsComponent callbacks. */ @@ -547,4 +565,42 @@ public class AlternateRecentsComponent { sRecentsComponentCallbacks.onVisibilityChanged(visible); } } + + /**** OnAnimationStartedListener Implementation ****/ + + @Override + public void onAnimationStarted() { + // Notify recents to start the enter animation + if (!mStartAnimationTriggered) { + // There can be a race condition between the start animation callback and + // the start of the new activity (where we register the receiver that listens + // to this broadcast, so we add our own receiver and if that gets called, then + // we know the activity has not yet started and we can retry sending the broadcast. + BroadcastReceiver fallbackReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (getResultCode() == Activity.RESULT_OK) { + mStartAnimationTriggered = true; + return; + } + + // Schedule for the broadcast to be sent again after some time + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + onAnimationStarted(); + } + }, 25); + } + }; + + // Send the broadcast to notify Recents that the animation has started + Intent intent = new Intent(ACTION_START_ENTER_ANIMATION); + intent.setPackage(mContext.getPackageName()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | + Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, + fallbackReceiver, null, Activity.RESULT_CANCELED, null, null); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index d2c55f71ca467..f8d981fc2d97d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -27,6 +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; @@ -142,6 +143,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) { // If we are toggling Recents, then first unfilter any filtered stacks first dismissRecentsToFocusedTaskOrHome(true); + } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) { + // Trigger the enter animation + onEnterAnimationTriggered(); + // Notify the fallback receiver that we have successfully got the broadcast + // See AlternateRecentsComponent.onAnimationStarted() + setResultCode(Activity.RESULT_OK); } } }; @@ -157,7 +164,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // When the screen turns off, dismiss Recents to Home dismissRecentsToHome(false); // Start preloading some tasks in the background - RecentsTaskLoader.getInstance().preload(RecentsActivity.this); + RecentsTaskLoader.getInstance().preload(RecentsActivity.this, + Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount); } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { // When the search activity changes, update the Search widget refreshSearchWidget(); @@ -188,6 +196,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView AlternateRecentsComponent.EXTRA_FROM_TASK_ID, -1); mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); + mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra( + AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false); // Load all the tasks RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); @@ -397,8 +407,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Update if we are getting a configuration change if (savedInstanceState != null) { + // Update RecentsConfiguration + mConfig = RecentsConfiguration.reinitialize(this, + RecentsTaskLoader.getInstance().getSystemServicesProxy()); mConfig.updateOnConfigurationChange(); - onConfigurationChange(); + // Trigger the enter animation + onEnterAnimationTriggered(); } // Start listening for widget package changes if there is one bound, post it since we don't @@ -428,19 +442,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } - /** Called when the configuration changes. */ - void onConfigurationChange() { - // Update RecentsConfiguration - mConfig = RecentsConfiguration.reinitialize(this, - RecentsTaskLoader.getInstance().getSystemServicesProxy()); - - // Try and start the enter animation (or restart it on configuration changed) - ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); - mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t)); - // Animate the SystemUI scrim views - mScrimViews.startEnterRecentsAnimation(); - } - /** Handles changes to the activity visibility. */ void onRecentsActivityVisibilityChanged(boolean visible) { if (!visible) { @@ -474,6 +475,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView IntentFilter filter = new IntentFilter(); filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY); filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY); + filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION); registerReceiver(mServiceBroadcastReceiver, filter); // Register any broadcast receivers for the task loader @@ -492,8 +494,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView protected void onStop() { super.onStop(); - // Remove all the views - mRecentsView.removeAllTaskStacks(); + // Notify the views that we are no longer visible + mRecentsView.onRecentsHidden(); // Unregister the RecentsService receiver unregisterReceiver(mServiceBroadcastReceiver); @@ -515,8 +517,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } - @Override - public void onEnterAnimationComplete() { + public void onEnterAnimationTriggered() { // Try and start the enter animation (or restart it on configuration changed) ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t)); @@ -584,7 +585,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Called when debug mode is triggered */ public void onDebugModeTriggered() { - if (mConfig.developerOptionsEnabled) { SharedPreferences settings = getSharedPreferences(getPackageName(), 0); if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index bfea3f754f7d9..e0c76b1dea026 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -115,8 +115,8 @@ public class RecentsConfiguration { public boolean launchedWithAltTab; public boolean launchedWithNoRecentTasks; public boolean launchedFromAppWithThumbnail; - public boolean launchedFromAppWithScreenshot; public boolean launchedFromHome; + public boolean launchedReuseTaskStackViews; public int launchedToTaskId; /** Misc **/ @@ -308,6 +308,7 @@ public class RecentsConfiguration { launchedWithNoRecentTasks = false; launchedFromAppWithThumbnail = false; launchedFromHome = false; + launchedReuseTaskStackViews = false; launchedToTaskId = -1; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java index 4456066827ba0..735f79f4021d6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java @@ -82,4 +82,9 @@ public class DozeTrigger { public boolean hasTriggered() { return mHasTriggered; } + + /** Resets the doze trigger state. */ + public void resetTrigger() { + mHasTriggered = false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index b4f62d5ef4905..390507f12a562 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -260,6 +260,7 @@ public class RecentsTaskLoader { private static final String TAG = "RecentsTaskLoader"; static RecentsTaskLoader sInstance; + public static final int ALL_TASKS = -1; SystemServicesProxy mSystemServicesProxy; DrawableLruCache mApplicationIconCache; @@ -326,10 +327,9 @@ public class RecentsTaskLoader { /** Gets the list of recent tasks, ordered from back to front. */ private static List getRecentTasks(SystemServicesProxy ssp, - boolean isTopTaskHome) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); + int numTasksToLoad, boolean isTopTaskHome) { List tasks = - ssp.getRecentTasks(config.maxNumTasksToLoad, UserHandle.CURRENT.getIdentifier(), + ssp.getRecentTasks(numTasksToLoad, UserHandle.CURRENT.getIdentifier(), isTopTaskHome); Collections.reverse(tasks); return tasks; @@ -416,7 +416,8 @@ public class RecentsTaskLoader { ArrayList taskKeys = new ArrayList(); ArrayList tasksToLoad = new ArrayList(); TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(), - -1, preloadCount, true, isTopTaskHome, taskKeys, tasksToLoad); + -1, preloadCount, RecentsTaskLoader.ALL_TASKS, true, isTopTaskHome, taskKeys, + tasksToLoad); SpaceNode root = new SpaceNode(); root.setStack(stack); @@ -428,10 +429,10 @@ public class RecentsTaskLoader { } /** Preloads the set of recent tasks (not including thumbnails). */ - public void preload(Context context) { + public void preload(Context context, int numTasksToPreload) { ArrayList tasksToLoad = new ArrayList(); getTaskStack(mSystemServicesProxy, context.getResources(), - -1, -1, true, true, null, tasksToLoad); + -1, -1, numTasksToPreload, true, true, null, tasksToLoad); // Start the task loader and add all the tasks we need to load mLoadQueue.addTasks(tasksToLoad); @@ -440,11 +441,13 @@ public class RecentsTaskLoader { /** 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 preloadTaskId, int preloadTaskCount, int loadTaskCount, boolean loadTaskThumbnails, boolean isTopTaskHome, List taskKeysOut, List tasksToLoadOut) { RecentsConfiguration config = RecentsConfiguration.getInstance(); - List tasks = getRecentTasks(ssp, isTopTaskHome); + List tasks = getRecentTasks(ssp, + (loadTaskCount == ALL_TASKS ? config.maxNumTasksToLoad : loadTaskCount), + isTopTaskHome); HashMap activityInfoCache = new HashMap(); ArrayList tasksToAdd = new ArrayList(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 6b0d3066720e9..80dcb287f87fd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -96,42 +96,57 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** Set/get the bsp root node */ public void setTaskStacks(ArrayList stacks) { - // Remove all TaskStackViews (but leave the search bar) + int numStacks = stacks.size(); + + // Make a list of the stack view children only + ArrayList stackViews = new ArrayList(); int childCount = getChildCount(); - for (int i = childCount - 1; i >= 0; i--) { - View v = getChildAt(i); - if (v != mSearchBar) { - removeViewAt(i); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child != mSearchBar) { + stackViews.add((TaskStackView) child); } } - // Create and add all the stacks for this partition of space. + // Remove all/extra stack views + int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout + if (mConfig.launchedReuseTaskStackViews) { + numTaskStacksToKeep = Math.min(childCount, numStacks); + } + for (int i = stackViews.size() - 1; i >= numTaskStacksToKeep; i--) { + removeView(stackViews.get(i)); + stackViews.remove(i); + } + + // Update the stack views that we are keeping + for (int i = 0; i < numTaskStacksToKeep; i++) { + stackViews.get(i).setStack(stacks.get(i)); + } + + // Add remaining/recreate stack views mStacks = stacks; - int numStacks = mStacks.size(); - for (int i = 0; i < numStacks; i++) { - TaskStack stack = mStacks.get(i); + for (int i = stackViews.size(); i < numStacks; i++) { + TaskStack stack = stacks.get(i); TaskStackView stackView = new TaskStackView(getContext(), stack); stackView.setCallbacks(this); - // Enable debug mode drawing - if (mConfig.debugModeEnabled) { - stackView.setDebugOverlay(mDebugOverlay); - } addView(stackView); } + // Enable debug mode drawing on all the stacks if necessary + if (mConfig.debugModeEnabled) { + for (int i = childCount - 1; i >= 0; i--) { + View v = getChildAt(i); + if (v != mSearchBar) { + TaskStackView stackView = (TaskStackView) v; + stackView.setDebugOverlay(mDebugOverlay); + } + } + } + // Reset the launched state mAlreadyLaunchingTask = false; - } - - /** Removes all the task stack views from this recents view. */ - public void removeAllTaskStacks() { - int childCount = getChildCount(); - for (int i = childCount - 1; i >= 0; i--) { - View child = getChildAt(i); - if (child != mSearchBar) { - removeViewAt(i); - } - } + // Trigger a new layout + requestLayout(); } /** Launches the focused task from the first stack if possible */ @@ -531,6 +546,19 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV mCb.onAllTaskViewsDismissed(); } + /** Final callback after Recents is finally hidden. */ + public void onRecentsHidden() { + // Notify each task stack view + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child != mSearchBar) { + TaskStackView stackView = (TaskStackView) child; + stackView.onRecentsHidden(); + } + } + } + @Override public void onTaskStackFilterTriggered() { // Hide the search bar diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index dee26e6e45caa..9df0db611d08a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -40,6 +40,7 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; /* The visual representation of a task stack view */ @@ -99,25 +100,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } }; - // A convenience runnable to return all views to the pool - Runnable mReturnAllViewsToPoolRunnable = new Runnable() { - @Override - public void run() { - int childCount = getChildCount(); - for (int i = childCount - 1; i >= 0; i--) { - TaskView tv = (TaskView) getChildAt(i); - mViewPool.returnViewToPool(tv); - // Also hide the view since we don't need it anymore - tv.setVisibility(View.INVISIBLE); - } - } - }; - public TaskStackView(Context context, TaskStack stack) { super(context); + // Set the stack first + setStack(stack); mConfig = RecentsConfiguration.getInstance(); - mStack = stack; - mStack.setCallbacks(this); mViewPool = new ViewPool(context, this); mInflater = LayoutInflater.from(context); mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig); @@ -143,11 +130,62 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mCb = cb; } + /** Sets the task stack */ + void setStack(TaskStack stack) { + // Unset the old stack + if (mStack != null) { + mStack.setCallbacks(null); + + // Return all existing views to the pool + reset(); + // Layout again with the new stack + requestLayout(); + } + + // Set the new stack + mStack = stack; + if (mStack != null) { + mStack.setCallbacks(this); + } + } + /** Sets the debug overlay */ public void setDebugOverlay(DebugOverlayView overlay) { mDebugOverlay = overlay; } + /** Resets this TaskStackView for reuse. */ + void reset() { + // Return all the views to the pool + int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + TaskView tv = (TaskView) getChildAt(i); + mViewPool.returnViewToPool(tv); + } + + // Mark each task view for relayout + if (mViewPool != null) { + Iterator iter = mViewPool.poolViewIterator(); + if (iter != null) { + while (iter.hasNext()) { + TaskView tv = iter.next(); + tv.reset(); + } + } + } + + // Reset the stack state + resetFocusedTask(); + mStackViewsDirty = true; + mStackViewsClipDirty = true; + mAwaitingFirstLayout = true; + mPrevAccessibilityFocusedIndex = -1; + if (mUIDozeTrigger != null) { + mUIDozeTrigger.stopDozing(); + mUIDozeTrigger.resetTrigger(); + } + } + /** Requests that the views be synchronized with the model */ void requestSynchronizeStackViewsWithModel() { requestSynchronizeStackViewsWithModel(0); @@ -510,6 +548,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.dismissTask(); } + /** Resets the focused task. */ + void resetFocusedTask() { + if (mFocusedTaskIndex > -1) { + Task t = mStack.getTasks().get(mFocusedTaskIndex); + TaskView tv = getChildViewForTask(t); + tv.unsetFocusedTask(); + } + mFocusedTaskIndex = -1; + } + @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); @@ -543,6 +591,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void computeScroll() { + if (mStack == null) return; + mStackScroller.computeScroll(); // Synchronize the views synchronizeStackViewsWithModel(); @@ -758,9 +808,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = (TaskView) getChildAt(i); tv.startExitToHomeAnimation(ctx); } - - // Add a runnable to the post animation ref counter to clear all the views - ctx.postAnimationTrigger.addLastDecrementRunnable(mReturnAllViewsToPoolRunnable); } /** Animates a task view in this stack as it launches. */ @@ -780,6 +827,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + /** Final callback after Recents is finally hidden. */ + void onRecentsHidden() { + reset(); + setStack(null); + } + public boolean isTransformedTouchPointInView(float x, float y, View child) { return isTransformedTouchPointInView(x, y, child, null); } @@ -944,20 +997,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Reset the view properties tv.resetViewProperties(); + + // Reset the clip state of the task view + tv.setClipViewInStack(false); } @Override public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) { + // It is possible for a view to be returned to the view pool before it is laid out, + // which means that we will need to relayout the view when it is first used next. + boolean requiresRelayout = tv.getWidth() <= 0 && !isNewView; + // Rebind the task and request that this task's data be filled into the TaskView tv.onTaskBound(task); // Load the task data RecentsTaskLoader.getInstance().loadTaskData(task); - // Sanity check, the task view should always be clipping against the stack at this point, - // but just in case, re-enable it here - tv.setClipViewInStack(true); - // If the doze trigger has already fired, then update the state for this task view if (mUIDozeTrigger.hasTriggered()) { tv.setNoUserInteractionState(); @@ -985,13 +1041,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Add/attach the view to the hierarchy if (isNewView) { addView(tv, insertIndex); - - // Set the callbacks and listeners for this new view - tv.setTouchEnabled(true); - tv.setCallbacks(this); } else { attachViewToParent(tv, insertIndex, tv.getLayoutParams()); + if (requiresRelayout) { + tv.requestLayout(); + } } + + // Set the new state for this view, including the callbacks and view clipping + tv.setCallbacks(this); + tv.setTouchEnabled(true); + tv.setClipViewInStack(true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 7b4e10a74b3d5..790130ac6b354 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -112,6 +112,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mCb = cb; } + /** Resets this TaskView for reuse. */ + void reset() { + resetViewProperties(); + resetNoUserInteractionState(); + setClipViewInStack(false); + setCallbacks(null); + } + /** Gets the task */ Task getTask() { return mTask; @@ -191,6 +199,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Resets this view's properties */ void resetViewProperties() { setDim(0); + setLayerType(View.LAYER_TYPE_NONE, null); TaskViewTransform.reset(this); } @@ -448,6 +457,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mHeaderView.setNoUserInteractionState(); } + /** Resets the state tracking that the user has not interacted with the stack after a certain time. */ + void resetNoUserInteractionState() { + mHeaderView.resetNoUserInteractionState(); + } + /** Dismisses this task. */ void dismissTask() { // Animate out the view and call the callback diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index ba868f5bc90d0..5de84bde822fb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -263,6 +263,11 @@ public class TaskViewHeader extends FrameLayout { } } + /** Resets the state tracking that the user has not interacted with the stack after a certain time. */ + void resetNoUserInteractionState() { + mDismissButton.setVisibility(View.INVISIBLE); + } + @Override protected int[] onCreateDrawableState(int extraSpace) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java index af0094ef7e116..12b91affce7de 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java @@ -75,4 +75,12 @@ public class ViewPool { mViewCreator.prepareViewToLeavePool(v, prepareData, isNewView); return v; } + + /** Returns an iterator to the list of the views in the pool. */ + Iterator poolViewIterator() { + if (mPool != null) { + return mPool.iterator(); + } + return null; + } }