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 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.dynamicanimation.animation; 18d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 19d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.os.Looper; 20d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.util.AndroidRuntimeException; 21d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 22d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu/** 23d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines 24d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * the spring's stiffness, damping ratio, as well as the rest position. Once the SpringAnimation is 25d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * started, on each frame the spring force will update the animation's value and velocity. 26d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * The animation will continue to run until the spring force reaches equilibrium. If the spring used 27d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * in the animation is undamped, the animation will never reach equilibrium. Instead, it will 28d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * oscillate forever. 2962f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * 3062f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <div class="special reference"> 3162f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <h3>Developer Guides</h3> 3262f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * </div> 3362f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * 3462f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <p>To create a simple {@link SpringAnimation} that uses the default {@link SpringForce}:</p> 3562f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <pre class="prettyprint"> 3662f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // Create an animation to animate view's X property, set the rest position of the 3762f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // default spring to 0, and start the animation with a starting velocity of 5000 (pixel/s). 3862f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.X, 0) 39b9b09be93068e05c010b8e67f1e4c3a839531fc6Doris Liu * .setStartVelocity(5000); 40b9b09be93068e05c010b8e67f1e4c3a839531fc6Doris Liu * anim.start(); 4162f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * </pre> 4262f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * 4362f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <p>Alternatively, a {@link SpringAnimation} can take a pre-configured {@link SpringForce}, and 4462f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * use that to drive the animation. </p> 4562f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * <pre class="prettyprint"> 4662f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // Create a low stiffness, low bounce spring at position 0. 4762f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * SpringForce spring = new SpringForce(0) 4862f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) 4962f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * .setStiffness(SpringForce.STIFFNESS_LOW); 5062f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // Create an animation to animate view's scaleY property, and start the animation using 5162f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // the spring above and a starting value of 0.5. Additionally, constrain the range of value for 5262f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * // the animation to be non-negative, effectively preventing any spring overshoot. 5362f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.SCALE_Y) 54b9b09be93068e05c010b8e67f1e4c3a839531fc6Doris Liu * .setMinValue(0).setSpring(spring).setStartValue(1); 55b9b09be93068e05c010b8e67f1e4c3a839531fc6Doris Liu * anim.start(); 5662f5f70f678299fb18d8b7855e7bc50d9e426641Doris Liu * </pre> 57d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 58d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liupublic final class SpringAnimation extends DynamicAnimation<SpringAnimation> { 59d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 60d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private SpringForce mSpring = null; 61d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private float mPendingPosition = UNSET; 62d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private static final float UNSET = Float.MAX_VALUE; 63a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu private boolean mEndRequested = false; 64d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 65d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 667a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During 677a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * the animation, the {@link FloatValueHolder} instance will be updated via 687a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date 697a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * animation value via {@link FloatValueHolder#getValue()}. 70bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * 717a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via 727a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * {@link FloatValueHolder#setValue(float)} outside of the animation during an 73bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * animation run will not have any effect on the on-going animation. 74bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * 757a33db57c360aaabfa5c16dff109072925576b0bDoris Liu * @param floatValueHolder the property to be animated 76bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu */ 777a33db57c360aaabfa5c16dff109072925576b0bDoris Liu public SpringAnimation(FloatValueHolder floatValueHolder) { 787a33db57c360aaabfa5c16dff109072925576b0bDoris Liu super(floatValueHolder); 79bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu } 80bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu 81bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu /** 82bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * This creates a SpringAnimation that animates the property of the given object. 83d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Note, a spring will need to setup through {@link #setSpring(SpringForce)} before 84d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * the animation starts. 85d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 86bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param object the Object whose property will be animated 87bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param property the property to be animated 88bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param <K> the class on which the Property is declared 89d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 90bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu public <K> SpringAnimation(K object, FloatPropertyCompat<K> property) { 91bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu super(object, property); 92d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 93d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 94d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 95bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * This creates a SpringAnimation that animates the property of the given object. A Spring will 96bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * be created with the given final position and default stiffness and damping ratio. 97d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}. 98d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 99bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param object the Object whose property will be animated 100bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param property the property to be animated 101d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param finalPosition the final position of the spring to be created. 102bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu * @param <K> the class on which the Property is declared 103bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu */ 104bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu public <K> SpringAnimation(K object, FloatPropertyCompat<K> property, 105bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu float finalPosition) { 106bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu super(object, property); 107bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu mSpring = new SpringForce(finalPosition); 108bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu } 109bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu 110bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu /** 111d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Returns the spring that the animation uses for animations. 112d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 113d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return the spring that the animation uses for animations 114d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 115d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringForce getSpring() { 116d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring; 117d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 118d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 119d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 120d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Uses the given spring as the force that drives this animation. If this spring force has its 121d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * parameters re-configured during the animation, the new configuration will be reflected in the 122d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * animation immediately. 123d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 124d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param force a pre-defined spring force that drives the animation 125d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return the animation that the spring force is set on 126d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 127d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public SpringAnimation setSpring(SpringForce force) { 128d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring = force; 129d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return this; 130d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 131d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 132d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 133d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void start() { 134d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu sanityCheck(); 1355df257d2a2f330f8e4d37321a40431973fa9b0d1Doris Liu mSpring.setValueThreshold(getValueThreshold()); 136d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu super.start(); 137d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 138d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 139d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 140d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Updates the final position of the spring. 141d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p/> 142d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * When the animation is running, calling this method would assume the position change of the 143d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * spring as a continuous movement since last frame, which yields more accurate results than 144d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * changing the spring position directly through {@link SpringForce#setFinalPosition(float)}. 145d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p/> 146d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * If the animation hasn't started, calling this method will change the spring position, and 147d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * immediately start the animation. 148d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 149d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @param finalPosition rest position of the spring 150d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 151d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void animateToFinalPosition(float finalPosition) { 152d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (isRunning()) { 153d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mPendingPosition = finalPosition; 154d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else { 155d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mSpring == null) { 156d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring = new SpringForce(finalPosition); 157d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 158d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setFinalPosition(finalPosition); 159d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu start(); 160d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 161d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 162d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 163d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 164d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Skips to the end of the animation. If the spring is undamped, an 165d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * {@link IllegalStateException} will be thrown, as the animation would never reach to an end. 166d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * It is recommended to check {@link #canSkipToEnd()} before calling this method. This method 167d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * should only be called on main thread. If animation is not running, no-op. 168d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 169d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @throws IllegalStateException if the spring is undamped (i.e. damping ratio = 0) 170d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @throws AndroidRuntimeException if this method is not called on the main thread 171d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 172d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public void skipToEnd() { 173d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (!canSkipToEnd()) { 174d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Spring animations can only come to an end" 175d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " when there is damping"); 176d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 177d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (Looper.myLooper() != Looper.getMainLooper()) { 178d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new AndroidRuntimeException("Animations may only be started on the main thread"); 179d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 180d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mRunning) { 181a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mEndRequested = true; 182d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 183d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 184d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 185d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /** 186d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Queries whether the spring can eventually come to the rest position. 187d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * 188d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * @return {@code true} if the spring is damped, otherwise {@code false} 189d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */ 190d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu public boolean canSkipToEnd() { 191d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.mDampingRatio > 0; 192d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 193d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 194d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu /************************ Below are private APIs *************************/ 195d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 196d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu private void sanityCheck() { 197d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mSpring == null) { 198d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Incomplete SpringAnimation: Either final" 199d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " position or a spring force needs to be set."); 200d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 201d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu double finalPosition = mSpring.getFinalPosition(); 202d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (finalPosition > mMaxValue) { 203d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Final position of the spring cannot be greater" 204d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " than the max value."); 205d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else if (finalPosition < mMinValue) { 206d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu throw new UnsupportedOperationException("Final position of the spring cannot be less" 207d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu + " than the min value."); 208d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 209d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 210d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 211d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 212d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu boolean updateValueAndVelocity(long deltaT) { 213a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu // If user had requested end, then update the value and velocity to end state and consider 214a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu // animation done. 215a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu if (mEndRequested) { 216a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu if (mPendingPosition != UNSET) { 217a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mSpring.setFinalPosition(mPendingPosition); 218a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mPendingPosition = UNSET; 219a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu } 220a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mValue = mSpring.getFinalPosition(); 221a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mVelocity = 0; 222a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu mEndRequested = false; 223a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu return true; 224a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu } 225a00ac0ab7a6db7d635fadac1de5eb1e172f1dd89Doris Liu 226d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (mPendingPosition != UNSET) { 227d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu double lastPosition = mSpring.getFinalPosition(); 228d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu // Approximate by considering half of the time spring position stayed at the old 229d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu // position, half of the time it's at the new position. 230bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT / 2); 231d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mSpring.setFinalPosition(mPendingPosition); 232d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mPendingPosition = UNSET; 233d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 234d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu massState = mSpring.updateValues(massState.mValue, massState.mVelocity, deltaT / 2); 235d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = massState.mValue; 236d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = massState.mVelocity; 237d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 238d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } else { 239bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT); 240d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = massState.mValue; 241d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = massState.mVelocity; 242d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 243d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 244d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = Math.max(mValue, mMinValue); 245d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = Math.min(mValue, mMaxValue); 246d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 247d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu if (isAtEquilibrium(mValue, mVelocity)) { 248d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mValue = mSpring.getFinalPosition(); 249d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu mVelocity = 0f; 250d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return true; 251d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 252d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return false; 253d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 254d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 255d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 256d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu float getAcceleration(float value, float velocity) { 257d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.getAcceleration(value, velocity); 258d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 259d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu 260d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu @Override 261d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu boolean isAtEquilibrium(float value, float velocity) { 262d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu return mSpring.isAtEquilibrium(value, velocity); 263d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu } 264bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu 265bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu @Override 2665df257d2a2f330f8e4d37321a40431973fa9b0d1Doris Liu void setValueThreshold(float threshold) { 267bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu } 268d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu} 269