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

Commit 736ff82e authored by Robin Lee's avatar Robin Lee
Browse files

Handover Recents to Keyguard on swipe-up finish

Test: atest CtsWindowManagerDeviceKeyguard:KeyguardTests
Bug: 293862576
Change-Id: I74b76ffb4505c0b1e4f30e3c8af348fa07fc7cc9
parent 72f2a2b9
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.view.WindowManager;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Used to communicate information about what is changing during a transition to a TransitionPlayer.
@@ -610,7 +611,7 @@ public final class TransitionInfo implements Parcelable {
        private final WindowContainerToken mContainer;
        private WindowContainerToken mParent;
        private WindowContainerToken mLastParent;
        private final SurfaceControl mLeash;
        private SurfaceControl mLeash;
        private @TransitionMode int mMode = TRANSIT_NONE;
        private @ChangeFlags int mFlags = FLAG_NONE;
        private final Rect mStartAbsBounds = new Rect();
@@ -697,6 +698,11 @@ public final class TransitionInfo implements Parcelable {
            mLastParent = lastParent;
        }

        /** Sets the animation leash for controlling this change's container */
        public void setLeash(@NonNull SurfaceControl leash) {
            mLeash = Objects.requireNonNull(leash);
        }

        /** Sets the transition mode for this change */
        public void setMode(@TransitionMode int mode) {
            mMode = mode;
+2 −1
Original line number Diff line number Diff line
@@ -646,11 +646,12 @@ public abstract class WMShellBaseModule {
    @Provides
    static KeyguardTransitionHandler provideKeyguardTransitionHandler(
            ShellInit shellInit,
            ShellController shellController,
            Transitions transitions,
            @ShellMainThread Handler mainHandler,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new KeyguardTransitionHandler(
                    shellInit, transitions, mainHandler, mainExecutor);
                    shellInit, shellController, transitions, mainHandler, mainExecutor);
    }

    @WMSingleton
+22 −1
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
@@ -58,10 +60,12 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
 *
 * <p>This takes the highest priority.
 */
public class KeyguardTransitionHandler implements Transitions.TransitionHandler {
public class KeyguardTransitionHandler
        implements Transitions.TransitionHandler, KeyguardChangeListener {
    private static final String TAG = "KeyguardTransition";

    private final Transitions mTransitions;
    private final ShellController mShellController;
    private final Handler mMainHandler;
    private final ShellExecutor mMainExecutor;

@@ -80,6 +84,9 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
    // transition.
    private boolean mIsLaunchingActivityOverLockscreen;

    // Last value reported by {@link KeyguardChangeListener}.
    private boolean mKeyguardShowing = true;

    private final class StartedTransition {
        final TransitionInfo mInfo;
        final SurfaceControl.Transaction mFinishT;
@@ -92,12 +99,15 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
            mPlayer = player;
        }
    }

    public KeyguardTransitionHandler(
            @NonNull ShellInit shellInit,
            @NonNull ShellController shellController,
            @NonNull Transitions transitions,
            @NonNull Handler mainHandler,
            @NonNull ShellExecutor mainExecutor) {
        mTransitions = transitions;
        mShellController = shellController;
        mMainHandler = mainHandler;
        mMainExecutor = mainExecutor;
        shellInit.addInitCallback(this::onInit, this);
@@ -105,6 +115,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler

    private void onInit() {
        mTransitions.addHandler(this);
        mShellController.addKeyguardChangeListener(this);
    }

    /**
@@ -119,6 +130,16 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
        return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0;
    }

    @Override
    public void onKeyguardVisibilityChanged(
            boolean visible, boolean occluded, boolean animatingDismiss) {
        mKeyguardShowing = visible;
    }

    public boolean isKeyguardShowing() {
        return mKeyguardShowing;
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
+18 −15
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;

import java.util.ArrayList;
import java.util.function.Consumer;

/**
 * Handles the Recents (overview) animation. Only one of these can run at a time. A recents
@@ -130,21 +131,21 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
        wct.sendPendingIntent(intent, fillIn, options);
        final RecentsController controller = new RecentsController(listener);
        RecentsMixedHandler mixer = null;
        Transitions.TransitionHandler mixedHandler = null;
        Consumer<IBinder> setTransitionForMixer = null;
        for (int i = 0; i < mMixers.size(); ++i) {
            mixedHandler = mMixers.get(i).handleRecentsRequest(wct);
            if (mixedHandler != null) {
            setTransitionForMixer = mMixers.get(i).handleRecentsRequest(wct);
            if (setTransitionForMixer != null) {
                mixer = mMixers.get(i);
                break;
            }
        }
        final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
                mixedHandler == null ? this : mixedHandler);
                mixer == null ? this : mixer);
        for (int i = 0; i < mStateListeners.size(); i++) {
            mStateListeners.get(i).onTransitionStarted(transition);
        }
        if (mixer != null) {
            mixer.setRecentsTransition(transition);
            setTransitionForMixer.accept(transition);
        }
        if (transition != null) {
            controller.setTransition(transition);
@@ -589,6 +590,13 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
                cancel("transit_sleep");
                return;
            }
            if (mKeyguardLocked) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                        "[%d] RecentsController.merge: keyguard is locked", mInstanceId);
                // We will not accept new changes if we are swiping over the keyguard.
                cancel(true /* toHome */, false /* withScreenshots */, "keyguard_locked");
                return;
            }
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                    "[%d] RecentsController.merge", mInstanceId);
            // Keep all tasks in one list because order matters.
@@ -1105,22 +1113,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
     * An interface for a mixed handler to receive information about recents requests (since these
     * come into this handler directly vs from WMCore request).
     */
    public interface RecentsMixedHandler {
    public interface RecentsMixedHandler extends Transitions.TransitionHandler {
        /**
         * Called when a recents request comes in. The handler can add operations to outWCT. If
         * the handler wants to "accept" the transition, it should return itself; otherwise, it
         * should return `null`.
         * the handler wants to "accept" the transition, it should return a Consumer accepting the
         * IBinder for the transition. If not, it should return `null`.
         *
         * If a mixed-handler accepts this recents, it will be the de-facto handler for this
         * transition and is required to call the associated {@link #startAnimation},
         * {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
         */
        Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT);

        /**
         * Reports the transition token associated with the accepted recents request. If there was
         * a problem starting the request, this will be called with `null`.
         */
        void setRecentsTransition(@Nullable IBinder transition);
        @Nullable
        Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT);
    }
}
+139 −40
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;

