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