1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.anim;
17 
18 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
19 import androidx.dynamicanimation.animation.FlingAnimation;
20 import androidx.dynamicanimation.animation.FloatPropertyCompat;
21 import androidx.dynamicanimation.animation.SpringAnimation;
22 import androidx.dynamicanimation.animation.SpringForce;
23 
24 /**
25  * Given a property to animate and a target value and starting velocity, first apply friction to
26  * the fling until we pass the target, then apply a spring force to pull towards the target.
27  */
28 public class FlingSpringAnim {
29 
30     private static final float FLING_FRICTION = 1.5f;
31     private static final float SPRING_STIFFNESS = 200;
32     private static final float SPRING_DAMPING = 0.8f;
33 
34     private final FlingAnimation mFlingAnim;
35     private SpringAnimation mSpringAnim;
36 
37     private float mTargetPosition;
38 
FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition, float targetPosition, float startVelocity, float minVisChange, float minValue, float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener)39     public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
40             float targetPosition, float startVelocity, float minVisChange, float minValue,
41             float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) {
42         mFlingAnim = new FlingAnimation(object, property)
43                 .setFriction(FLING_FRICTION)
44                 // Have the spring pull towards the target if we've slowed down too much before
45                 // reaching it.
46                 .setMinimumVisibleChange(minVisChange)
47                 .setStartVelocity(startVelocity)
48                 .setMinValue(minValue)
49                 .setMaxValue(maxValue);
50         mTargetPosition = targetPosition;
51 
52         mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
53             mSpringAnim = new SpringAnimation(object, property)
54                     .setStartValue(value)
55                     .setStartVelocity(velocity * springVelocityFactor)
56                     .setSpring(new SpringForce(mTargetPosition)
57                             .setStiffness(SPRING_STIFFNESS)
58                             .setDampingRatio(SPRING_DAMPING));
59             mSpringAnim.addEndListener(onEndListener);
60             mSpringAnim.animateToFinalPosition(mTargetPosition);
61         }));
62     }
63 
getTargetPosition()64     public float getTargetPosition() {
65         return mTargetPosition;
66     }
67 
updatePosition(float startPosition, float targetPosition)68     public void updatePosition(float startPosition, float targetPosition) {
69         mFlingAnim.setMinValue(Math.min(startPosition, targetPosition))
70                 .setMaxValue(Math.max(startPosition, targetPosition));
71         mTargetPosition = targetPosition;
72         if (mSpringAnim != null) {
73             mSpringAnim.animateToFinalPosition(mTargetPosition);
74         }
75     }
76 
start()77     public void start() {
78         mFlingAnim.start();
79     }
80 
end()81     public void end() {
82         mFlingAnim.cancel();
83         if (mSpringAnim.canSkipToEnd()) {
84             mSpringAnim.skipToEnd();
85         }
86     }
87 }
88