mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-04 09:56:49 +00:00
Add spring to shelf for home <-> overview <-> all apps state transitions.
Added new SpringObjectAnimator class that wraps an ObjectAnimator so the Object can be controlled via the Animator or via a SpringAnimation. It extends ValueAnimator so that it remains compatible with AnimatorPlaybackController. Code is behind feature flag toggle QUICKSTEP_SPRINGS. Bug: 111698021 Change-Id: I1b20179ede37e89a6a6bb2a45d407cc74c99ac4e
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package com.android.launcher3.anim;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.Animator.AnimatorListener;
|
||||
@@ -23,10 +24,16 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
|
||||
/**
|
||||
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
|
||||
@@ -37,6 +44,9 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
|
||||
|
||||
private static final String TAG = "AnimatorPlaybackCtrler";
|
||||
private static boolean DEBUG = false;
|
||||
|
||||
public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
|
||||
return wrap(anim, duration, null);
|
||||
}
|
||||
@@ -60,6 +70,7 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
private final long mDuration;
|
||||
|
||||
protected final AnimatorSet mAnim;
|
||||
private Set<SpringAnimation> mSprings;
|
||||
|
||||
protected float mCurrentFraction;
|
||||
private Runnable mEndAction;
|
||||
@@ -67,6 +78,9 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
protected boolean mTargetCancelled = false;
|
||||
protected Runnable mOnCancelRunnable;
|
||||
|
||||
private OnAnimationEndDispatcher mEndListener;
|
||||
private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
|
||||
|
||||
protected AnimatorPlaybackController(AnimatorSet anim, long duration,
|
||||
Runnable onCancelRunnable) {
|
||||
mAnim = anim;
|
||||
@@ -75,7 +89,8 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
|
||||
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
|
||||
mAnimationPlayer.setInterpolator(LINEAR);
|
||||
mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
|
||||
mEndListener = new OnAnimationEndDispatcher();
|
||||
mAnimationPlayer.addListener(mEndListener);
|
||||
mAnimationPlayer.addUpdateListener(this);
|
||||
|
||||
mAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -99,6 +114,15 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
mTargetCancelled = false;
|
||||
}
|
||||
});
|
||||
|
||||
mSprings = new HashSet<>();
|
||||
mSpringEndListener = (animation, canceled, value, velocity1) -> {
|
||||
if (canceled) {
|
||||
mEndListener.onAnimationCancel(mAnimationPlayer);
|
||||
} else {
|
||||
mEndListener.onAnimationEnd(mAnimationPlayer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AnimatorSet getTarget() {
|
||||
@@ -180,6 +204,29 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playback and sets the spring.
|
||||
*/
|
||||
public void dispatchOnStartWithVelocity(float end, float velocity) {
|
||||
if (!QUICKSTEP_SPRINGS.get()) {
|
||||
dispatchOnStart();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity);
|
||||
|
||||
for (Animator a : mAnim.getChildAnimations()) {
|
||||
if (a instanceof SpringObjectAnimator) {
|
||||
if (DEBUG) Log.d(TAG, "Found springAnimator=" + a);
|
||||
SpringObjectAnimator springAnimator = (SpringObjectAnimator) a;
|
||||
mSprings.add(springAnimator.getSpring());
|
||||
springAnimator.startSpring(end, velocity, mSpringEndListener);
|
||||
}
|
||||
}
|
||||
|
||||
dispatchOnStart();
|
||||
}
|
||||
|
||||
public void dispatchOnStart() {
|
||||
dispatchOnStartRecursively(mAnim);
|
||||
}
|
||||
@@ -282,6 +329,18 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnySpringRunning() {
|
||||
for (SpringAnimation spring : mSprings) {
|
||||
if (spring.isRunning()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only dispatches the on end actions once the animator and all springs have completed running.
|
||||
*/
|
||||
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
|
||||
|
||||
@Override
|
||||
@@ -291,9 +350,12 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
dispatchOnEndRecursively(mAnim);
|
||||
if (mEndAction != null) {
|
||||
mEndAction.run();
|
||||
// We wait for the spring (if any) to finish running before completing the end callback.
|
||||
if (mSprings.isEmpty() || !isAnySpringRunning()) {
|
||||
dispatchOnEndRecursively(mAnim);
|
||||
if (mEndAction != null) {
|
||||
mEndAction.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user