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

Commit d0272fd3 authored by Ats Jenk's avatar Ats Jenk
Browse files

Animate task to bubble immediately during transition

Remove the wait for Launcher when converting a task to a bubble via
transition.
Previously we were waiting for the Launcher part to show the bubble bar
and come back to shell to show the expanded view.
This works for new bubbles that are triggered via notifications. But
adds a visible to delay when the bubble is triggered via user action
from a running app.
The delay is especially noticable when moving an app to a bubble via
drag. As the app waits on the screen before starting to move to the
bubble position.
Update the flow so that the convert to bubble animation can start
immediately when the transition is ready and the surface is created.
Add a new progress tracker class to the ConvertToBubble transition class
that can track different parts of the transition completing and then
performing the required actions.
We still have the callback from Launcher, but only use it to clear the
pending transition object from bubble. This pending transition is used
to tell other parts of the bubble code that this bubble will be expanded
via the transition animation and they do not need to animate it.

Bug: 388851898
Test: atest BubbleTransitionsTest
Flag: com.android.wm.shell.enable_bubble_to_fullscreen
Change-Id: Ie61f67a752ed7faec728f8de6c20e779cd0e4460
parent 83385e6b
Loading
Loading
Loading
Loading
+56 −15
Original line number Diff line number Diff line
@@ -189,14 +189,16 @@ public class BubbleTransitions {
     *
     * 1. Start inflating the bubble view
     * 2. Once inflated (but not-yet visible), tell WM to do the shell-transition.
     * 3. Transition becomes ready, so notify Launcher
     * 4. Launcher responds with showExpandedView which calls continueExpand() to make view visible
     * 5. Surface is created which kicks off actual animation
     * 3. When the transition becomes ready, notify Launcher in parallel
     * 4. Wait for surface to be created
     * 5. Once surface is ready, animate the task to a bubble
     *
     * So, constructor -> onInflated -> startAnimation -> continueExpand -> surfaceCreated.
     * While the animation is pending, we keep a reference to the pending transition in the bubble.
     * This allows us to check in other parts of the code that this bubble will be shown via the
     * transition animation.
     *
     * continueExpand and surfaceCreated are set-up to happen in either order, though, to support
     * UX/timing adjustments.
     * startAnimation, continueExpand and surfaceCreated are set-up to happen in either order,
     * to support UX/timing adjustments.
     */
    @VisibleForTesting
    class ConvertToBubble implements Transitions.TransitionHandler, BubbleTransition {
@@ -209,9 +211,9 @@ public class BubbleTransitions {
        final Rect mStartBounds = new Rect();
        SurfaceControl mSnapshot = null;
        TaskInfo mTaskInfo;
        boolean mFinishedExpand = false;
        BubbleViewProvider mPriorBubble = null;

        private final TransitionProgress mTransitionProgress = new TransitionProgress();
        private SurfaceControl.Transaction mFinishT;
        private SurfaceControl mTaskLeash;

@@ -359,12 +361,12 @@ public class BubbleTransitions {
            startTransaction.apply();

            mTaskViewTransitions.onExternalDone(transition);
            mTransitionProgress.setTransitionReady();
            startExpandAnim();
            return true;
        }

        @Override
        public void continueExpand() {
            mFinishedExpand = true;
        private void startExpandAnim() {
            final boolean animate = mLayerView.canExpandView(mBubble);
            if (animate) {
                mPriorBubble = mLayerView.prepareConvertedView(mBubble);
@@ -375,19 +377,25 @@ public class BubbleTransitions {
                mLayerView.removeView(priorView);
                mPriorBubble = null;
            }
            if (!animate || mBubble.getTaskView().getSurfaceControl() != null) {
            if (!animate || mTransitionProgress.isReadyToAnimate()) {
                playAnimation(animate);
            }
        }

        @Override
        public void continueExpand() {
            mTransitionProgress.setReadyToExpand();
        }

        @Override
        public void surfaceCreated() {
            mTransitionProgress.setSurfaceReady();
            mMainExecutor.execute(() -> {
                final TaskViewTaskController tvc = mBubble.getTaskView().getController();
                final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
                if (state == null) return;
                state.mVisible = true;
                if (mFinishedExpand) {
                if (mTransitionProgress.isReadyToAnimate()) {
                    playAnimation(true /* animate */);
                }
            });
@@ -403,9 +411,6 @@ public class BubbleTransitions {
                mFinishWct = null;
            }

            // Preparation is complete.
            mBubble.setPreparingTransition(null);

            if (animate) {
                mLayerView.animateConvert(startT, mStartBounds, mSnapshot, mTaskLeash, () -> {
                    mFinishCb.onTransitionFinished(mFinishWct);
@@ -417,6 +422,42 @@ public class BubbleTransitions {
                mFinishCb = null;
            }
        }

        /**
         * Keeps track of internal state of different steps of this BubbleTransition.
         */
        private class TransitionProgress {
            private boolean mTransitionReady;
            private boolean mReadyToExpand;
            private boolean mSurfaceReady;

            void setTransitionReady() {
                mTransitionReady = true;
                onUpdate();
            }

            void setReadyToExpand() {
                mReadyToExpand = true;
                onUpdate();
            }

            void setSurfaceReady() {
                mSurfaceReady = true;
                onUpdate();
            }

            boolean isReadyToAnimate() {
                // Animation only depends on transition and surface state
                return mTransitionReady && mSurfaceReady;
            }

            private void onUpdate() {
                if (mTransitionReady && mReadyToExpand && mSurfaceReady) {
                    // Clear the transition from bubble when all the steps are ready
                    mBubble.setPreparingTransition(null);
                }
            }
        }
    }

    /**
+7 −2
Original line number Diff line number Diff line
@@ -186,15 +186,20 @@ public class BubbleTransitionsTest extends ShellTestCase {
        verify(startT).setPosition(any(), eq(0f), eq(0f));

        verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean());
        ctb.continueExpand();

        clearInvocations(mBubble);
        verify(mBubble, never()).setPreparingTransition(any());

        ctb.surfaceCreated();
        verify(mBubble).setPreparingTransition(isNull());
        // Check that preparing transition is not reset before continueExpand is called
        verify(mBubble, never()).setPreparingTransition(any());
        ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class);
        verify(mLayerView).animateConvert(any(), any(), any(), any(), animCb.capture());

        // continueExpand is now called, check that preparing transition is cleared
        ctb.continueExpand();
        verify(mBubble).setPreparingTransition(isNull());

        assertFalse(finishCalled[0]);
        animCb.getValue().run();
        assertTrue(finishCalled[0]);