diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt index 0da70a9249..428bd95159 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt +++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt @@ -75,6 +75,7 @@ class TaskMenuViewWithArrow : ArrowPopup { clipToOutline = true shouldScaleArrow = true + mIsArrowRotated = true // This synchronizes the arrow and menu to open at the same time OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index c23ea8aa4f..a0f21dc297 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -20,6 +20,8 @@ import static androidx.core.content.ContextCompat.getColorStateList; import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE; import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP; @@ -37,11 +39,13 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.util.Pair; +import android.util.Property; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import androidx.annotation.Nullable; @@ -81,6 +85,19 @@ public abstract class ArrowPopup protected int CLOSE_CHILD_FADE_START_DELAY = 0; protected int CLOSE_CHILD_FADE_DURATION = 140; + private static final int OPEN_DURATION_U = 200; + private static final int OPEN_FADE_START_DELAY_U = 0; + private static final int OPEN_FADE_DURATION_U = 83; + private static final int OPEN_CHILD_FADE_START_DELAY_U = 0; + private static final int OPEN_CHILD_FADE_DURATION_U = 83; + private static final int OPEN_OVERSHOOT_DURATION_U = 200; + + private static final int CLOSE_DURATION_U = 233; + private static final int CLOSE_FADE_START_DELAY_U = 150; + private static final int CLOSE_FADE_DURATION_U = 83; + private static final int CLOSE_CHILD_FADE_START_DELAY_U = 150; + private static final int CLOSE_CHILD_FADE_DURATION_U = 83; + protected final Rect mTempRect = new Rect(); protected final LayoutInflater mInflater; @@ -104,6 +121,7 @@ public abstract class ArrowPopup protected AnimatorSet mOpenCloseAnimator; protected boolean mDeferContainerRemoval; protected boolean shouldScaleArrow = false; + protected boolean mIsArrowRotated = false; private final GradientDrawable mRoundedTop; private final GradientDrawable mRoundedBottom; @@ -591,10 +609,24 @@ public abstract class ArrowPopup protected void animateOpen() { setVisibility(View.VISIBLE); + mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get() + ? getMaterialUOpenCloseAnimator( + true, + OPEN_DURATION_U, + OPEN_FADE_START_DELAY_U, + OPEN_FADE_DURATION_U, + OPEN_CHILD_FADE_START_DELAY_U, + OPEN_CHILD_FADE_DURATION_U, + EMPHASIZED_DECELERATE) + : getOpenCloseAnimator( + true, + OPEN_DURATION, + OPEN_FADE_START_DELAY, + OPEN_FADE_DURATION, + OPEN_CHILD_FADE_START_DELAY, + OPEN_CHILD_FADE_DURATION, + DECELERATED_EASE); - mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY, - OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION, - DECELERATED_EASE); onCreateOpenAnimation(mOpenCloseAnimator); mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -680,6 +712,24 @@ public abstract class ArrowPopup mOpenCloseAnimator = getOpenCloseAnimator(false, CLOSE_DURATION, CLOSE_FADE_START_DELAY, CLOSE_FADE_DURATION, CLOSE_CHILD_FADE_START_DELAY, CLOSE_CHILD_FADE_DURATION, ACCELERATED_EASE); + + mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get() + ? getMaterialUOpenCloseAnimator( + false, + CLOSE_DURATION_U, + CLOSE_FADE_START_DELAY_U, + CLOSE_FADE_DURATION_U, + CLOSE_CHILD_FADE_START_DELAY_U, + CLOSE_CHILD_FADE_DURATION_U, + EMPHASIZED_ACCELERATE) + : getOpenCloseAnimator(false, + CLOSE_DURATION, + CLOSE_FADE_START_DELAY, + CLOSE_FADE_DURATION, + CLOSE_CHILD_FADE_START_DELAY, + CLOSE_CHILD_FADE_DURATION, + ACCELERATED_EASE); + onCreateCloseAnimation(mOpenCloseAnimator); mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -695,6 +745,59 @@ public abstract class ArrowPopup mOpenCloseAnimator.start(); } + protected AnimatorSet getMaterialUOpenCloseAnimator(boolean isOpening, int scaleDuration, + int fadeStartDelay, int fadeDuration, int childFadeStartDelay, int childFadeDuration, + Interpolator interpolator) { + + int arrowCenter = mArrowOffsetHorizontal + mArrowWidth / 2; + if (mIsArrowRotated) { + setPivotX(mIsLeftAligned ? 0f : getMeasuredWidth()); + setPivotY(arrowCenter); + } else { + setPivotX(mIsLeftAligned ? arrowCenter : getMeasuredWidth() - arrowCenter); + setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0f); + } + + float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0}; + float[] scaleValues = isOpening ? new float[] {0.5f, 1.02f} : new float[] {1f, 0.5f}; + Animator alpha = getAnimatorOfFloat(this, View.ALPHA, fadeDuration, fadeStartDelay, + LINEAR, alphaValues); + Animator arrowAlpha = getAnimatorOfFloat(mArrow, View.ALPHA, fadeDuration, fadeStartDelay, + LINEAR, alphaValues); + Animator scaleY = getAnimatorOfFloat(this, View.SCALE_Y, scaleDuration, 0, interpolator, + scaleValues); + Animator scaleX = getAnimatorOfFloat(this, View.SCALE_X, scaleDuration, 0, interpolator, + scaleValues); + + final AnimatorSet animatorSet = new AnimatorSet(); + if (isOpening) { + float[] scaleValuesOvershoot = new float[] {1.02f, 1f}; + PathInterpolator overshootInterpolator = new PathInterpolator(0.3f, 0, 0.33f, 1f); + Animator overshootY = getAnimatorOfFloat(this, View.SCALE_Y, + OPEN_OVERSHOOT_DURATION_U, scaleDuration, overshootInterpolator, + scaleValuesOvershoot); + Animator overshootX = getAnimatorOfFloat(this, View.SCALE_X, + OPEN_OVERSHOOT_DURATION_U, scaleDuration, overshootInterpolator, + scaleValuesOvershoot); + + animatorSet.playTogether(alpha, arrowAlpha, scaleY, scaleX, overshootX, overshootY); + } else { + animatorSet.playTogether(alpha, arrowAlpha, scaleY, scaleX); + } + + fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet); + return animatorSet; + } + + private Animator getAnimatorOfFloat(View view, Property property, + int duration, int startDelay, Interpolator interpolator, float... values) { + Animator animator = ObjectAnimator.ofFloat(view, property, values); + animator.setDuration(duration); + animator.setInterpolator(interpolator); + animator.setStartDelay(startDelay); + return animator; + } + /** * Called when creating the open transition allowing subclass can add additional animations. */