From b2f5a2f98526977bc82fcc407bd06df20f002e5a Mon Sep 17 00:00:00 2001 From: "jorgegil@google.com" Date: Thu, 29 Apr 2021 14:54:46 -0700 Subject: [PATCH] Synchronize PIP menu fade out with task dismiss This CL addreses two issues: 1. PipMenuView.dismissPip was previously fading out the PIP menu, waiting for that to complete, then notifying the task organizer to remove and fade out the window, and then it would fade the menu out again even though it was already hidden. This resulted in a quick flash because the menu and the window are not faded out in sync. 2. The menu fade out animation had a fixed duration of 150ms, meaning that even if it starting fading out at the same time as the PIP window, it would not finish at the same time because the PIP exit anim duration is longer. To fix both of them, the X button now hides the menu and starts dismissing the task at the same time (once), and the dismiss animation duration is equal to the task fade out animation duration. Previous: http://recall/-/e8RgaQ0dFlwFuh0c7GRpyy/g7XMbvBGMyAOKZMuFlYpW0 With CL: http://recall/-/e8RgaQ0dFlwFuh0c7GRpyy/g7XMbvBGMyAOKZMuFlYpW0 Bug: 182727923 Test: Tap X to dismiss PIP, no flashes seen Change-Id: If9bdda8d0fd8b819c00ec4a592d1b2441d642d00 --- .../pip/phone/PhonePipMenuController.java | 12 +-- .../wm/shell/pip/phone/PipMenuView.java | 77 ++++++++++++++----- .../wm/shell/pip/phone/PipMotionHelper.java | 6 +- .../pip/phone/PipResizeGestureHandler.java | 3 +- .../wm/shell/pip/phone/PipTouchHandler.java | 3 +- 5 files changed, 71 insertions(+), 30 deletions(-) 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 17cde731faa2a..9cf0b721cc487 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 @@ -18,6 +18,8 @@ package com.android.wm.shell.pip.phone; import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_HIDE; + import android.annotation.Nullable; import android.app.RemoteAction; import android.content.Context; @@ -397,26 +399,26 @@ public class PhonePipMenuController implements PipMenuController { * Hides the menu view. */ public void hideMenu() { - hideMenu(true /* animate */, true /* resize */); + hideMenu(ANIM_TYPE_HIDE, true /* resize */); } /** * Hides the menu view. * - * @param animate whether to animate the menu fadeout + * @param animationType the animation type to use upon hiding the menu * @param resize whether or not to resize the PiP with the state change */ - public void hideMenu(boolean animate, boolean resize) { + public void hideMenu(@PipMenuView.AnimationType int animationType, boolean resize) { final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { Log.d(TAG, "hideMenu() state=" + mMenuState + " isMenuVisible=" + isMenuVisible - + " animate=" + animate + + " animationType=" + animationType + " resize=" + resize + " callers=\n" + Debug.getCallers(5, " ")); } if (isMenuVisible) { - mPipMenuView.hideMenu(animate, resize); + mPipMenuView.hideMenu(resize, animationType); } } 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 a57e8cdd09286..2b45346cfa5c0 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 @@ -32,6 +32,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.content.ComponentName; @@ -64,6 +65,8 @@ import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -74,9 +77,26 @@ public class PipMenuView extends FrameLayout { private static final String TAG = "PipMenuView"; + private static final int ANIMATION_NONE_DURATION_MS = 0; + private static final int ANIMATION_HIDE_DURATION_MS = 125; + + /** No animation performed during menu hide. */ + public static final int ANIM_TYPE_NONE = 0; + /** Fade out the menu until it's invisible. Used when the PIP window remains visible. */ + public static final int ANIM_TYPE_HIDE = 1; + /** Fade out the menu in sync with the PIP window. */ + public static final int ANIM_TYPE_DISMISS = 2; + + @IntDef(prefix = { "ANIM_TYPE_" }, value = { + ANIM_TYPE_NONE, + ANIM_TYPE_HIDE, + ANIM_TYPE_DISMISS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} + private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; - private static final long MENU_FADE_DURATION = 125; private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30; private static final float MENU_BACKGROUND_ALPHA = 0.3f; @@ -87,6 +107,7 @@ public class PipMenuView extends FrameLayout { private int mMenuState; private boolean mAllowMenuTimeout = true; private boolean mAllowTouches = true; + private int mDismissFadeOutDurationMs; private final List mActions = new ArrayList<>(); @@ -167,6 +188,8 @@ public class PipMenuView extends FrameLayout { mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext); mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, mResizeHandle, mSettingsButton, mDismissButton); + mDismissFadeOutDurationMs = context.getResources() + .getInteger(R.integer.config_pipExitAnimationDuration); initAccessibility(); } @@ -258,7 +281,7 @@ public class PipMenuView extends FrameLayout { mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim); } mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); - mMenuContainerAnimator.setDuration(MENU_FADE_DURATION); + mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS); if (allowMenuTimeout) { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -320,17 +343,18 @@ public class PipMenuView extends FrameLayout { hideMenu(null); } - void hideMenu(boolean animate, boolean resize) { - hideMenu(null, true /* notifyMenuVisibility */, animate, resize); - } - void hideMenu(Runnable animationEndCallback) { - hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */, - true /* resize */); + hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* resize */, + ANIM_TYPE_HIDE); } - private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, - boolean animate, boolean resize) { + void hideMenu(boolean resize, @AnimationType int animationType) { + hideMenu(null /* animationFinishedRunnable */, true /* notifyMenuVisibility */, resize, + animationType); + } + + void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, + boolean resize, @AnimationType int animationType) { if (mMenuState != MENU_STATE_NONE) { cancelDelayedHide(); if (notifyMenuVisibility) { @@ -348,7 +372,7 @@ public class PipMenuView extends FrameLayout { mResizeHandle.getAlpha(), 0f); mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT); - mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0); + mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType)); mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -478,19 +502,17 @@ public class PipMenuView extends FrameLayout { private void expandPip() { // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message - hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */, - true /* resize */); + hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* resize */, + ANIM_TYPE_HIDE); } private void dismissPip() { - // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler, - // we want to disable animating the fadeout animation of the buttons in order to call on - // PipTouchHandler#onPipDismiss fast enough. - final boolean animate = mMenuState != MENU_STATE_CLOSE; - // Do not notify menu visibility when hiding the menu, the controller will do this when it - // handles the message - hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate, - true /* resize */); + if (mMenuState != MENU_STATE_NONE) { + // Do not call hideMenu() directly. Instead, let the menu controller handle it just as + // any other dismissal that will update the touch state and fade out the PIP task + // and the menu view at the same time. + mController.onPipDismiss(); + } } private void showSettings() { @@ -514,4 +536,17 @@ public class PipMenuView extends FrameLayout { mMainExecutor.removeCallbacks(mHideMenuRunnable); mMainExecutor.executeDelayed(mHideMenuRunnable, recommendedTimeout); } + + private long getFadeOutDuration(@AnimationType int animationType) { + switch (animationType) { + case ANIM_TYPE_NONE: + return ANIMATION_NONE_DURATION_MS; + case ANIM_TYPE_HIDE: + return ANIMATION_HIDE_DURATION_MS; + case ANIM_TYPE_DISMISS: + return mDismissFadeOutDurationMs; + default: + throw new IllegalStateException("Invalid animation type " + animationType); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 10625617a379e..7320d5b815fdf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -22,6 +22,8 @@ import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_DISMISS; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -336,7 +338,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, + " callers=\n" + Debug.getCallers(5, " ")); } cancelPhysicsAnimation(); - mMenuController.hideMenu(false /* animate */, false /* resize */); + mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */); mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION); } @@ -349,7 +351,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, Log.d(TAG, "removePip: callers=\n" + Debug.getCallers(5, " ")); } cancelPhysicsAnimation(); - mMenuController.hideMenu(true /* animate*/, false /* resize */); + mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */); mPipTaskOrganizer.removePip(); } 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 c2ec1c55a59f5..8726ee76d29ab 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 @@ -21,6 +21,7 @@ import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE; import android.content.Context; import android.content.res.Resources; @@ -471,7 +472,7 @@ public class PipResizeGestureHandler { } if (mThresholdCrossed) { if (mPhonePipMenuController.isMenuVisible()) { - mPhonePipMenuController.hideMenu(false /* animate */, + mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */); } final Rect currentPipBounds = mPipBoundsState.getBounds(); 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 6d96312ad962d..0a0798ef24c12 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 @@ -25,6 +25,7 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE; import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE; import android.annotation.NonNull; import android.annotation.SuppressLint; @@ -881,7 +882,7 @@ public class PipTouchHandler { && mPipBoundsState.getBounds().height() < mPipBoundsState.getMaxSize().y; if (mMenuController.isMenuVisible()) { - mMenuController.hideMenu(false /* animate */, false /* resize */); + mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */); } if (toExpand) { mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());