Ensure that loading and unloading of tasks happen symmetrically with lifecycle events. (Bug 18574950)

Since we were unloading tasks when the activity was stopped, but only reloading
tasks on the next onCreate()/onNewIntent(), there were cases where the system
would create the activity but then immediately stop and start it (ie. when the
screen is turned off while the activity has resumeWhilePausing=True set in its
manifest).  This CL ensures that we reload the tasks with the same state used
the last time the Recents activity was started.

The CL also cleans up some visibility state that was confusing and is no longer
necessary, fixes a small issue with the dismiss button flashing when toggling
Recents quickly, and fixes some cases where the initial stack scroll is set
incorrectly.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 4f0700e..f1bf66d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -61,15 +61,8 @@
 /** A proxy implementation for the recents component */
 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";
-    final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
-    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 EXTRA_NUM_VISIBLE_TASKS = "recents.numVisibleTasks";
-    final public static String EXTRA_NUM_VISIBLE_THUMBNAILS = "recents.numVisibleThumbnails";
 
     final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
     final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
@@ -550,7 +543,8 @@
             ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
                     mDummyStackView);
             if (opts != null) {
-                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL, stackVr);
+                startAlternateRecentsActivity(topTask, opts, false /* fromHome */,
+                        false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
             } else {
                 // Fall through below to the non-thumbnail transition
                 useThumbnailTransition = false;
@@ -583,12 +577,13 @@
                 }
 
                 ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
-                startAlternateRecentsActivity(topTask, opts,
-                        fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME, stackVr);
+                startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+                        false /* fromThumbnail */, stackVr);
             } else {
                 // Otherwise we do the normal fade from an unknown source
                 ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
+                startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+                        false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
             }
         }
         mLastToggleTime = SystemClock.elapsedRealtime();
@@ -596,21 +591,24 @@
 
     /** Starts the recents activity */
     void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
-            ActivityOptions opts, String extraFlag,
+            ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
             TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
+        // Update the configuration based on the launch options
+        mConfig.launchedFromHome = fromSearchHome || fromHome;
+        mConfig.launchedFromSearchHome = fromSearchHome;
+        mConfig.launchedFromAppWithThumbnail = fromThumbnail;
+        mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+        mConfig.launchedWithAltTab = mTriggeredFromAltTab;
+        mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+        mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
+        mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+        mConfig.launchedHasConfigurationChanged = false;
+
         Intent intent = new Intent(sToggleRecentsAction);
         intent.setClassName(sRecentsPackage, sRecentsActivity);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-        if (extraFlag != null) {
-            intent.putExtra(extraFlag, true);
-        }
-        intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
-        intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
-        intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews);
-        intent.putExtra(EXTRA_NUM_VISIBLE_TASKS, vr.numVisibleTasks);
-        intent.putExtra(EXTRA_NUM_VISIBLE_THUMBNAILS, vr.numVisibleThumbnails);
         if (opts != null) {
             mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index a37bc54..6baff96 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -108,8 +108,6 @@
 
         @Override
         public void run() {
-            // Mark Recents as no longer visible
-            onRecentsActivityVisibilityChanged(false);
             // Finish Recents
             if (mLaunchIntent != null) {
                 if (mLaunchOpts != null) {
@@ -133,8 +131,6 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) {
-                // Mark Recents as no longer visible
-                AlternateRecentsComponent.notifyVisibilityChanged(false);
                 if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
                     // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
                     dismissRecentsToFocusedTaskOrHome(false);
@@ -186,24 +182,6 @@
 
     /** Updates the set of recent tasks */
     void updateRecentsTasks(Intent launchIntent) {
-        // Update the configuration based on the launch intent
-        boolean fromSearchHome = launchIntent.getBooleanExtra(
-                AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false);
-        int numVisibleTasks = launchIntent.getIntExtra(
-                AlternateRecentsComponent.EXTRA_NUM_VISIBLE_TASKS, 0);
-        int numVisibleThumbnails = launchIntent.getIntExtra(
-                AlternateRecentsComponent.EXTRA_NUM_VISIBLE_THUMBNAILS, 0);
-        mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra(
-                AlternateRecentsComponent.EXTRA_FROM_HOME, false);
-        mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
-                AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
-        mConfig.launchedToTaskId = launchIntent.getIntExtra(
-                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);
-
         // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
         // reconstructing the task stack
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -218,8 +196,8 @@
         }
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
         loadOpts.runningTaskId = mConfig.launchedToTaskId;
-        loadOpts.numVisibleTasks = numVisibleTasks;
-        loadOpts.numVisibleTaskThumbnails = numVisibleThumbnails;
+        loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
         loader.loadTasks(this, plan, loadOpts);
 
         SpaceNode root = plan.getSpaceNode();
@@ -237,9 +215,9 @@
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
             ActivityOptions.makeCustomAnimation(this,
-                fromSearchHome ? R.anim.recents_to_search_launcher_enter :
+                mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
                         R.anim.recents_to_launcher_enter,
-                fromSearchHome ? R.anim.recents_to_search_launcher_exit :
+                    mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
                         R.anim.recents_to_launcher_exit));
 
         // Mark the task that is the launch target
@@ -403,12 +381,12 @@
         mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
         mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub);
         mScrimViews = new SystemBarScrimViews(this, mConfig);
+        mStatusBar = ((SystemUIApplication) getApplication())
+                .getComponent(PhoneStatusBar.class);
         inflateDebugOverlay();
 
         // Bind the search app widget when we first start up
         bindSearchBarAppWidget();
-        // Update the recent tasks
-        updateRecentsTasks(getIntent());
 
         // Register the broadcast receiver to handle messages when the screen is turned off
         IntentFilter filter = new IntentFilter();
@@ -424,17 +402,6 @@
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
-
-        // Update if we are getting a configuration change
-        if (savedInstanceState != null) {
-            // Update RecentsConfiguration
-            mConfig.updateOnConfigurationChange();
-            // Trigger the enter animation
-            onEnterAnimationTriggered();
-        }
-
-        mStatusBar = ((SystemUIApplication) getApplication())
-                .getComponent(PhoneStatusBar.class);
     }
 
     /** Inflates the debug overlay if debug mode is enabled. */
@@ -449,14 +416,6 @@
         }
     }
 
