Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8873754f authored by Winson's avatar Winson
Browse files

Refactoring to update stack in onResume.

This CL fixes a long-standing issue in which Recents is not updated
correctly if it is not completely hidden and shown.  In particular, it
would cause animation issues when launching into a non-fullscreen
activity or if the user quickly toggles between recent tasks.  It
contains several fixes:

- The visual state in Recents is no longer reset until the activity is 
  fully hidden (onStop() is called), and the task stack state is saved 
  allowing us to return to the same initial state.  When restarting the 
  activity, we propagate whether the activity was hidden down the view 
  hierarchy, so that each task can decide whether to reset itself.
- When the recents activity is started, we now merge the new stack with
  the current stack instead of replacing it completely.  This unifies
  the logic when dismissing multi-window while Recents is open, and this
  CL fixes an issue with the merging where onStackTaskAdded() was called
  before the stack was updated with the new task.  As a result of this
  change, we can just rebind the task views without having to return and
  pick them up from the view pool.
- This CL also fixes an issue with flashing when the screen turns off.  
  The activity onStop() can be called before the activity is fully 
  hidden, which would trigger a reset(), which would return all views to
  the pool.

Bug: 23815609
Bug: 25411120
Bug: 27186407
Change-Id: I83d74c947f9b47766d6778b7f5c421bb6df833e9
parent 8b030cce
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -513,8 +513,9 @@ public class Recents extends SystemUI
     * Handle Recents activity visibility changed.
     */
    public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
        int processUser = event.systemServicesProxy.getProcessUser();
        if (event.systemServicesProxy.isSystemUser(processUser)) {
        SystemServicesProxy ssp = Recents.getSystemServices();
        int processUser = ssp.getProcessUser();
        if (ssp.isSystemUser(processUser)) {
            mImpl.onVisibilityChanged(event.applicationContext, event.visible);
        } else {
            postToSystemUser(new Runnable() {
+102 −102
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -97,6 +97,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
    private long mLastTabKeyEventTime;
    private boolean mFinishedOnStartup;
    private boolean mIgnoreAltTabRelease;
    private boolean mIsVisible;

    // Top level views
    private RecentsView mRecentsView;
@@ -174,59 +175,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
        }
    };

    /** Updates the set of recent tasks */
    void updateRecentsTasks() {
        // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
        // reconstructing the task stack
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
        if (plan == null) {
            plan = loader.createLoadPlan(this);
        }

        // Start loading tasks according to the load plan
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        if (!plan.hasTasks()) {
            loader.preloadTasks(plan, -1, launchState.launchedFromHome);
        }
        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
        loadOpts.runningTaskId = launchState.launchedToTaskId;
        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
        loader.loadTasks(this, plan, loadOpts);

        TaskStack stack = plan.getTaskStack();
        mRecentsView.setTaskStack(stack);

        // Animate the SystemUI scrims into view
        Task launchTarget = stack.getLaunchTarget();
        int taskCount = stack.getTaskCount();
        int launchTaskIndexInStack = launchTarget != null
                ? stack.indexOfStackTask(launchTarget)
                : 0;
        boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);

        // Keep track of whether we launched from the nav bar button or via alt-tab
        if (launchState.launchedWithAltTab) {
            MetricsLogger.count(this, "overview_trigger_alttab", 1);
        } else {
            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
        }
        // Keep track of whether we launched from an app or from home
        if (launchState.launchedFromApp) {
            MetricsLogger.count(this, "overview_source_app", 1);
            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
        } else {
            MetricsLogger.count(this, "overview_source_home", 1);
        }
        // Keep track of the total stack task count
        MetricsLogger.histogram(this, "overview_task_count", taskCount);
    }

    /**
     * Dismisses the history view back into the stack view.
     */
@@ -345,6 +293,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD

        // Set the Recents layout
        setContentView(R.layout.recents);
        takeKeyEvents(true);
        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
@@ -382,35 +331,66 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    protected void onStart() {
        super.onStart();

        // Notify that recents is now visible
        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
    }

    @Override
    protected void onStart() {
        super.onStart();
    public void onEnterAnimationComplete() {
        super.onEnterAnimationComplete();
        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
    }

        // Update the recent tasks
        updateRecentsTasks();
    @Override
    protected void onResume() {
        super.onResume();

        // If the Recents component has preloaded a load plan, then use that to prevent
        // reconstructing the task stack
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
        if (loadPlan == null) {
            loadPlan = loader.createLoadPlan(this);
        }

        // Start loading tasks according to the load plan
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        if (!loadPlan.hasTasks()) {
            loader.preloadTasks(loadPlan, -1, launchState.launchedFromHome);
        }

        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
        loadOpts.runningTaskId = launchState.launchedToTaskId;
        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
        loader.loadTasks(this, loadPlan, loadOpts);
        TaskStack stack = loadPlan.getTaskStack();
        mRecentsView.onResume(mIsVisible, stack);

        // Animate the SystemUI scrims into view
        Task launchTarget = stack.getLaunchTarget();
        int taskCount = stack.getTaskCount();
        int launchTaskIndexInStack = launchTarget != null
                ? stack.indexOfStackTask(launchTarget)
                : 0;
        boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);

        // If this is a new instance from a configuration change, then we have to manually trigger
        // the enter animation state, or if recents was relaunched by AM, without going through
        // the normal mechanisms
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
                !launchState.launchedFromApp;
        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
        }

        // Notify that recents is now visible
        SystemServicesProxy ssp = Recents.getSystemServices();
        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));

        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);

        mRecentsView.getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {

@@ -421,43 +401,57 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
                        return true;
                    }
                });

        // Keep track of whether we launched from the nav bar button or via alt-tab
        if (launchState.launchedWithAltTab) {
            MetricsLogger.count(this, "overview_trigger_alttab", 1);
        } else {
            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
        }

    @Override
    public void onEnterAnimationComplete() {
        super.onEnterAnimationComplete();
        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
        // Keep track of whether we launched from an app or from home
        if (launchState.launchedFromApp) {
            MetricsLogger.count(this, "overview_source_app", 1);
            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
        } else {
            MetricsLogger.count(this, "overview_source_home", 1);
        }

        // Keep track of the total stack task count
        MetricsLogger.histogram(this, "overview_task_count", taskCount);

        // After we have resumed, set the visible state until the next onStop() call
        mIsVisible = true;
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Stop the fast-toggle dozer
        mIgnoreAltTabRelease = false;
        mIterateTrigger.stopDozing();

        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
        // through SystemUI, we need to reset the config launch flags to ensure that we do not
        // wait on the system to send a signal that was never queued.
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        launchState.reset();
    }

    @Override
    protected void onStop() {
        super.onStop();

        // Reset some states
        mIgnoreAltTabRelease = false;
        // Only hide the history if Recents is completely hidden
        if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
        }

        // Notify that recents is now hidden
        SystemServicesProxy ssp = Recents.getSystemServices();
        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));

        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
        // through SystemUI, we need to reset the config launch flags to ensure that we do not
        // wait on the system to send a signal that was never queued.
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        launchState.reset();

        mIsVisible = false;
        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
        MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
    }

