diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index ca05ff47d5071..17e0d1bc90185 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -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 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 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,7 +532,9 @@ public class PipAnimationController { getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialSourceValue, bounds, insets); } - tx.apply(); + if (!handlePipTransaction(leash, tx, bounds)) { + tx.apply(); + } } private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 4ce6c9e35e9ee..0633330dc8303 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -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) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 9b6909b2bd512..4759550c35c01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -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: " diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 9cf0b721cc487..052653e0cc818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -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; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 2b45346cfa5c0..bed6b420960bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -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) { + 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() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 8726ee76d29ab..0878f54b7d274 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -419,13 +419,13 @@ public class PipResizeGestureHandler { // Reset the down to begin resizing from this point mDownPoint.set(mLastPoint); mDownSecondPoint.set(mLastSecondPoint); - } - if (mThresholdCrossed) { if (mPhonePipMenuController.isMenuVisible()) { mPhonePipMenuController.hideMenu(); } + } + if (mThresholdCrossed) { mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint, mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize, mDownBounds, mLastResizeBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 0a0798ef24c12..50f170a7ca861 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -129,8 +129,13 @@ public class PipTouchHandler { */ private class PipMenuListener implements PhonePipMenuController.Listener { @Override - public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) { - setMenuState(menuState, resize, callback); + public void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { + PipTouchHandler.this.onPipMenuStateChangeStart(menuState, resize, callback); + } + + @Override + public void onPipMenuStateChangeFinish(int menuState) { + setMenuState(menuState); } @Override @@ -646,9 +651,9 @@ public class PipTouchHandler { } /** - * Sets the menu visibility. + * Called when the PiP menu state is in the process of animating/changing from one to another. */ - private void setMenuState(int menuState, boolean resize, Runnable callback) { + private void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { if (mMenuState == menuState && !resize) { return; } @@ -686,6 +691,9 @@ public class PipTouchHandler { mSavedSnapFraction = -1f; } } + } + + private void setMenuState(int menuState) { mMenuState = menuState; updateMovementBounds(); // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip