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 android.content.Context;
19 
20 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
21 import androidx.dynamicanimation.animation.FlingAnimation;
22 import androidx.dynamicanimation.animation.FloatPropertyCompat;
23 import androidx.dynamicanimation.animation.SpringAnimation;
24 import androidx.dynamicanimation.animation.SpringForce;
25 
26 import com.android.launcher3.R;
27 import com.android.launcher3.util.DynamicResource;
28 import com.android.systemui.plugins.ResourceProvider;
29 
30 /**
31  * Given a property to animate and a target value and starting velocity, first apply friction to
32  * the fling until we pass the target, then apply a spring force to pull towards the target.
33  */
34 public class FlingSpringAnim {
35 
36     private final FlingAnimation mFlingAnim;
37     private SpringAnimation mSpringAnim;
38     private final boolean mSkipFlingAnim;
39 
40     private float mTargetPosition;
41 
FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property, float startPosition, float targetPosition, float startVelocityPxPerS, float minVisChange, float minValue, float maxValue, float damping, float stiffness, OnAnimationEndListener onEndListener)42     public <K> FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property,
43             float startPosition, float targetPosition, float startVelocityPxPerS,
44             float minVisChange, float minValue, float maxValue, float damping, float stiffness,
45             OnAnimationEndListener onEndListener) {
46         ResourceProvider rp = DynamicResource.provider(context);
47         float friction = rp.getFloat(R.dimen.swipe_up_rect_xy_fling_friction);
48 
49         mFlingAnim = new FlingAnimation(object, property)
50                 .setFriction(friction)
51                 // Have the spring pull towards the target if we've slowed down too much before
52                 // reaching it.
53                 .setMinimumVisibleChange(minVisChange)
54                 .setStartVelocity(startVelocityPxPerS)
55                 .setMinValue(minValue)
56                 .setMaxValue(maxValue);
57         mTargetPosition = targetPosition;
58 
59         // We are already past the fling target, so skip it to avoid losing a frame of the spring.
60         mSkipFlingAnim = startPosition <= minValue && startVelocityPxPerS < 0
61                 || startPosition >= maxValue && startVelocityPxPerS > 0;
62 
63         mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
64             mSpringAnim = new SpringAnimation(object, property)
65                     .setStartValue(value)
66                     .setStartVelocity(velocity)
67                     .setSpring(new SpringForce(mTargetPosition)
68                             .setStiffness(stiffness)
69                             .setDampingRatio(damping));
70             mSpringAnim.addEndListener(onEndListener);
71             mSpringAnim.animateToFinalPosition(mTargetPosition);
72         }));
73     }
74 
getTargetPosition()75     public float getTargetPosition() {
76         return mTargetPosition;
77     }
78 
updatePosition(float startPosition, float targetPosition)79     public void updatePosition(float startPosition, float targetPosition) {
80         mFlingAnim.setMinValue(Math.min(startPosition, targetPosition))
81                 .setMaxValue(Math.max(startPosition, targetPosition));
82         mTargetPosition = targetPosition;
83         if (mSpringAnim != null) {
84             mSpringAnim.animateToFinalPosition(mTargetPosition);
85         }
86     }
87 
start()88     public void start() {
89         mFlingAnim.start();
90         if (mSkipFlingAnim) {
91             mFlingAnim.cancel();
92         }
93     }
94 
end()95     public void end() {
96         mFlingAnim.cancel();
97         if (mSpringAnim.canSkipToEnd()) {
98             mSpringAnim.skipToEnd();
99         }
100     }
101 }
102