Loading core/java/android/animation/AnimationHandler.java +2 −1 Original line number Diff line number Diff line Loading @@ -432,8 +432,9 @@ public class AnimationHandler { /** * Callbacks that receives notifications for animation timing and frame commit timing. * @hide */ interface AnimationFrameCallback { public interface AnimationFrameCallback { /** * Run animation based on the frame time. * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time Loading core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java 0 → 100644 +815 −0 File added.Preview size limit exceeded, changes collapsed. Show changes core/java/com/android/internal/dynamicanimation/animation/FloatValueHolder.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; /** * <p>FloatValueHolder holds a float value. FloatValueHolder provides a setter and a getter ( * i.e. {@link #setValue(float)} and {@link #getValue()}) to access this float value. Animations can * be performed on a FloatValueHolder instance. During each frame of the animation, the * FloatValueHolder will have its value updated via {@link #setValue(float)}. The caller can * obtain the up-to-date animation value via {@link FloatValueHolder#getValue()}. * * @see SpringAnimation#SpringAnimation(FloatValueHolder) */ public class FloatValueHolder { private float mValue = 0.0f; /** * Constructs a holder for a float value that is initialized to 0. */ public FloatValueHolder() { } /** * Constructs a holder for a float value that is initialized to the input value. * * @param value the value to initialize the value held in the FloatValueHolder */ public FloatValueHolder(float value) { setValue(value); } /** * Sets the value held in the FloatValueHolder instance. * * @param value float value held in the FloatValueHolder instance */ public void setValue(float value) { mValue = value; } /** * Returns the float value held in the FloatValueHolder instance. * * @return float value held in the FloatValueHolder instance */ public float getValue() { return mValue; } } core/java/com/android/internal/dynamicanimation/animation/Force.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; /** * Hide this for now, in case we want to change the API. */ interface Force { // Acceleration based on position. float getAcceleration(float position, float velocity); boolean isAtEquilibrium(float value, float velocity); } core/java/com/android/internal/dynamicanimation/animation/SpringAnimation.java 0 → 100644 +314 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; import android.util.AndroidRuntimeException; import android.util.FloatProperty; /** * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines * the spring's stiffness, damping ratio, as well as the rest position. Once the SpringAnimation is * started, on each frame the spring force will update the animation's value and velocity. * The animation will continue to run until the spring force reaches equilibrium. If the spring used * in the animation is undamped, the animation will never reach equilibrium. Instead, it will * oscillate forever. * * <div class="special reference"> * <h3>Developer Guides</h3> * </div> * * <p>To create a simple {@link SpringAnimation} that uses the default {@link SpringForce}:</p> * <pre class="prettyprint"> * // Create an animation to animate view's X property, set the rest position of the * // default spring to 0, and start the animation with a starting velocity of 5000 (pixel/s). * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.X, 0) * .setStartVelocity(5000); * anim.start(); * </pre> * * <p>Alternatively, a {@link SpringAnimation} can take a pre-configured {@link SpringForce}, and * use that to drive the animation. </p> * <pre class="prettyprint"> * // Create a low stiffness, low bounce spring at position 0. * SpringForce spring = new SpringForce(0) * .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) * .setStiffness(SpringForce.STIFFNESS_LOW); * // Create an animation to animate view's scaleY property, and start the animation using * // the spring above and a starting value of 0.5. Additionally, constrain the range of value for * // the animation to be non-negative, effectively preventing any spring overshoot. * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.SCALE_Y) * .setMinValue(0).setSpring(spring).setStartValue(1); * anim.start(); * </pre> */ public final class SpringAnimation extends DynamicAnimation<SpringAnimation> { private SpringForce mSpring = null; private float mPendingPosition = UNSET; private static final float UNSET = Float.MAX_VALUE; private boolean mEndRequested = false; /** * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated */ public SpringAnimation(FloatValueHolder floatValueHolder) { super(floatValueHolder); } /** * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * A Spring will be created with the given final position and default stiffness and damping * ratio. This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated * @param finalPosition the final position of the spring to be created. */ public SpringAnimation(FloatValueHolder floatValueHolder, float finalPosition) { super(floatValueHolder); mSpring = new SpringForce(finalPosition); } /** * This creates a SpringAnimation that animates the property of the given object. * Note, a spring will need to setup through {@link #setSpring(SpringForce)} before * the animation starts. * * @param object the Object whose property will be animated * @param property the property to be animated * @param <K> the class on which the Property is declared */ public <K> SpringAnimation(K object, FloatProperty<K> property) { super(object, property); } /** * This creates a SpringAnimation that animates the property of the given object. A Spring will * be created with the given final position and default stiffness and damping ratio. * This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. * * @param object the Object whose property will be animated * @param property the property to be animated * @param finalPosition the final position of the spring to be created. * @param <K> the class on which the Property is declared */ public <K> SpringAnimation(K object, FloatProperty<K> property, float finalPosition) { super(object, property); mSpring = new SpringForce(finalPosition); } /** * Returns the spring that the animation uses for animations. * * @return the spring that the animation uses for animations */ public SpringForce getSpring() { return mSpring; } /** * Uses the given spring as the force that drives this animation. If this spring force has its * parameters re-configured during the animation, the new configuration will be reflected in the * animation immediately. * * @param force a pre-defined spring force that drives the animation * @return the animation that the spring force is set on */ public SpringAnimation setSpring(SpringForce force) { mSpring = force; return this; } @Override public void start() { sanityCheck(); mSpring.setValueThreshold(getValueThreshold()); super.start(); } /** * Updates the final position of the spring. * <p/> * When the animation is running, calling this method would assume the position change of the * spring as a continuous movement since last frame, which yields more accurate results than * changing the spring position directly through {@link SpringForce#setFinalPosition(float)}. * <p/> * If the animation hasn't started, calling this method will change the spring position, and * immediately start the animation. * * @param finalPosition rest position of the spring */ public void animateToFinalPosition(float finalPosition) { if (isRunning()) { mPendingPosition = finalPosition; } else { if (mSpring == null) { mSpring = new SpringForce(finalPosition); } mSpring.setFinalPosition(finalPosition); start(); } } /** * Cancels the on-going animation. If the animation hasn't started, no op. Note that this method * should only be called on main thread. * * @throws AndroidRuntimeException if this method is not called on the main thread */ @Override public void cancel() { super.cancel(); if (mPendingPosition != UNSET) { if (mSpring == null) { mSpring = new SpringForce(mPendingPosition); } else { mSpring.setFinalPosition(mPendingPosition); } mPendingPosition = UNSET; } } /** * Skips to the end of the animation. If the spring is undamped, an * {@link IllegalStateException} will be thrown, as the animation would never reach to an end. * It is recommended to check {@link #canSkipToEnd()} before calling this method. If animation * is not running, no-op. * * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler * is created on the same thread as the first call to start/cancel an animation. All the * subsequent animation lifecycle manipulations need to be on that same thread, until the * AnimationHandler is reset (using [setAnimationHandler]). * * @throws IllegalStateException if the spring is undamped (i.e. damping ratio = 0) * @throws AndroidRuntimeException if this method is not called on the same thread as the * animation handler */ public void skipToEnd() { if (!canSkipToEnd()) { throw new UnsupportedOperationException("Spring animations can only come to an end" + " when there is damping"); } if (!isCurrentThread()) { throw new AndroidRuntimeException("Animations may only be started on the same thread " + "as the animation handler"); } if (mRunning) { mEndRequested = true; } } /** * Queries whether the spring can eventually come to the rest position. * * @return {@code true} if the spring is damped, otherwise {@code false} */ public boolean canSkipToEnd() { return mSpring.mDampingRatio > 0; } /************************ Below are private APIs *************************/ private void sanityCheck() { if (mSpring == null) { throw new UnsupportedOperationException("Incomplete SpringAnimation: Either final" + " position or a spring force needs to be set."); } double finalPosition = mSpring.getFinalPosition(); if (finalPosition > mMaxValue) { throw new UnsupportedOperationException("Final position of the spring cannot be greater" + " than the max value."); } else if (finalPosition < mMinValue) { throw new UnsupportedOperationException("Final position of the spring cannot be less" + " than the min value."); } } @Override boolean updateValueAndVelocity(long deltaT) { // If user had requested end, then update the value and velocity to end state and consider // animation done. if (mEndRequested) { if (mPendingPosition != UNSET) { mSpring.setFinalPosition(mPendingPosition); mPendingPosition = UNSET; } mValue = mSpring.getFinalPosition(); mVelocity = 0; mEndRequested = false; return true; } if (mPendingPosition != UNSET) { // Approximate by considering half of the time spring position stayed at the old // position, half of the time it's at the new position. MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT / 2); mSpring.setFinalPosition(mPendingPosition); mPendingPosition = UNSET; massState = mSpring.updateValues(massState.mValue, massState.mVelocity, deltaT / 2); mValue = massState.mValue; mVelocity = massState.mVelocity; } else { MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT); mValue = massState.mValue; mVelocity = massState.mVelocity; } mValue = Math.max(mValue, mMinValue); mValue = Math.min(mValue, mMaxValue); if (isAtEquilibrium(mValue, mVelocity)) { mValue = mSpring.getFinalPosition(); mVelocity = 0f; return true; } return false; } @Override float getAcceleration(float value, float velocity) { return mSpring.getAcceleration(value, velocity); } @Override boolean isAtEquilibrium(float value, float velocity) { return mSpring.isAtEquilibrium(value, velocity); } @Override void setValueThreshold(float threshold) { } } Loading
core/java/android/animation/AnimationHandler.java +2 −1 Original line number Diff line number Diff line Loading @@ -432,8 +432,9 @@ public class AnimationHandler { /** * Callbacks that receives notifications for animation timing and frame commit timing. * @hide */ interface AnimationFrameCallback { public interface AnimationFrameCallback { /** * Run animation based on the frame time. * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time Loading
core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java 0 → 100644 +815 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
core/java/com/android/internal/dynamicanimation/animation/FloatValueHolder.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; /** * <p>FloatValueHolder holds a float value. FloatValueHolder provides a setter and a getter ( * i.e. {@link #setValue(float)} and {@link #getValue()}) to access this float value. Animations can * be performed on a FloatValueHolder instance. During each frame of the animation, the * FloatValueHolder will have its value updated via {@link #setValue(float)}. The caller can * obtain the up-to-date animation value via {@link FloatValueHolder#getValue()}. * * @see SpringAnimation#SpringAnimation(FloatValueHolder) */ public class FloatValueHolder { private float mValue = 0.0f; /** * Constructs a holder for a float value that is initialized to 0. */ public FloatValueHolder() { } /** * Constructs a holder for a float value that is initialized to the input value. * * @param value the value to initialize the value held in the FloatValueHolder */ public FloatValueHolder(float value) { setValue(value); } /** * Sets the value held in the FloatValueHolder instance. * * @param value float value held in the FloatValueHolder instance */ public void setValue(float value) { mValue = value; } /** * Returns the float value held in the FloatValueHolder instance. * * @return float value held in the FloatValueHolder instance */ public float getValue() { return mValue; } }
core/java/com/android/internal/dynamicanimation/animation/Force.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; /** * Hide this for now, in case we want to change the API. */ interface Force { // Acceleration based on position. float getAcceleration(float position, float velocity); boolean isAtEquilibrium(float value, float velocity); }
core/java/com/android/internal/dynamicanimation/animation/SpringAnimation.java 0 → 100644 +314 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.dynamicanimation.animation; import android.util.AndroidRuntimeException; import android.util.FloatProperty; /** * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines * the spring's stiffness, damping ratio, as well as the rest position. Once the SpringAnimation is * started, on each frame the spring force will update the animation's value and velocity. * The animation will continue to run until the spring force reaches equilibrium. If the spring used * in the animation is undamped, the animation will never reach equilibrium. Instead, it will * oscillate forever. * * <div class="special reference"> * <h3>Developer Guides</h3> * </div> * * <p>To create a simple {@link SpringAnimation} that uses the default {@link SpringForce}:</p> * <pre class="prettyprint"> * // Create an animation to animate view's X property, set the rest position of the * // default spring to 0, and start the animation with a starting velocity of 5000 (pixel/s). * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.X, 0) * .setStartVelocity(5000); * anim.start(); * </pre> * * <p>Alternatively, a {@link SpringAnimation} can take a pre-configured {@link SpringForce}, and * use that to drive the animation. </p> * <pre class="prettyprint"> * // Create a low stiffness, low bounce spring at position 0. * SpringForce spring = new SpringForce(0) * .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) * .setStiffness(SpringForce.STIFFNESS_LOW); * // Create an animation to animate view's scaleY property, and start the animation using * // the spring above and a starting value of 0.5. Additionally, constrain the range of value for * // the animation to be non-negative, effectively preventing any spring overshoot. * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.SCALE_Y) * .setMinValue(0).setSpring(spring).setStartValue(1); * anim.start(); * </pre> */ public final class SpringAnimation extends DynamicAnimation<SpringAnimation> { private SpringForce mSpring = null; private float mPendingPosition = UNSET; private static final float UNSET = Float.MAX_VALUE; private boolean mEndRequested = false; /** * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated */ public SpringAnimation(FloatValueHolder floatValueHolder) { super(floatValueHolder); } /** * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * A Spring will be created with the given final position and default stiffness and damping * ratio. This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated * @param finalPosition the final position of the spring to be created. */ public SpringAnimation(FloatValueHolder floatValueHolder, float finalPosition) { super(floatValueHolder); mSpring = new SpringForce(finalPosition); } /** * This creates a SpringAnimation that animates the property of the given object. * Note, a spring will need to setup through {@link #setSpring(SpringForce)} before * the animation starts. * * @param object the Object whose property will be animated * @param property the property to be animated * @param <K> the class on which the Property is declared */ public <K> SpringAnimation(K object, FloatProperty<K> property) { super(object, property); } /** * This creates a SpringAnimation that animates the property of the given object. A Spring will * be created with the given final position and default stiffness and damping ratio. * This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. * * @param object the Object whose property will be animated * @param property the property to be animated * @param finalPosition the final position of the spring to be created. * @param <K> the class on which the Property is declared */ public <K> SpringAnimation(K object, FloatProperty<K> property, float finalPosition) { super(object, property); mSpring = new SpringForce(finalPosition); } /** * Returns the spring that the animation uses for animations. * * @return the spring that the animation uses for animations */ public SpringForce getSpring() { return mSpring; } /** * Uses the given spring as the force that drives this animation. If this spring force has its * parameters re-configured during the animation, the new configuration will be reflected in the * animation immediately. * * @param force a pre-defined spring force that drives the animation * @return the animation that the spring force is set on */ public SpringAnimation setSpring(SpringForce force) { mSpring = force; return this; } @Override public void start() { sanityCheck(); mSpring.setValueThreshold(getValueThreshold()); super.start(); } /** * Updates the final position of the spring. * <p/> * When the animation is running, calling this method would assume the position change of the * spring as a continuous movement since last frame, which yields more accurate results than * changing the spring position directly through {@link SpringForce#setFinalPosition(float)}. * <p/> * If the animation hasn't started, calling this method will change the spring position, and * immediately start the animation. * * @param finalPosition rest position of the spring */ public void animateToFinalPosition(float finalPosition) { if (isRunning()) { mPendingPosition = finalPosition; } else { if (mSpring == null) { mSpring = new SpringForce(finalPosition); } mSpring.setFinalPosition(finalPosition); start(); } } /** * Cancels the on-going animation. If the animation hasn't started, no op. Note that this method * should only be called on main thread. * * @throws AndroidRuntimeException if this method is not called on the main thread */ @Override public void cancel() { super.cancel(); if (mPendingPosition != UNSET) { if (mSpring == null) { mSpring = new SpringForce(mPendingPosition); } else { mSpring.setFinalPosition(mPendingPosition); } mPendingPosition = UNSET; } } /** * Skips to the end of the animation. If the spring is undamped, an * {@link IllegalStateException} will be thrown, as the animation would never reach to an end. * It is recommended to check {@link #canSkipToEnd()} before calling this method. If animation * is not running, no-op. * * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler * is created on the same thread as the first call to start/cancel an animation. All the * subsequent animation lifecycle manipulations need to be on that same thread, until the * AnimationHandler is reset (using [setAnimationHandler]). * * @throws IllegalStateException if the spring is undamped (i.e. damping ratio = 0) * @throws AndroidRuntimeException if this method is not called on the same thread as the * animation handler */ public void skipToEnd() { if (!canSkipToEnd()) { throw new UnsupportedOperationException("Spring animations can only come to an end" + " when there is damping"); } if (!isCurrentThread()) { throw new AndroidRuntimeException("Animations may only be started on the same thread " + "as the animation handler"); } if (mRunning) { mEndRequested = true; } } /** * Queries whether the spring can eventually come to the rest position. * * @return {@code true} if the spring is damped, otherwise {@code false} */ public boolean canSkipToEnd() { return mSpring.mDampingRatio > 0; } /************************ Below are private APIs *************************/ private void sanityCheck() { if (mSpring == null) { throw new UnsupportedOperationException("Incomplete SpringAnimation: Either final" + " position or a spring force needs to be set."); } double finalPosition = mSpring.getFinalPosition(); if (finalPosition > mMaxValue) { throw new UnsupportedOperationException("Final position of the spring cannot be greater" + " than the max value."); } else if (finalPosition < mMinValue) { throw new UnsupportedOperationException("Final position of the spring cannot be less" + " than the min value."); } } @Override boolean updateValueAndVelocity(long deltaT) { // If user had requested end, then update the value and velocity to end state and consider // animation done. if (mEndRequested) { if (mPendingPosition != UNSET) { mSpring.setFinalPosition(mPendingPosition); mPendingPosition = UNSET; } mValue = mSpring.getFinalPosition(); mVelocity = 0; mEndRequested = false; return true; } if (mPendingPosition != UNSET) { // Approximate by considering half of the time spring position stayed at the old // position, half of the time it's at the new position. MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT / 2); mSpring.setFinalPosition(mPendingPosition); mPendingPosition = UNSET; massState = mSpring.updateValues(massState.mValue, massState.mVelocity, deltaT / 2); mValue = massState.mValue; mVelocity = massState.mVelocity; } else { MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT); mValue = massState.mValue; mVelocity = massState.mVelocity; } mValue = Math.max(mValue, mMinValue); mValue = Math.min(mValue, mMaxValue); if (isAtEquilibrium(mValue, mVelocity)) { mValue = mSpring.getFinalPosition(); mVelocity = 0f; return true; } return false; } @Override float getAcceleration(float value, float velocity) { return mSpring.getAcceleration(value, velocity); } @Override boolean isAtEquilibrium(float value, float velocity) { return mSpring.isAtEquilibrium(value, velocity); } @Override void setValueThreshold(float threshold) { } }