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

Commit 150072f4 authored by Ben Lin's avatar Ben Lin
Browse files

PiP: Update menu state AFTER animation is done for show/hide/resize.

When hiding the menu, there is a brief moment where the menu hide
animation is running, but the menu is already set as hidden in
mMenuState. In that case, other menu operations, such as resizing it,
would no-op, since it assumes the menu is not visible to the user
anymore. We will update the menu at end of the animation so that before
the animation finishes, we will respect that the menu is still in its
previous state. This will break apart the callback into two parts -
onMenuStateChanging, and onMenuStateChangeFinish.

Additionally, PipAnimationController used to only control animating the
PiP leash, but not the menu. This CL also adds a handler callback to
allow menu controller to apply the transaction.

Bug: 185513811
Bug: 186424689
Test: Tap once (show menu), pinch resize - menu resizes & fade along
with the resize
Test: Resize to smallest size, tap once (so that it grows bigger), and
then tap outside region - see that the menu no longer lags behind on
fading out.

Change-Id: I4432961aff074f4569d5d6d61b0e917511e2cbad
parent 964a1715
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
@@ -207,6 +207,24 @@ public class PipAnimationController {
        public void onPipAnimationCancel(TaskInfo taskInfo, PipTransitionAnimator animator) {}
    }

    /**
     * A handler class that could register itself to apply the transaction instead of the
     * animation controller doing it. For example, the menu controller can be one such handler.
     */
    public static class PipTransactionHandler {

        /**
         * Called when the animation controller is about to apply a transaction. Allow a registered
         * handler to apply the transaction instead.
         *
         * @return true if handled by the handler, false otherwise.
         */
        public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
                Rect destinationBounds) {
            return false;
        }
    }

    /**
     * Animator for PiP transition animation which supports both alpha and bounds animation.
     * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
@@ -225,6 +243,7 @@ public class PipAnimationController {
        private T mEndValue;
        private float mStartingAngle;
        private PipAnimationCallback mPipAnimationCallback;
        private PipTransactionHandler mPipTransactionHandler;
        private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
                mSurfaceControlTransactionFactory;
        private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -293,6 +312,20 @@ public class PipAnimationController {
            mPipAnimationCallback = callback;
            return this;
        }

        PipTransitionAnimator<T> setPipTransactionHandler(PipTransactionHandler handler) {
            mPipTransactionHandler = handler;
            return this;
        }

        boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
                Rect destinationBounds) {
            if (mPipTransactionHandler != null) {
                return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds);
            }
            return false;
        }

        @VisibleForTesting
        @TransitionDirection public int getTransitionDirection() {
            return mTransitionDirection;
@@ -499,8 +532,10 @@ public class PipAnimationController {
                        getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
                                initialSourceValue, bounds, insets);
                    }
                    if (!handlePipTransaction(leash, tx, bounds)) {
                        tx.apply();
                    }
                }

                private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
                        float fraction, Rect start, Rect end) {
+19 −1
Original line number Diff line number Diff line
@@ -200,6 +200,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        }
    };

    private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
            new PipAnimationController.PipTransactionHandler() {
                @Override
                public boolean handlePipTransaction(SurfaceControl leash,
                        SurfaceControl.Transaction tx, Rect destinationBounds) {
                    if (mPipMenuController.isMenuVisible()) {
                        mPipMenuController.movePipMenu(leash, tx, destinationBounds);
                        return true;
                    }
                    return false;
                }
            };

    private ActivityManager.RunningTaskInfo mTaskInfo;
    // To handle the edge case that onTaskInfoChanged callback is received during the entering
    // PiP transition, where we do not want to intercept the transition but still want to apply the
@@ -433,8 +446,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,

        // removePipImmediately is expected when the following animation finishes.
        ValueAnimator animator = mPipAnimationController
                .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f)
                .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(),
                        1f /* alphaStart */, 0f /* alphaEnd */)
                .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setPipAnimationCallback(mPipAnimationCallback);
        animator.setDuration(mExitAnimationDuration);
        animator.setInterpolator(Interpolators.ALPHA_OUT);
@@ -573,6 +588,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                    .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
                    .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                    .setPipAnimationCallback(mPipAnimationCallback)
                    .setPipTransactionHandler(mPipTransactionHandler)
                    .setDuration(durationMs)
                    .start();
            // mState is set right after the animation is kicked off to block any resize
@@ -749,6 +765,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mPipAnimationController
                .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
                .setTransitionDirection(TRANSITION_DIRECTION_SAME)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setDuration(show ? mEnterAnimationDuration : mExitAnimationDuration)
                .start();
        mHasFadeOut = !show;
@@ -1226,6 +1243,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                        sourceHintRect, direction, startingAngle, rotationDelta);
        animator.setTransitionDirection(direction)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setDuration(durationMs)
                .start();
        if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
