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

Commit 94e43bd7 authored by Jon Miranda's avatar Jon Miranda
Browse files

New motion for ArrowPopup open/close.

Bug: 175329686
Test: long press on apps where container opens above & below
      and where container is aligned left & right

Change-Id: I09bbf57b6e19cc6f022b9a96fb48d8c4ae080813
parent c9744631
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -77,11 +77,6 @@
    <!-- Menu id for feature flags -->
    <item type="id" name="menu_apply_flags" />

    <!-- Popup items -->
    <integer name="config_popupOpenCloseDuration">150</integer>
    <integer name="config_popupArrowOpenCloseDuration">40</integer>
    <integer name="config_removeNotificationViewDuration">300</integer>

    <!-- Default packages -->
    <string name="wallpaper_picker_package" translatable="false"></string>
    <string name="local_colors_extraction_class" translatable="false"></string>
+3 −0
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ public class Interpolators {
    public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
    public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);

    public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
    public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);

    public static final Interpolator EXAGGERATED_EASE;

    public static final Interpolator INSTANT = t -> 1;
+73 −97
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.launcher3.popup;

import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
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.LINEAR;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;

import android.animation.Animator;
@@ -24,7 +26,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -42,6 +43,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
@@ -51,13 +53,10 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -77,6 +76,19 @@ import java.util.HashMap;
public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
        extends AbstractFloatingView {

    // Duration values (ms) for popup open and close animations.
    private static final int OPEN_DURATION = 276;
    private static final int OPEN_FADE_START_DELAY = 0;
    private static final int OPEN_FADE_DURATION = 38;
    private static final int OPEN_CHILD_FADE_START_DELAY = 38;
    private static final int OPEN_CHILD_FADE_DURATION = 76;

    private static final int CLOSE_DURATION = 200;
    private static final int CLOSE_FADE_START_DELAY = 140;
    private static final int CLOSE_FADE_DURATION = 50;
    private static final int CLOSE_CHILD_FADE_START_DELAY = 0;
    private static final int CLOSE_CHILD_FADE_DURATION = 140;

    // +1 for system shortcut view
    private static final int MAX_NUM_CHILDREN = MAX_SHORTCUTS + 1;
    // Index used to get background color when using local wallpaper color extraction,
@@ -103,10 +115,8 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
    protected boolean mIsAboveIcon;
    private int mGravity;

    protected Animator mOpenCloseAnimator;
    protected AnimatorSet mOpenCloseAnimator;
    protected boolean mDeferContainerRemoval;
    private final Rect mStartRect = new Rect();
    private final Rect mEndRect = new Rect();

    private final GradientDrawable mRoundedTop;
    private final GradientDrawable mRoundedBottom;
@@ -557,48 +567,13 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
        return getChildCount() > 0 ? getChildAt(0) : this;
    }

    private int getArrowDuration() {
        return shouldAddArrow()
                ? getResources().getInteger(R.integer.config_popupArrowOpenCloseDuration)
                : 0;
    }

    private void animateOpen() {
        setVisibility(View.VISIBLE);

        final AnimatorSet openAnim = new AnimatorSet();
        final Resources res = getResources();
        final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
        final long arrowDuration = getArrowDuration();
        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;

        // Rectangular reveal.
        mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
                .createRevealAnimator(this, false);
        revealAnim.setDuration(revealDuration);
        revealAnim.setInterpolator(revealInterpolator);
        // Clip the popup to the initial outline while the notification dot and arrow animate.
        revealAnim.start();
        revealAnim.pause();

        ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);
        fadeIn.setDuration(revealDuration + arrowDuration);
        fadeIn.setInterpolator(revealInterpolator);
        fadeIn.addUpdateListener(anim -> {
            float alpha = (float) anim.getAnimatedValue();
            mArrow.setAlpha(alpha);
            setAlpha(revealAnim.isStarted() ? alpha : 0);
        });
        openAnim.play(fadeIn);

        // Animate the arrow.
        mArrow.setScaleX(0);
        mArrow.setScaleY(0);
        Animator arrowScale = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
                .setDuration(arrowDuration);

        openAnim.addListener(new AnimatorListenerAdapter() {
        mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY,
                OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION,
                DECELERATED_EASE);
        mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setAlpha(1f);
@@ -606,56 +581,68 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
                mOpenCloseAnimator = null;
            }
        });
        mOpenCloseAnimator.start();
    }

    private AnimatorSet getOpenCloseAnimator(boolean isOpening, int totalDuration,
            int fadeStartDelay, int fadeDuration, int childFadeStartDelay,
            int childFadeDuration, Interpolator interpolator) {
        final AnimatorSet openAnim = new AnimatorSet();
        float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
        float[] scaleValues = isOpening ? new float[] {0.5f, 1} : new float[] {1, 0.5f};

        ValueAnimator fade = ValueAnimator.ofFloat(alphaValues);
        fade.setStartDelay(fadeStartDelay);
        fade.setDuration(fadeDuration);
        fade.setInterpolator(LINEAR);
        fade.addUpdateListener(anim -> {
            float alpha = (float) anim.getAnimatedValue();
            mArrow.setAlpha(alpha);
            setAlpha(alpha);
        });
        openAnim.play(fade);

        setPivotX(mIsLeftAligned ? 0 : getMeasuredWidth());
        setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0);
        Animator scale = ObjectAnimator.ofFloat(this, View.SCALE_Y, scaleValues);
        scale.setDuration(totalDuration);
        scale.setInterpolator(interpolator);
        openAnim.play(scale);

        for (int i = getChildCount() - 1; i >= 0; --i) {
            View view = getChildAt(i);
            if (view.getVisibility() == VISIBLE && view instanceof ViewGroup) {
                for (int j = ((ViewGroup) view).getChildCount() - 1; j >= 0; --j) {
                    View childView = ((ViewGroup) view).getChildAt(j);

        mOpenCloseAnimator = openAnim;
        openAnim.playSequentially(arrowScale, revealAnim);
        openAnim.start();
                    childView.setAlpha(alphaValues[0]);
                    ValueAnimator childFade = ObjectAnimator.ofFloat(childView, ALPHA, alphaValues);
                    childFade.setStartDelay(childFadeStartDelay);
                    childFade.setDuration(childFadeDuration);
                    childFade.setInterpolator(LINEAR);

                    openAnim.play(childFade);
                }
            }
        }
        return openAnim;
    }


    protected void animateClose() {
        if (!mIsOpen) {
            return;
        }
        if (getOutlineProvider() instanceof RevealOutlineAnimation) {
            ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
        } else {
            mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
        }
        if (mOpenCloseAnimator != null) {
            mOpenCloseAnimator.cancel();
        }
        mIsOpen = false;


        final AnimatorSet closeAnim = new AnimatorSet();
        final Resources res = getResources();
        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
        final long revealDuration = res.getInteger(R.integer.config_popupOpenCloseDuration);
        final long arrowDuration = getArrowDuration();

        // Hide the arrow
        Animator scaleArrow = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0)
                .setDuration(arrowDuration);

        // Rectangular reveal (reversed).
        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
                .createRevealAnimator(this, true);
        revealAnim.setDuration(revealDuration);
        revealAnim.setInterpolator(revealInterpolator);
        closeAnim.playSequentially(revealAnim, scaleArrow);

        ValueAnimator fadeOut = ValueAnimator.ofFloat(getAlpha(), 0);
        fadeOut.setDuration(revealDuration + arrowDuration);
        fadeOut.setInterpolator(revealInterpolator);
        fadeOut.addUpdateListener(anim -> {
            float alpha = (float) anim.getAnimatedValue();
            mArrow.setAlpha(alpha);
            setAlpha(scaleArrow.isStarted() ? 0 : alpha);
        });
        closeAnim.play(fadeOut);

        onCreateCloseAnimation(closeAnim);
        closeAnim.addListener(new AnimatorListenerAdapter() {
        mOpenCloseAnimator = 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
            public void onAnimationEnd(Animator animation) {
                mOpenCloseAnimator = null;
@@ -666,8 +653,7 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
                }
            }
        });
        mOpenCloseAnimator = closeAnim;
        closeAnim.start();
        mOpenCloseAnimator.start();
    }

    /**
@@ -675,16 +661,6 @@ public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
     */
    protected void onCreateCloseAnimation(AnimatorSet anim) { }

    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
        int arrowLeft = getArrowLeft();
        int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;

        mStartRect.set(arrowLeft, arrowCenterY, arrowLeft + mArrowWidth, arrowCenterY);

        return new RoundedRectRevealOutlineProvider(
                mArrowPointRadius, mOutlineRadius, mStartRect, mEndRect);
    }

    /**
     * Closes the popup without animation.
     */