1 package com.android.launcher3.anim;
2 
3 import android.animation.Animator;
4 import android.animation.AnimatorListenerAdapter;
5 import android.animation.ValueAnimator;
6 import android.graphics.Outline;
7 import android.graphics.Rect;
8 import android.view.View;
9 import android.view.ViewOutlineProvider;
10 
11 /**
12  * A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
13  * This class should be extended so that subclasses can define the reveal shape as the
14  * animation progresses from 0 to 1.
15  */
16 public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
17     protected Rect mOutline;
18     protected float mOutlineRadius;
19 
RevealOutlineAnimation()20     public RevealOutlineAnimation() {
21         mOutline = new Rect();
22     }
23 
24     /** Returns whether elevation should be removed for the duration of the reveal animation. */
shouldRemoveElevationDuringAnimation()25     abstract boolean shouldRemoveElevationDuringAnimation();
26     /** Sets the progress, from 0 to 1, of the reveal animation. */
setProgress(float progress)27     abstract void setProgress(float progress);
28 
29     /**
30      * @see #createRevealAnimator(View, boolean, float) where startProgress is set to 0.
31      */
createRevealAnimator(final View revealView, boolean isReversed)32     public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
33         return createRevealAnimator(revealView, isReversed, 0f /* startProgress */);
34     }
35 
36     /**
37      * Animates the given View's ViewOutline according to {@link #setProgress(float)}.
38      * @param revealView The View whose outline we are animating.
39      * @param isReversed Whether we are hiding rather than revealing the View.
40      * @param startProgress The progress at which to start the newly created animation. Useful if
41      * the previous reveal animation was cancelled and we want to create a new animation where it
42      * left off. Note that if isReversed=true, we start at 1 - startProgress (and go to 0).
43      * @return The Animator, which the caller must start.
44      */
createRevealAnimator(final View revealView, boolean isReversed, float startProgress)45     public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed,
46             float startProgress) {
47         ValueAnimator va = isReversed
48                 ? ValueAnimator.ofFloat(1f - startProgress, 0f)
49                 : ValueAnimator.ofFloat(startProgress, 1f);
50         final float elevation = revealView.getElevation();
51 
52         va.addListener(new AnimatorListenerAdapter() {
53             private boolean mIsClippedToOutline;
54             private ViewOutlineProvider mOldOutlineProvider;
55 
56             public void onAnimationStart(Animator animation) {
57                 mIsClippedToOutline = revealView.getClipToOutline();
58                 mOldOutlineProvider = revealView.getOutlineProvider();
59 
60                 revealView.setOutlineProvider(RevealOutlineAnimation.this);
61                 revealView.setClipToOutline(true);
62                 if (shouldRemoveElevationDuringAnimation()) {
63                     revealView.setTranslationZ(-elevation);
64                 }
65             }
66 
67             public void onAnimationEnd(Animator animation) {
68                 revealView.setOutlineProvider(mOldOutlineProvider);
69                 revealView.setClipToOutline(mIsClippedToOutline);
70                 if (shouldRemoveElevationDuringAnimation()) {
71                     revealView.setTranslationZ(0);
72                 }
73             }
74 
75         });
76 
77         va.addUpdateListener(v -> {
78             float progress = (Float) v.getAnimatedValue();
79             setProgress(progress);
80             revealView.invalidateOutline();
81         });
82         return va;
83     }
84 
85     @Override
getOutline(View v, Outline outline)86     public void getOutline(View v, Outline outline) {
87         outline.setRoundRect(mOutline, mOutlineRadius);
88     }
89 
getRadius()90     public float getRadius() {
91         return mOutlineRadius;
92     }
93 
getOutline(Rect out)94     public void getOutline(Rect out) {
95         out.set(mOutline);
96     }
97 }
98