+5 −5
Original line number Diff line number Diff line
@@ -111,14 +111,14 @@ public class PipTransition extends PipTransitionController {
            final Rect sourceHintRect =
                    PipBoundsAlgorithm.getValidSourceHintRect(
                            taskInfo.pictureInPictureParams, currentBounds);
            animator = mPipAnimationController.getAnimator(taskInfo, leash,
                    currentBounds, currentBounds, destinationBounds, sourceHintRect,
                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
            animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
                    currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
                    0 /* startingAngle */, Surface.ROTATION_0);
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
            t.setAlpha(leash, 0f);
            t.apply();
            animator = mPipAnimationController.getAnimator(taskInfo, leash,
                    destinationBounds, 0f, 1f);
            animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
                    0f, 1f);
            mOneShotAnimationType = ANIM_TYPE_BOUNDS;
        } else {
            throw new RuntimeException("Unrecognized animation type: "
+19 −6
Original line number Diff line number Diff line
@@ -70,12 +70,19 @@ public class PhonePipMenuController implements PipMenuController {
     */
    public interface Listener {
        /**
         * Called when the PIP menu visibility changes.
         * Called when the PIP menu visibility change has started.
         *
         * @param menuState the current state of the menu
         * @param menuState the new, about-to-change state of the menu
         * @param resize whether or not to resize the PiP with the state change
         */
        void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback);
        void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback);

        /**
         * Called when the PIP menu state has finished changing/animating.
         *
         * @param menuState the new state of the menu.
         */
        void onPipMenuStateChangeFinish(int menuState);

        /**
         * Called when the PIP requested to be expanded.
@@ -485,15 +492,15 @@ public class PhonePipMenuController implements PipMenuController {
    /**
     * Handles changes in menu visibility.
     */
    void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
    void onMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
        if (DEBUG) {
            Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
            Log.d(TAG, "onMenuStateChangeStart() mMenuState=" + mMenuState
                    + " menuState=" + menuState + " resize=" + resize
                    + " callers=\n" + Debug.getCallers(5, "    "));
        }

        if (menuState != mMenuState) {
            mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize, callback));
            mListeners.forEach(l -> l.onPipMenuStateChangeStart(menuState, resize, callback));
            if (menuState == MENU_STATE_FULL) {
                // Once visible, start listening for media action changes. This call will trigger
                // the menu actions to be updated again.
@@ -511,6 +518,12 @@ public class PhonePipMenuController implements PipMenuController {
                Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
            }
        }
    }

    void onMenuStateChangeFinish(int menuState) {
        if (menuState != mMenuState) {
            mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
        }
        mMenuState = menuState;
    }

+24 −16
Original line number Diff line number Diff line
@@ -282,17 +282,18 @@ public class PipMenuView extends FrameLayout {
            }
            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
            mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
            if (allowMenuTimeout) {
            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    notifyMenuStateChangeFinish(menuState);
                    if (allowMenuTimeout) {
                        repostDelayedHide(INITIAL_DISMISS_DELAY);
                    }
                });
                }
            });
            if (withDelay) {
                // starts the menu container animation after window expansion is completed
                notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
                notifyMenuStateChangeStart(menuState, resizeMenuOnShow, () -> {
                    if (mMenuContainerAnimator == null) {
                        return;
                    }
@@ -301,11 +302,11 @@ public class PipMenuView extends FrameLayout {
                    mMenuContainerAnimator.start();
                });
            } else {
                notifyMenuStateChange(menuState, resizeMenuOnShow, null);
                notifyMenuStateChangeStart(menuState, resizeMenuOnShow, null);
                setVisibility(VISIBLE);
                mMenuContainerAnimator.start();
            }
            updateActionViews(stackBounds);
            updateActionViews(menuState, stackBounds);
        } else {
            // If we are already visible, then just start the delayed dismiss and unregister any
            // existing input consumers from the previous drag
@@ -358,7 +359,7 @@ public class PipMenuView extends FrameLayout {
        if (mMenuState != MENU_STATE_NONE) {
            cancelDelayedHide();
            if (notifyMenuVisibility) {
                notifyMenuStateChange(MENU_STATE_NONE, resize, null);
                notifyMenuStateChangeStart(MENU_STATE_NONE, resize, null);
            }
            mMenuContainerAnimator = new AnimatorSet();
            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -377,6 +378,9 @@ public class PipMenuView extends FrameLayout {
                @Override
                public void onAnimationEnd(Animator animation) {
                    setVisibility(GONE);
                    if (notifyMenuVisibility) {
                        notifyMenuStateChangeFinish(MENU_STATE_NONE);
                    }
                    if (animationFinishedRunnable != null) {
                        animationFinishedRunnable.run();
                    }
@@ -405,11 +409,11 @@ public class PipMenuView extends FrameLayout {
        mActions.clear();
        mActions.addAll(actions);
        if (mMenuState == MENU_STATE_FULL) {
            updateActionViews(stackBounds);
            updateActionViews(mMenuState, stackBounds);
        }
    }

    private void updateActionViews(Rect stackBounds) {
    private void updateActionViews(int menuState, Rect stackBounds) {
        ViewGroup expandContainer = findViewById(R.id.expand_container);
        ViewGroup actionsContainer = findViewById(R.id.actions_container);
        actionsContainer.setOnTouchListener((v, ev) -> {
@@ -418,13 +422,13 @@ public class PipMenuView extends FrameLayout {
        });

        // Update the expand button only if it should show with the menu
        expandContainer.setVisibility(mMenuState == MENU_STATE_FULL
        expandContainer.setVisibility(menuState == MENU_STATE_FULL
                ? View.VISIBLE
                : View.INVISIBLE);

        FrameLayout.LayoutParams expandedLp =
                (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
        if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) {
            actionsContainer.setVisibility(View.INVISIBLE);

            // Update the expand container margin to adjust the center of the expand button to
@@ -494,9 +498,13 @@ public class PipMenuView extends FrameLayout {
        expandContainer.requestLayout();
    }

    private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
    private void notifyMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
        mController.onMenuStateChangeStart(menuState, resize, callback);
    }

    private void notifyMenuStateChangeFinish(int menuState) {
        mMenuState = menuState;
        mController.onMenuStateChanged(menuState, resize, callback);
        mController.onMenuStateChangeFinish(menuState);
    }

    private void expandPip() {
Loading