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

Commit be1fd970 authored by Ikram Gabiyev's avatar Ikram Gabiyev
Browse files

Adjust PiP bounds upon IME visibility change

We are implementing two CUJs:

1. If IME is occluded by PiP, change the PiP bounds
2. If PiP hasn't been interacted with, IME becoming
invisible should return PiP to original bounds.

We are also now caching the movement bounds in PipBoundsState,
which acts a source of truth for this.

We are also implementing a scheduling mechanism in PipTransitionState
waiting for a valid PiP idle state to run a new transition for example.
This is helpful for dealing with trigger paths from outside
PipComponent.

Bug: 352596856
Flag: com.android.wm.shell.enable_pip2_implementation
Test: atest ImeListenerTest
Test: enter PiP and open an IME
Change-Id: I27fa96bddd2181df0957e7d83edd52457c54d6d8
parent fc197f12
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ public abstract class Pip2Module {
            TaskStackListenerImpl taskStackListener,
            ShellTaskOrganizer shellTaskOrganizer,
            PipTransitionState pipTransitionState,
            PipTouchHandler pipTouchHandler,
            @ShellMainThread ShellExecutor mainExecutor) {
        if (!PipUtils.isPip2ExperimentEnabled()) {
            return Optional.empty();
@@ -112,7 +113,7 @@ public abstract class Pip2Module {
                    context, shellInit, shellCommandHandler, shellController, displayController,
                    displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                    pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                    pipTransitionState, mainExecutor));
                    pipTransitionState, pipTouchHandler, mainExecutor));
        }
    }

+8 −2
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ public class PipController implements ConfigurationChangeListener,
    private final TaskStackListenerImpl mTaskStackListener;
    private final ShellTaskOrganizer mShellTaskOrganizer;
    private final PipTransitionState mPipTransitionState;
    private final PipTouchHandler mPipTouchHandler;
    private final ShellExecutor mMainExecutor;
    private final PipImpl mImpl;
    private Consumer<Boolean> mOnIsInPipStateChangedListener;
@@ -130,6 +131,7 @@ public class PipController implements ConfigurationChangeListener,
            TaskStackListenerImpl taskStackListener,
            ShellTaskOrganizer shellTaskOrganizer,
            PipTransitionState pipTransitionState,
            PipTouchHandler pipTouchHandler,
            ShellExecutor mainExecutor) {
        mContext = context;
        mShellCommandHandler = shellCommandHandler;
@@ -144,6 +146,7 @@ public class PipController implements ConfigurationChangeListener,
        mShellTaskOrganizer = shellTaskOrganizer;
        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this);
        mPipTouchHandler = pipTouchHandler;
        mMainExecutor = mainExecutor;
        mImpl = new PipImpl();

@@ -168,6 +171,7 @@ public class PipController implements ConfigurationChangeListener,
            TaskStackListenerImpl taskStackListener,
            ShellTaskOrganizer shellTaskOrganizer,
            PipTransitionState pipTransitionState,
            PipTouchHandler pipTouchHandler,
            ShellExecutor mainExecutor) {
        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -177,7 +181,7 @@ public class PipController implements ConfigurationChangeListener,
        return new PipController(context, shellInit, shellCommandHandler, shellController,
                displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                pipTransitionState, mainExecutor);
                pipTransitionState, pipTouchHandler, mainExecutor);
    }

    public PipImpl getPipImpl() {
@@ -204,7 +208,9 @@ public class PipController implements ConfigurationChangeListener,
        mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
                    @Override
                    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
                    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
                        mPipTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
                    }
                });

        // Allow other outside processes to bind to PiP controller using the key below.