@@ -37,11 +38,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.internal.protolog.common.ProtoLog;
@@ -61,7 +64,9 @@ import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.util.TransitionUtil;

import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

/**
 * A handler for dealing with transitions involving multiple other handlers. For example: an
@@ -79,7 +84,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    private UnfoldTransitionHandler mUnfoldHandler;
    private ActivityEmbeddingController mActivityEmbeddingController;

    private static class MixedTransition {
    private class MixedTransition {
        static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;

        /** Both the display and split-state (enter/exit) is changing */
@@ -94,14 +99,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        /** Keyguard exit/occlude/unocclude transition. */
        static final int TYPE_KEYGUARD = 5;

        /** Recents transition on top of the lock screen. */
        static final int TYPE_RECENTS_DURING_KEYGUARD = 6;

        /** Recents Transition while in desktop mode. */
        static final int TYPE_RECENTS_DURING_DESKTOP = 6;
        static final int TYPE_RECENTS_DURING_DESKTOP = 7;

        /** Fold/Unfold transition. */
        static final int TYPE_UNFOLD = 7;
        static final int TYPE_UNFOLD = 8;

        /** Enter pip from one of the Activity Embedding windows. */
        static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;
        static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9;

        /** The default animation for this mixed transition. */
        static final int ANIM_TYPE_DEFAULT = 0;