@@ -525,16 +519,22 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
    public void onMultiWindowChanged(boolean inMultiWindow) {
        super.onMultiWindowChanged(inMultiWindow);
        EventBus.getDefault().send(new ConfigurationChangedEvent());

        // Reload the task stack completely
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
        launchOpts.loadIcons = false;
        launchOpts.loadThumbnails = false;
        launchOpts.onlyLoadForCache = true;
        RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
        loader.preloadTasks(loadPlan, -1, false);
        loader.loadTasks(this, loadPlan, launchOpts);
        EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
                inMultiWindow));
        loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);

        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
        loader.loadTasks(this, loadPlan, loadOpts);

        mRecentsView.onResume(mIsVisible, loadPlan.getTaskStack());

        EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
    }

    @Override
+7 −9
Original line number Diff line number Diff line
@@ -602,7 +602,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
                com.android.internal.R.dimen.navigation_bar_width);
        mTaskBarHeight = res.getDimensionPixelSize(
                R.dimen.recents_task_bar_height);
        mDummyStackView = new TaskStackView(mContext, new TaskStack());
        mDummyStackView = new TaskStackView(mContext);
        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
                null, false);
    }
@@ -615,8 +615,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
     *                               is not already bound (can be expensive)
     * @param stack the stack to initialize the stack layout with
     */
    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
            TaskStack stack) {
    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
        RecentsConfiguration config = Recents.getConfiguration();
        SystemServicesProxy ssp = Recents.getSystemServices();
        Rect systemInsets = new Rect();
@@ -640,14 +639,15 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
                mSearchBarBounds, mTaskStackBounds);

        // Rebind the header bar and draw it for the transition
        TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
        TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
        Rect taskStackBounds = new Rect(mTaskStackBounds);
        algo.setSystemInsets(systemInsets);
        stackLayout.setSystemInsets(systemInsets);
        if (stack != null) {
            algo.initialize(taskStackBounds,
            stackLayout.initialize(taskStackBounds,
                    TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
            mDummyStackView.setTasks(stack, false /* notifyStackChanges */);
        }
        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
        Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
            mLastTaskViewBounds.set(taskViewBounds);

@@ -705,7 +705,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);

        // Update the destination rect
        mDummyStackView.updateLayoutForStack(stack);
        final Task toTask = new Task();
        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
        ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@@ -887,7 +886,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);

        // Prepare the dummy stack for the transition
        mDummyStackView.updateLayoutForStack(stack);
        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
                mDummyStackView.computeStackVisibilityReport();

+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.events.activity;

import com.android.systemui.recents.events.EventBus;

/**
 * This is sent by the activity whenever the multi-window state has changed.
 */
public class MultiWindowStateChangedEvent extends EventBus.Event {

    public final boolean inMultiWindow;

    public MultiWindowStateChangedEvent(boolean inMultiWindow) {
        this.inMultiWindow = inMultiWindow;
    }
}
+3 −6
Original line number Diff line number Diff line
@@ -19,21 +19,18 @@ package com.android.systemui.recents.events.component;
import android.content.Context;

import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.SystemServicesProxy;

/**
 * This is sent when the visibility of the RecentsActivity for the current user changes.
 * This is sent when the visibility of the RecentsActivity for the current user changes.  Handlers
 * of this event should not alter the UI, as the activity may still be visible.
 */
public class RecentsVisibilityChangedEvent extends EventBus.Event {

    public final Context applicationContext;
    public final SystemServicesProxy systemServicesProxy;
    public final boolean visible;

    public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
            boolean visible) {
    public RecentsVisibilityChangedEvent(Context context, boolean visible) {
        this.applicationContext = context.getApplicationContext();
        this.systemServicesProxy = systemServicesProxy;
        this.visible = visible;
    }
}
Loading