+39 −11
Original line number Diff line number Diff line
@@ -134,6 +134,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
            new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_NO_BOUNCY);

    @Nullable private Runnable mUpdateMovementBoundsRunnable;

    private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
        if (mPipBoundsState.getBounds().equals(newBounds)) {
            return;
@@ -141,6 +143,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

        mMenuController.updateMenuLayout(newBounds);
        mPipBoundsState.setBounds(newBounds);
        maybeUpdateMovementBounds();
    };

    /**
@@ -555,11 +558,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                            + " callers=\n%s", TAG, originalBounds, offset,
                    Debug.getCallers(5, "    "));
        }
        if (offset == 0) {
            return;
        }

        cancelPhysicsAnimation();
        /*
        mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
                mUpdateBoundsCallback);
         */

        Rect adjustedBounds = new Rect(originalBounds);
        adjustedBounds.offset(0, offset);

        setAnimatingToBounds(adjustedBounds);
        Bundle extra = new Bundle();
        extra.putBoolean(ANIMATING_BOUNDS_CHANGE, true);
        extra.putInt(ANIMATING_BOUNDS_CHANGE_DURATION, SHIFT_DURATION);
        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
    }

    /**
@@ -574,11 +586,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    /** Set new fling configs whose min/max values respect the given movement bounds. */
    private void rebuildFlingConfigs() {
        mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).left,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).right);
                mPipBoundsState.getMovementBounds().left,
                mPipBoundsState.getMovementBounds().right);
        mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).top,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom);
                mPipBoundsState.getMovementBounds().top,
                mPipBoundsState.getMovementBounds().bottom);
        final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
        mStashConfigX = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION,
@@ -660,6 +672,16 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        cleanUpHighPerfSessionMaybe();
    }

    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
    }

    private void maybeUpdateMovementBounds() {
        if (mUpdateMovementBoundsRunnable != null)  {
            mUpdateMovementBoundsRunnable.run();
        }
    }

    /**
     * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
     * we return these bounds from
@@ -796,8 +818,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
                destinationBounds, duration, 0f /* angle */);
        animator.setAnimationEndCallback(() -> {
            mPipBoundsState.setBounds(destinationBounds);
            // All motion operations have actually finished, so make bounds cache updates.
            mUpdateBoundsCallback.accept(destinationBounds);

            // In case an ongoing drag/fling was present before a deterministic resize transition
            // kicked in, we need to update the update bounds properly before cleaning in-motion
            // state.
            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(destinationBounds);
            settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);

            cleanUpHighPerfSessionMaybe();
            // Signal that we are done with resize transition
            mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
@@ -806,7 +834,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    }

    private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
        if (!animatingAfter) {
        if (!animatingAfter && mPipBoundsState.getMotionBoundsState().isInMotion()) {
            // The physics animation ended, though we may not necessarily be done animating, such as
            // when we're still dragging after moving out of the magnetic target. Only set the final
            // bounds state and clear motion bounds completely if the whole animation is over.
+1 −1
Original line number Diff line number Diff line
@@ -146,8 +146,8 @@ public class PipResizeGestureHandler implements
        mUpdateResizeBoundsCallback = (rect) -> {
            mUserResizeBounds.set(rect);
            // mMotionHelper.synchronizePinnedStackBounds();
            mUpdateMovementBoundsRunnable.run();
            mPipBoundsState.setBounds(rect);
            mUpdateMovementBoundsRunnable.run();
            resetState();
        };
    }
+20 −0
Original line number Diff line number Diff line
@@ -199,6 +199,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
        mMenuController.addListener(new PipMenuListener());
        mGesture = new DefaultPipTouchGesture();
        mMotionHelper = pipMotionHelper;
        mMotionHelper.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
        mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
                mMotionHelper, mainExecutor);
        mTouchState = new PipTouchState(ViewConfiguration.get(context),
@@ -317,6 +318,8 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
        mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
        mPipResizeGestureHandler.onActivityUnpinned();
        mPipInputConsumer.unregisterInputConsumer();
        mPipBoundsState.setHasUserMovedPip(false);
        mPipBoundsState.setHasUserResizedPip(false);
    }

    void onPinnedStackAnimationEnded(
@@ -346,6 +349,22 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
    void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
        mIsImeShowing = imeVisible;
        mImeHeight = imeHeight;

        // Cache new movement bounds using the new potential IME height.
        updateMovementBounds();

        mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
            int delta = mPipBoundsState.getMovementBounds().bottom
                    - mPipBoundsState.getBounds().top;

            boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
                    || mPipBoundsState.hasUserResizedPip());
            if ((imeVisible && delta < 0) || (!imeVisible && !hasUserInteracted)) {
                // The policy is to ignore an IME disappearing if user has interacted with PiP.
                // Otherwise, only offset due to an appearing IME if PiP occludes it.
                mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
            }
        });
    }

    void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
@@ -1077,6 +1096,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
        switch (newState) {
            case PipTransitionState.ENTERED_PIP:
                onActivityPinned();
                updateMovementBounds();
                mTouchState.setAllowInputEvents(true);
                mTouchState.reset();
                break;
Loading