blob: f5a746f7192837100af09b4139f8a4a30da57475 [file] [log] [blame]
package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
/**
* A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
* This class should be extended so that subclasses can define the reveal shape as the
* animation progresses from 0 to 1.
*/
public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
protected Rect mOutline;
protected float mOutlineRadius;
public RevealOutlineAnimation() {
mOutline = new Rect();
}
/** Returns whether elevation should be removed for the duration of the reveal animation. */
abstract boolean shouldRemoveElevationDuringAnimation();
/** Sets the progress, from 0 to 1, of the reveal animation. */
abstract void setProgress(float progress);
/**
* @see #createRevealAnimator(View, boolean, float) where startProgress is set to 0.
*/
public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
return createRevealAnimator(revealView, isReversed, 0f /* startProgress */);
}
/**
* Animates the given View's ViewOutline according to {@link #setProgress(float)}.
* @param revealView The View whose outline we are animating.
* @param isReversed Whether we are hiding rather than revealing the View.
* @param startProgress The progress at which to start the newly created animation. Useful if
* the previous reveal animation was cancelled and we want to create a new animation where it
* left off. Note that if isReversed=true, we start at 1 - startProgress (and go to 0).
* @return The Animator, which the caller must start.
*/
public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed,
float startProgress) {
ValueAnimator va = isReversed
? ValueAnimator.ofFloat(1f - startProgress, 0f)
: ValueAnimator.ofFloat(startProgress, 1f);
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
private boolean mIsClippedToOutline;
private ViewOutlineProvider mOldOutlineProvider;
public void onAnimationStart(Animator animation) {
mIsClippedToOutline = revealView.getClipToOutline();
mOldOutlineProvider = revealView.getOutlineProvider();
revealView.setOutlineProvider(RevealOutlineAnimation.this);
revealView.setClipToOutline(true);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(-elevation);
}
}
public void onAnimationEnd(Animator animation) {
revealView.setOutlineProvider(mOldOutlineProvider);
revealView.setClipToOutline(mIsClippedToOutline);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(0);
}
}
});
va.addUpdateListener(v -> {
float progress = (Float) v.getAnimatedValue();
setProgress(progress);
revealView.invalidateOutline();
});
return va;
}
@Override
public void getOutline(View v, Outline outline) {
outline.setRoundRect(mOutline, mOutlineRadius);
}
public float getRadius() {
return mOutlineRadius;
}
public void getOutline(Rect out) {
out.set(mOutline);
}
}