-    /** Handles changes to the activity visibility. */
-    void onRecentsActivityVisibilityChanged(boolean visible) {
-        if (!visible) {
-            AlternateRecentsComponent.notifyVisibilityChanged(visible);
-        }
-        mVisible = visible;
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
@@ -469,14 +428,13 @@
         if (mDebugOverlay != null) {
             mDebugOverlay.clear();
         }
-
-        // Update the recent tasks
-        updateRecentsTasks(intent);
     }
 
     @Override
     protected void onStart() {
         super.onStart();
+        mVisible = true;
+        AlternateRecentsComponent.notifyVisibilityChanged(true);
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
@@ -487,19 +445,16 @@
 
         // Register any broadcast receivers for the task loader
         RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView);
-    }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        // Mark Recents as visible
-        onRecentsActivityVisibilityChanged(true);
+        // Update the recent tasks
+        updateRecentsTasks(getIntent());
     }
 
     @Override
     protected void onStop() {
         super.onStop();
+        mVisible = false;
+        AlternateRecentsComponent.notifyVisibilityChanged(false);
 
         // Notify the views that we are no longer visible
         mRecentsView.onRecentsHidden();
@@ -641,8 +596,6 @@
 
     @Override
     public void onTaskViewClicked() {
-        // Mark recents as no longer visible
-        onRecentsActivityVisibilityChanged(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 2b33d14..52e7e7f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -124,8 +124,12 @@
     public boolean launchedWithNoRecentTasks;
     public boolean launchedFromAppWithThumbnail;
     public boolean launchedFromHome;
+    public boolean launchedFromSearchHome;
     public boolean launchedReuseTaskStackViews;
+    public boolean launchedHasConfigurationChanged;
     public int launchedToTaskId;
+    public int launchedNumVisibleTasks;
+    public int launchedNumVisibleThumbnails;
 
     /** Misc **/
     public boolean useHardwareLayers;
@@ -308,12 +312,10 @@
     /** Called when the configuration has changed, and we want to reset any configuration specific
      * members. */
     public void updateOnConfigurationChange() {
-        launchedWithAltTab = false;
-        launchedWithNoRecentTasks = false;
-        launchedFromAppWithThumbnail = false;
-        launchedFromHome = false;
+        // Reset this flag on configuration change to ensure that we recreate new task views
         launchedReuseTaskStackViews = false;
-        launchedToTaskId = -1;
+        // Set this flag to indicate that the configuration has changed since Recents last launched
+        launchedHasConfigurationChanged = true;
     }
 
     /** Returns whether the search bar app widget exists. */
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 33a36f6..169683f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -715,14 +715,20 @@
             mStartEnterAnimationContext = null;
         }
 
-        // When Alt-Tabbing, we scroll to and focus the previous task
+        // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
+        // enter animation).
         if (mConfig.launchedWithAltTab) {
-            if (mConfig.launchedFromHome) {
-                focusTask(Math.max(0, mStack.getTaskCount() - 1), false, true);
+            if (mConfig.launchedFromAppWithThumbnail) {
+                focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
+                        mConfig.launchedHasConfigurationChanged);
             } else {
-                focusTask(Math.max(0, mStack.getTaskCount() - 2), false, true);
+                focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
+                        mConfig.launchedHasConfigurationChanged);
             }
         }
+
+        // Start dozing
+        mUIDozeTrigger.startDozing();
     }
 
     /** Requests this task stacks to start it's enter-recents animation */
