diff options
6 files changed, 106 insertions, 15 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index ea7e9685dd92..06c1e68753e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -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));          }      } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 8aa093379ee7..94fe286de869 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -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. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index e1e072a7faad..83253c6006fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -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();      };      /** @@ -566,11 +569,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);      }      /** @@ -585,11 +597,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, @@ -671,6 +683,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 @@ -807,8 +829,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 */); @@ -817,7 +845,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. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java index 5b0ca1837a1c..d28204add0ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java @@ -146,8 +146,8 @@ public class PipResizeGestureHandler implements          mUpdateResizeBoundsCallback = (rect) -> {              mUserResizeBounds.set(rect);              // mMotionHelper.synchronizePinnedStackBounds(); -            mUpdateMovementBoundsRunnable.run();              mPipBoundsState.setBounds(rect); +            mUpdateMovementBoundsRunnable.run();              resetState();          };      } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index 53b80e8b7542..f387e72b3da6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java index 29272be6e9bd..a132796f4a84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java @@ -149,6 +149,12 @@ public class PipTransitionState {      @Nullable      private SurfaceControl mSwipePipToHomeOverlay; +    // +    // Scheduling-related state +    // +    @Nullable +    private Runnable mOnIdlePipTransitionStateRunnable; +      /**       * An interface to track state updates as we progress through PiP transitions.       */ @@ -197,6 +203,8 @@ public class PipTransitionState {              mState = state;              dispatchPipTransitionStateChanged(prevState, mState, extra);          } + +        maybeRunOnIdlePipTransitionStateCallback();      }      /** @@ -231,6 +239,29 @@ public class PipTransitionState {      }      /** +     * Schedule a callback to run when in a valid idle PiP state. +     * +     * <p>We only allow for one callback to be scheduled to avoid cases with multiple transitions +     * being scheduled. For instance, if user double taps and IME shows, this would +     * schedule a bounds change transition for IME appearing. But if some other transition would +     * want to animate PiP before the scheduled callback executes, we would rather want to replace +     * the existing callback with a new one, to avoid multiple animations +     * as soon as we are idle.</p> +     */ +    public void setOnIdlePipTransitionStateRunnable( +            @Nullable Runnable onIdlePipTransitionStateRunnable) { +        mOnIdlePipTransitionStateRunnable = onIdlePipTransitionStateRunnable; +        maybeRunOnIdlePipTransitionStateCallback(); +    } + +    private void maybeRunOnIdlePipTransitionStateCallback() { +        if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) { +            mOnIdlePipTransitionStateRunnable.run(); +            mOnIdlePipTransitionStateRunnable = null; +        } +    } + +    /**       * Adds a {@link PipTransitionStateChangedListener} for future PiP transition state updates.       */      public void addPipTransitionStateChangedListener(PipTransitionStateChangedListener listener) { @@ -318,6 +349,11 @@ public class PipTransitionState {          throw new IllegalStateException("Unknown state: " + state);      } +    public boolean isPipStateIdle() { +        // This needs to be a valid in-PiP state that isn't a transient state. +        return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS; +    } +      @Override      public String toString() {          return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",  |