@@ -117,7 +125,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        final IBinder mTransition;

        Transitions.TransitionHandler mLeftoversHandler = null;
        TransitionInfo mInfo = null;
        WindowContainerTransaction mFinishWCT = null;
        SurfaceControl.Transaction mFinishT = null;
        Transitions.TransitionFinishCallback mFinishCB = null;

        /**
         * Whether the transition has request for remote transition while mLeftoversHandler
@@ -138,6 +149,37 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            mTransition = transition;
        }

        boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info,
                SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
            if (mInfo != null) {
                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                        "startSubAnimation #%d.%d", mInfo.getDebugId(), info.getDebugId());
            }
            mInFlightSubAnimations++;
            if (!handler.startAnimation(
                    mTransition, info, startT, finishT, wct -> onSubAnimationFinished(info, wct))) {
                mInFlightSubAnimations--;
                return false;
            }
            return true;
        }

        void onSubAnimationFinished(TransitionInfo info, WindowContainerTransaction wct) {
            mInFlightSubAnimations--;
            if (mInfo != null) {
                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                        "onSubAnimationFinished #%d.%d remaining=%d",
                        mInfo.getDebugId(), info.getDebugId(), mInFlightSubAnimations);
            }

            joinFinishArgs(wct);

            if (mInFlightSubAnimations == 0) {
                mActiveTransitions.remove(MixedTransition.this);
                mFinishCB.onTransitionFinished(mFinishWCT);
            }
        }

        void joinFinishArgs(WindowContainerTransaction wct) {
            if (wct != null) {
                if (mFinishWCT == null) {
@@ -271,39 +313,46 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    }

    @Override
    public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
    public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) {
        if (mRecentsHandler != null) {
            if (mSplitHandler.isSplitScreenVisible()) {
                return this;
                return this::setRecentsTransitionDuringSplit;
            } else if (mKeyguardHandler.isKeyguardShowing()) {
                return this::setRecentsTransitionDuringKeyguard;
            } else if (mDesktopTasksController != null
                    // Check on the default display. Recents/gesture nav is only available there
                    && mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) {
                return this;
                return this::setRecentsTransitionDuringDesktop;
            }
        }
        return null;
    }

    @Override
    public void setRecentsTransition(IBinder transition) {
        if (mSplitHandler.isSplitScreenVisible()) {
    private void setRecentsTransitionDuringSplit(IBinder transition) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                + "Split-Screen is foreground, so treat it as Mixed.");
        final MixedTransition mixed = new MixedTransition(
                MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
        mixed.mLeftoversHandler = mRecentsHandler;
        mActiveTransitions.add(mixed);
        } else if (DesktopModeStatus.isEnabled()) {
    }

    private void setRecentsTransitionDuringKeyguard(IBinder transition) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                + "keyguard is visible, so treat it as Mixed.");
        final MixedTransition mixed = new MixedTransition(
                MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition);
        mixed.mLeftoversHandler = mRecentsHandler;
        mActiveTransitions.add(mixed);
    }

    private void setRecentsTransitionDuringDesktop(IBinder transition) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                + "desktop mode is active, so treat it as Mixed.");
        final MixedTransition mixed = new MixedTransition(
                MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition);
        mixed.mLeftoversHandler = mRecentsHandler;
        mActiveTransitions.add(mixed);
        } else {
            throw new IllegalStateException("Accepted a recents transition but don't know how to"
                    + " handle it");
        }
    }

    private TransitionInfo subCopy(@NonNull TransitionInfo info,
@@ -410,6 +459,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
            return animateKeyguard(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
            return animateRecentsDuringKeyguard(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
            return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
@@ -764,24 +816,28 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        final Transitions.TransitionFinishCallback finishCB = (wct) -> {
            mixed.mInFlightSubAnimations--;
            if (mixed.mInFlightSubAnimations == 0) {
                mActiveTransitions.remove(mixed);
                finishCallback.onTransitionFinished(wct);
        if (mixed.mFinishT == null) {
            mixed.mFinishT = finishTransaction;
            mixed.mFinishCB = finishCallback;
        }
        };
        mixed.mInFlightSubAnimations++;
        // Sync pip state.
        if (mPipHandler != null) {
            mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
        }
        if (!mKeyguardHandler.startAnimation(
                mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) {
            mixed.mInFlightSubAnimations--;
            return false;
        return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction);
    }
        return true;

    private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (mixed.mInfo == null) {
            mixed.mInfo = info;
            mixed.mFinishT = finishTransaction;
            mixed.mFinishCB = finishCallback;
        }
        return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
    }

    private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed,
@@ -905,6 +961,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
                        finishCallback);
            } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
                mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
                if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
                    handoverTransitionLeashes(mixed, info, t, mixed.mFinishT);
                    if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) {
                        finishCallback.onTransitionFinished(null);
                    }
                }
                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                        finishCallback);
            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                        finishCallback);
@@ -947,4 +1012,38 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
        }
    }

    /**
     * Update an incoming {@link TransitionInfo} with the leashes from an ongoing
     * {@link MixedTransition} so that it can take over some parts of the animation without
     * reparenting to new transition roots.
     */
    private static void handoverTransitionLeashes(@NonNull MixedTransition mixed,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startT,
            @NonNull SurfaceControl.Transaction finishT) {

        // Show the roots in case they contain new changes not present in the original transition.
        for (int j = info.getRootCount() - 1; j >= 0; --j) {
            startT.show(info.getRoot(j).getLeash());
        }

        // Find all of the leashes from the original transition.
        Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>();
        for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) {
            if (oldChange.getContainer() != null) {
                originalChanges.put(oldChange.getContainer(), oldChange);
            }
        }

        // Merge the animation leashes by re-using the original ones if we see the same container
        // in the new transition and the old.
        for (TransitionInfo.Change newChange : info.getChanges()) {
            if (originalChanges.containsKey(newChange.getContainer())) {
                final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer());
                startT.reparent(newChange.getLeash(), null);
                newChange.setLeash(oldChange.getLeash());
            }
        }
    }
}