@@ -767,16 +773,27 @@
                 @Override
                 public void run() {
                     mStartEnterAnimationCompleted = true;
-                    // Start dozing
-                    mUIDozeTrigger.startDozing();
-                    // Focus the first view if accessibility is enabled
+                    // Poke the dozer to restart the trigger after the animation completes
+                    mUIDozeTrigger.poke();
+
                     RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
                     SystemServicesProxy ssp = loader.getSystemServicesProxy();
                     int childCount = getChildCount();
-                    if (childCount > 0 && ssp.isTouchExplorationEnabled()) {
-                        TaskView tv = ((TaskView) getChildAt(childCount - 1));
-                        tv.requestAccessibilityFocus();
-                        mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask());
+                    if (childCount > 0) {
+                        // Focus the first view if accessibility is enabled
+                        if (ssp.isTouchExplorationEnabled()) {
+                            TaskView tv = ((TaskView) getChildAt(childCount - 1));
+                            tv.requestAccessibilityFocus();
+                            mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask());
+                        }
+                    }
+
+                    // Start the focus animation when alt-tabbing
+                    if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged) {
+                        View tv = getChildAt(mFocusedTaskIndex);
+                        if (tv != null) {
+                            ((TaskView) tv).setFocusedTask(true);
+                        }
                     }
                 }
             });
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 26fbbf4..49b9129 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -135,7 +135,6 @@
         // Update the task offsets
         float pAtBackMostCardTop = 0.5f;
         float pAtFrontMostCardTop = pAtBackMostCardTop;
-        float pAtSecondFrontMostCardTop = pAtBackMostCardTop;
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
             Task task = tasks.get(i);
@@ -145,25 +144,19 @@
                 // Increment the peek height
                 float pPeek = task.group.isFrontMostTask(task) ?
                         pBetweenAffiliateOffset : pWithinAffiliateOffset;
-                pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
                 pAtFrontMostCardTop += pPeek;
             }
         }
 
         mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
         mMinScrollP = tasks.size() == 1 ? Math.max(mMaxScrollP, 0f) : 0f;
-        if (launchedWithAltTab) {
-            if (launchedFromHome) {
-                // Center the top most task, since that will be focused first
-                mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f;
-            } else {
-                // Center the second top most task, since that will be focused first
-                mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f;
-            }
+        if (launchedWithAltTab && launchedFromHome) {
+            // Center the top most task, since that will be focused first
+            mInitialScrollP = mMaxScrollP;
         } else {
             mInitialScrollP = pAtFrontMostCardTop - 0.825f;
         }
-        mInitialScrollP = Math.max(0, mInitialScrollP);
+        mInitialScrollP = Math.min(mMaxScrollP, Math.max(0, mInitialScrollP));
     }
 
     /**
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 de5974f..faa728d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -235,7 +235,9 @@
     void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
                                              boolean occludesLaunchTarget, int offscreenY) {
         int initialDim = getDim();
-        if (mConfig.launchedFromAppWithThumbnail) {
+        if (mConfig.launchedHasConfigurationChanged) {
+            // Just load the views as-is
+        } else if (mConfig.launchedFromAppWithThumbnail) {
             if (isTaskViewLaunchTargetTask) {
                 // Set the dim to 0 so we can animate it in
                 initialDim = 0;
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 464d007..05f6f40 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -237,15 +237,17 @@
 
     /** Animates this task bar if the user does not interact with the stack after a certain time. */
     void startNoUserInteractionAnimation() {
-        mDismissButton.setVisibility(View.VISIBLE);
-        mDismissButton.setAlpha(0f);
-        mDismissButton.animate()
-                .alpha(1f)
-                .setStartDelay(0)
-                .setInterpolator(mConfig.fastOutLinearInInterpolator)
-                .setDuration(mConfig.taskViewEnterFromAppDuration)
-                .withLayer()
-                .start();
+        if (mDismissButton.getVisibility() != View.VISIBLE) {
+            mDismissButton.setVisibility(View.VISIBLE);
+            mDismissButton.setAlpha(0f);
+            mDismissButton.animate()
+                    .alpha(1f)
+                    .setStartDelay(0)
+                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
+                    .setDuration(mConfig.taskViewEnterFromAppDuration)
+                    .withLayer()
+                    .start();
+        }
     }
 
     /** Mark this task view that the user does has not interacted with the stack after a certain time. */