SpringAnimation.java revision d5206a7781d95cba12ca70a20f7ee742a2e9d807
1d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu/* 2d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Copyright (C) 2017 The Android Open Source Project 3d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 4d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Licensed under the Apache License, Version 2.0 (the "License"); 5d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * you may not use this file except in compliance with the License. 6d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * You may obtain a copy of the License at 7d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 8d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * http://www.apache.org/licenses/LICENSE-2.0 9d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 10d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Unless required by applicable law or agreed to in writing, software 11d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * distributed under the License is distributed on an "AS IS" BASIS, 12d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * See the License for the specific language governing permissions and 14d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * limitations under the License. 15d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 16d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 17d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liupackage android.support.animation; 18d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 19d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.os.Looper; 20d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.util.AndroidRuntimeException; 21d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.view.View; 22d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 23d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu/** 24d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines 25d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * the spring's stiffness, damping ratio, as well as the rest position. Once the SpringAnimation is 26d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * started, on each frame the spring force will update the animation's value and velocity. 27d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * The animation will continue to run until the spring force reaches equilibrium. If the spring used 28d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * in the animation is undamped, the animation will never reach equilibrium. Instead, it will 29d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * oscillate forever. 30d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 31d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liupublic final class SpringAnimation extends DynamicAnimation<SpringAnimation> { 32d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 33d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private SpringForce mSpring = null; 34d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private float mPendingPosition = UNSET; 35d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private static final float UNSET = Float.MAX_VALUE; 36d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 37d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 38d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * This creates a SpringAnimation that animates the property of the given view. 39d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Note, a spring will need to setup through {@link #setSpring(SpringForce)} before 40d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * the animation starts. 41d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 42d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param v The View whose property will be animated 43d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param property the property index of the view 44d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 45d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringAnimation(View v, ViewProperty property) { 46d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu super(v, property); 47d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 48d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 49d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 50d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * This creates a SpringAnimation that animates the property of the given view. A Spring will be 51d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * created with the given final position and default stiffness and damping ratio. 52d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. 53d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 54d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param v The View whose property will be animated 55d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param property the property index of the view 56d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param finalPosition the final position of the spring to be created. 57d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 58d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringAnimation(View v, ViewProperty property, float finalPosition) { 59d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu super(v, property); 60d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring = new SpringForce(finalPosition); 61d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu setSpringThreshold(); 62d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 63d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 64d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 65d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Returns the spring that the animation uses for animations. 66d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 67d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return the spring that the animation uses for animations 68d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 69d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringForce getSpring() { 70d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring; 71d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 72d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 73d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 74d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Uses the given spring as the force that drives this animation. If this spring force has its 75d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * parameters re-configured during the animation, the new configuration will be reflected in the 76d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * animation immediately. 77d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 78d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param force a pre-defined spring force that drives the animation 79d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return the animation that the spring force is set on 80d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 81d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringAnimation setSpring(SpringForce force) { 82d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring = force; 83d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu setSpringThreshold(); 84d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return this; 85d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 86d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 87d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 88d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void start() { 89d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu sanityCheck(); 90d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu super.start(); 91d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 92d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 93d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 94d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Updates the final position of the spring. 95d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p/> 96d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * When the animation is running, calling this method would assume the position change of the 97d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * spring as a continuous movement since last frame, which yields more accurate results than 98d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * changing the spring position directly through {@link SpringForce#setFinalPosition(float)}. 99d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p/> 100d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * If the animation hasn't started, calling this method will change the spring position, and 101d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * immediately start the animation. 102d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 103d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param finalPosition rest position of the spring 104d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 105d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void animateToFinalPosition(float finalPosition) { 106d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (isRunning()) { 107d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mPendingPosition = finalPosition; 108d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else { 109d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mSpring == null) { 110d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring = new SpringForce(finalPosition); 111d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 112d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setFinalPosition(finalPosition); 113d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu start(); 114d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 115d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 116d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 117d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 118d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Skips to the end of the animation. If the spring is undamped, an 119d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * {@link IllegalStateException} will be thrown, as the animation would never reach to an end. 120d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * It is recommended to check {@link #canSkipToEnd()} before calling this method. This method 121d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * should only be called on main thread. If animation is not running, no-op. 122d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 123d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @throws IllegalStateException if the spring is undamped (i.e. damping ratio = 0) 124d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @throws AndroidRuntimeException if this method is not called on the main thread 125d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 126d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void skipToEnd() { 127d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (!canSkipToEnd()) { 128d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Spring animations can only come to an end" 129d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " when there is damping"); 130d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 131d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (Looper.myLooper() != Looper.getMainLooper()) { 132d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new AndroidRuntimeException("Animations may only be started on the main thread"); 133d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 134d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mRunning) { 135d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mPendingPosition != UNSET) { 136d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setFinalPosition(mPendingPosition); 137d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mPendingPosition = UNSET; 138d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 139d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = mSpring.getFinalPosition(); 140d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = 0; 141d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu cancel(); 142d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 143d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 144d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 145d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 146d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Queries whether the spring can eventually come to the rest position. 147d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 148d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return {@code true} if the spring is damped, otherwise {@code false} 149d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 150d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public boolean canSkipToEnd() { 151d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.mDampingRatio > 0; 152d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 153d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 154d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /************************ Below are private APIs *************************/ 155d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 156d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private void setSpringThreshold() { 157d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mViewProperty == ROTATION || mViewProperty == ROTATION_X 158d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu || mViewProperty == ROTATION_Y) { 159d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setDefaultThreshold(SpringForce.VALUE_THRESHOLD_ROTATION); 160d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else if (mViewProperty == ALPHA) { 161d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setDefaultThreshold(SpringForce.VALUE_THRESHOLD_ALPHA); 162d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else if (mViewProperty == SCALE_X || mViewProperty == SCALE_Y) { 163d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setDefaultThreshold(SpringForce.VALUE_THRESHOLD_SCALE); 164d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else { 165d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setDefaultThreshold(SpringForce.VALUE_THRESHOLD_IN_PIXEL); 166d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 167d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 168d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 169d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private void sanityCheck() { 170d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mSpring == null) { 171d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Incomplete SpringAnimation: Either final" 172d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " position or a spring force needs to be set."); 173d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 174d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu double finalPosition = mSpring.getFinalPosition(); 175d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (finalPosition > mMaxValue) { 176d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Final position of the spring cannot be greater" 177d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " than the max value."); 178d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else if (finalPosition < mMinValue) { 179d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Final position of the spring cannot be less" 180d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " than the min value."); 181d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 182d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 183d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 184d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 185d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu boolean updateValueAndVelocity(long deltaT) { 186d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mPendingPosition != UNSET) { 187d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu double lastPosition = mSpring.getFinalPosition(); 188d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu // Approximate by considering half of the time spring position stayed at the old 189d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu // position, half of the time it's at the new position. 190d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu SpringForce.MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT / 2); 191d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setFinalPosition(mPendingPosition); 192d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mPendingPosition = UNSET; 193d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 194d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu massState = mSpring.updateValues(massState.mValue, massState.mVelocity, deltaT / 2); 195d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = massState.mValue; 196d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = massState.mVelocity; 197d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 198d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else { 199d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu SpringForce.MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT); 200d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = massState.mValue; 201d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = massState.mVelocity; 202d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 203d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 204d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = Math.max(mValue, mMinValue); 205d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = Math.min(mValue, mMaxValue); 206d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 207d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (isAtEquilibrium(mValue, mVelocity)) { 208d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = mSpring.getFinalPosition(); 209d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = 0f; 210d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return true; 211d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 212d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return false; 213d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 214d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 215d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 216d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu float getAcceleration(float value, float velocity) { 217d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.getAcceleration(value, velocity); 218d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 219d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 220d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 221d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu boolean isAtEquilibrium(float value, float velocity) { 222d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.isAtEquilibrium(value, velocity); 223d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 224d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu} 225