From 7f9e8e2d2758d4bed14adc69d98f8fe2e1186a83 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 17 Apr 2020 12:05:21 -0700 Subject: [PATCH] Fixing animation player not overshooting spring animation; tuning springs => setCurrentPlayTime is bounded between [0, duration] by the animation framework Instead using interpolator so that we can go outside the bounds => Tune spring stiffness and dampening for overview card dismiss animations Bug: 154061408 Change-Id: Iaa31491fff499db916b36d9779ec159b8a89a2de --- .../LauncherAppTransitionManagerImpl.java | 5 +- .../util/StaggeredWorkspaceAnim.java | 5 +- res/values/config.xml | 8 +-- .../anim/AnimatorPlaybackController.java | 33 +++------ .../anim/SpringAnimationBuilder.java | 68 ++++++++++--------- 5 files changed, 54 insertions(+), 65 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 6c64bf7c75..8cf5da2144 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -199,13 +199,12 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti RecentsView.CONTENT_ALPHA, values); case INDEX_RECENTS_TRANSLATE_X_ANIM: // TODO: Do not assume motion across X axis for adjacent page - return new SpringAnimationBuilder<>( - mLauncher.getOverviewPanel(), ADJACENT_PAGE_OFFSET) + return new SpringAnimationBuilder(mLauncher) .setMinimumVisibleChange(1f / mLauncher.getOverviewPanel().getWidth()) .setDampingRatio(0.8f) .setStiffness(250) .setValues(values) - .build(mLauncher); + .build(mLauncher.getOverviewPanel(), ADJACENT_PAGE_OFFSET); case INDEX_PAUSE_TO_OVERVIEW_ANIM: { StateAnimationConfig config = new StateAnimationConfig(); config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index bde6f9af19..13c20f1800 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -28,6 +28,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.view.View; import android.view.ViewGroup; @@ -185,13 +186,13 @@ public class StaggeredWorkspaceAnim { ResourceProvider rp = DynamicResource.provider(v.getContext()); float stiffness = rp.getFloat(R.dimen.staggered_stiffness); float damping = rp.getFloat(R.dimen.staggered_damping_ratio); - ObjectAnimator springTransY = new SpringAnimationBuilder<>(v, VIEW_TRANSLATE_Y) + ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext()) .setStiffness(stiffness) .setDampingRatio(damping) .setMinimumVisibleChange(1f) .setEndValue(0) .setStartVelocity(mVelocity) - .build(v.getContext()); + .build(v, VIEW_TRANSLATE_Y); springTransY.setStartDelay(startDelay); mAnimators.play(springTransY); diff --git a/res/values/config.xml b/res/values/config.xml index 0657b86a6c..41bb909a1d 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -125,11 +125,11 @@ 0.75 600 - 0.5 - 1500 + 0.73 + 800 - 0.5 - 1500 + 0.73 + 800 0.8 400 diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index f12789a90f..e11917b273 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -27,7 +27,6 @@ import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; -import android.util.FloatProperty; import androidx.annotation.Nullable; @@ -64,19 +63,6 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL return new AnimatorPlaybackController(anim, duration, childAnims); } - private static final FloatProperty CURRENT_PLAY_TIME = - new FloatProperty("current-play-time") { - @Override - public void setValue(ValueAnimator animator, float v) { - animator.setCurrentPlayTime((long) v); - } - - @Override - public Float get(ValueAnimator animator) { - return (float) animator.getCurrentPlayTime(); - } - }; - // Progress factor after which an animation is considered almost completed. private static final float ANIMATION_COMPLETE_THRESHOLD = 0.95f; @@ -177,21 +163,22 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL long springDuration = animationDuration; for (Holder h : mChildAnimations) { if ((h.springProperty.flags & springFlag) != 0) { - SpringAnimationBuilder s = new SpringAnimationBuilder(h.anim, CURRENT_PLAY_TIME) - .setStartValue(clampDuration(mCurrentFraction)) - .setEndValue(goingToEnd ? h.anim.getDuration() : 0) - .setStartVelocity(scaledVelocity * h.anim.getDuration()) + SpringAnimationBuilder s = new SpringAnimationBuilder(context) + .setStartValue(mCurrentFraction) + .setEndValue(goingToEnd ? 1 : 0) + .setStartVelocity(scaledVelocity) .setMinimumVisibleChange(scaleInverse) .setDampingRatio(h.springProperty.mDampingRatio) - .setStiffness(h.springProperty.mStiffness); + .setStiffness(h.springProperty.mStiffness) + .computeParams(); - long expectedDurationL = s.build(context).getDuration(); + long expectedDurationL = s.getDuration(); springDuration = Math.max(expectedDurationL, springDuration); float expectedDuration = expectedDurationL; - h.setter = (a, l) -> - s.setValue(a, mAnimationPlayer.getCurrentPlayTime() / expectedDuration); - h.anim.setInterpolator(LINEAR); + h.setter = (a, l) -> a.setCurrentFraction( + mAnimationPlayer.getCurrentPlayTime() / expectedDuration); + h.anim.setInterpolator(s::getInterpolatedValue); } } diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java index f22a9f0ac6..bc77aab858 100644 --- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java +++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java @@ -15,7 +15,9 @@ */ package com.android.launcher3.anim; -import android.animation.ObjectAnimator; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.animation.ValueAnimator; import android.content.Context; import android.util.FloatProperty; @@ -28,10 +30,9 @@ import com.android.launcher3.util.DefaultDisplay; * Utility class to build an object animator which follows the same path as a spring animation for * an underdamped spring. */ -public class SpringAnimationBuilder extends FloatProperty { +public class SpringAnimationBuilder { - private final T mTarget; - private final FloatProperty mProperty; + private final Context mContext; private float mStartValue; private float mEndValue; @@ -64,27 +65,23 @@ public class SpringAnimationBuilder extends FloatProperty { private double mValueThreshold; private double mVelocityThreshold; - private float mCurrentTime = 0; + private float mDuration = 0; - public SpringAnimationBuilder(T target, FloatProperty property) { - super("dynamic-spring-property"); - mTarget = target; - mProperty = property; - - mStartValue = mProperty.get(target); + public SpringAnimationBuilder(Context context) { + mContext = context; } - public SpringAnimationBuilder setEndValue(float value) { + public SpringAnimationBuilder setEndValue(float value) { mEndValue = value; return this; } - public SpringAnimationBuilder setStartValue(float value) { + public SpringAnimationBuilder setStartValue(float value) { mStartValue = value; return this; } - public SpringAnimationBuilder setValues(float... values) { + public SpringAnimationBuilder setValues(float... values) { if (values.length > 1) { mStartValue = values[0]; mEndValue = values[values.length - 1]; @@ -94,7 +91,7 @@ public class SpringAnimationBuilder extends FloatProperty { return this; } - public SpringAnimationBuilder setStiffness( + public SpringAnimationBuilder setStiffness( @FloatRange(from = 0.0, fromInclusive = false) float stiffness) { if (stiffness <= 0) { throw new IllegalArgumentException("Spring stiffness constant must be positive."); @@ -103,7 +100,7 @@ public class SpringAnimationBuilder extends FloatProperty { return this; } - public SpringAnimationBuilder setDampingRatio( + public SpringAnimationBuilder setDampingRatio( @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false) float dampingRatio) { if (dampingRatio <= 0 || dampingRatio >= 1) { @@ -113,7 +110,7 @@ public class SpringAnimationBuilder extends FloatProperty { return this; } - public SpringAnimationBuilder setMinimumVisibleChange( + public SpringAnimationBuilder setMinimumVisibleChange( @FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) { if (minimumVisibleChange <= 0) { throw new IllegalArgumentException("Minimum visible change must be positive."); @@ -122,25 +119,21 @@ public class SpringAnimationBuilder extends FloatProperty { return this; } - public SpringAnimationBuilder setStartVelocity(float startVelocity) { + public SpringAnimationBuilder setStartVelocity(float startVelocity) { mVelocity = startVelocity; return this; } - @Override - public void setValue(T object, float time) { - mCurrentTime = time; - mProperty.setValue( - object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue); + public float getInterpolatedValue(float fraction) { + return getValue(mDuration * fraction); } - @Override - public Float get(T t) { - return mCurrentTime; + private float getValue(float time) { + return (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue; } - public ObjectAnimator build(Context context) { - int singleFrameMs = DefaultDisplay.getSingleFrameMs(context); + public SpringAnimationBuilder computeParams() { + int singleFrameMs = DefaultDisplay.getSingleFrameMs(mContext); double naturalFreq = Math.sqrt(mStiffness); double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio); @@ -187,12 +180,21 @@ public class SpringAnimationBuilder extends FloatProperty { } } while (true); + mDuration = (float) duration; + return this; + } - long durationMs = (long) (1000.0 * duration); - ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration); - animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR); - animator.addListener(AnimationSuccessListener.forRunnable( - () -> mProperty.setValue(mTarget, mEndValue))); + public long getDuration() { + return (long) (1000.0 * mDuration); + } + + public ValueAnimator build(T target, FloatProperty property) { + computeParams(); + + ValueAnimator animator = ValueAnimator.ofFloat(0, mDuration); + animator.setDuration(getDuration()).setInterpolator(LINEAR); + animator.addUpdateListener(anim -> + property.set(target, getInterpolatedValue(anim.getAnimatedFraction()))); return animator; }