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 
createRevealAnimator(final View revealView, boolean isReversed)29     public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
30         ValueAnimator va =
31                 isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
32         final float elevation = revealView.getElevation();
33 
34         va.addListener(new AnimatorListenerAdapter() {
35             private boolean mIsClippedToOutline;
36             private ViewOutlineProvider mOldOutlineProvider;
37 
38             public void onAnimationStart(Animator animation) {
39                 mIsClippedToOutline = revealView.getClipToOutline();
40                 mOldOutlineProvider = revealView.getOutlineProvider();
41 
42                 revealView.setOutlineProvider(RevealOutlineAnimation.this);
43                 revealView.setClipToOutline(true);
44                 if (shouldRemoveElevationDuringAnimation()) {
45                     revealView.setTranslationZ(-elevation);
46                 }
47             }
48 
49             public void onAnimationEnd(Animator animation) {
50                 revealView.setOutlineProvider(mOldOutlineProvider);
51                 revealView.setClipToOutline(mIsClippedToOutline);
52                 if (shouldRemoveElevationDuringAnimation()) {
53                     revealView.setTranslationZ(0);
54                 }
55             }
56 
57         });
58 
59         va.addUpdateListener(v -> {
60             float progress = (Float) v.getAnimatedValue();
61             setProgress(progress);
62             revealView.invalidateOutline();
63         });
64         return va;
65     }
66 
67     @Override
getOutline(View v, Outline outline)68     public void getOutline(View v, Outline outline) {
69         outline.setRoundRect(mOutline, mOutlineRadius);
70     }
71 
getRadius()72     public float getRadius() {
73         return mOutlineRadius;
74     }
75 
getOutline(Rect out)76     public void getOutline(Rect out) {
77         out.set(mOutline);
78     }
79 }
80