• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
19 
20 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
21 
22 import android.animation.Animator;
23 import android.animation.AnimatorListenerAdapter;
24 import android.animation.ObjectAnimator;
25 import android.animation.TimeInterpolator;
26 import android.animation.ValueAnimator;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.util.FloatProperty;
30 import android.util.Log;
31 
32 import java.util.ArrayList;
33 
34 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
35 import androidx.dynamicanimation.animation.SpringAnimation;
36 import androidx.dynamicanimation.animation.SpringForce;
37 
38 /**
39  * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
40  * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
41  */
42 public class SpringObjectAnimator<T> extends ValueAnimator {
43 
44     private static final String TAG = "SpringObjectAnimator";
45     private static boolean DEBUG = false;
46 
47     private ObjectAnimator mObjectAnimator;
48     private float[] mValues;
49 
50     private SpringAnimation mSpring;
51     private SpringProperty<T> mProperty;
52 
53     private ArrayList<AnimatorListener> mListeners;
54     private boolean mSpringEnded = true;
55     private boolean mAnimatorEnded = true;
56     private boolean mEnded = true;
57 
SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange, float damping, float stiffness, float... values)58     public SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange,
59             float damping, float stiffness, float... values) {
60         mSpring = new SpringAnimation(object, createFloatPropertyCompat(property));
61         mSpring.setMinimumVisibleChange(minimumVisibleChange);
62         mSpring.setSpring(new SpringForce(0)
63                 .setDampingRatio(damping)
64                 .setStiffness(stiffness));
65         mSpring.setStartVelocity(0.01f);
66         mProperty = new SpringProperty<>(property, mSpring);
67         mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
68         mValues = values;
69         mListeners = new ArrayList<>();
70         setFloatValues(values);
71 
72         // We use this listener and track mListeners so that we can sync the animator and spring
73         // listeners.
74         mObjectAnimator.addListener(new AnimatorListenerAdapter() {
75             @Override
76             public void onAnimationStart(Animator animation) {
77                 mAnimatorEnded = false;
78                 mEnded = false;
79                 for (AnimatorListener l : mListeners) {
80                     l.onAnimationStart(animation);
81                 }
82             }
83 
84             @Override
85             public void onAnimationEnd(Animator animation) {
86                 mAnimatorEnded = true;
87                 tryEnding();
88             }
89 
90             @Override
91             public void onAnimationCancel(Animator animation) {
92                 for (AnimatorListener l : mListeners) {
93                     l.onAnimationCancel(animation);
94                 }
95                 mSpring.cancel();
96             }
97         });
98 
99         mSpring.addUpdateListener((animation, value, velocity) -> mSpringEnded = false);
100         mSpring.addEndListener((animation, canceled, value, velocity) -> {
101             mSpringEnded = true;
102             tryEnding();
103         });
104     }
105 
tryEnding()106     private void tryEnding() {
107         if (DEBUG) {
108             Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded="
109                     + mSpringEnded + ", mEnded=" + mEnded);
110         }
111 
112         // If springs are disabled, ignore value of mSpringEnded
113         if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) {
114             for (AnimatorListener l : mListeners) {
115                 l.onAnimationEnd(this);
116             }
117             mEnded = true;
118         }
119     }
120 
getSpring()121     public SpringAnimation getSpring() {
122         return mSpring;
123     }
124 
125     /**
126      * Initializes and sets up the spring to take over controlling the object.
127      */
startSpring(float end, float velocity, OnAnimationEndListener endListener)128     public void startSpring(float end, float velocity, OnAnimationEndListener endListener) {
129         // Cancel the spring so we can set new start velocity and final position. We need to remove
130         // the listener since the spring is not actually ending.
131         mSpring.removeEndListener(endListener);
132         mSpring.cancel();
133         mSpring.addEndListener(endListener);
134 
135         mProperty.switchToSpring();
136 
137         mSpring.setStartVelocity(velocity);
138 
139         float startValue = end == 0 ? mValues[1] : mValues[0];
140         float endValue = end == 0 ? mValues[0] : mValues[1];
141         mSpring.setStartValue(startValue);
142         new Handler(Looper.getMainLooper()).postDelayed(() -> {
143             mSpring.animateToFinalPosition(endValue);
144         }, getStartDelay());
145     }
146 
147     @Override
addListener(AnimatorListener listener)148     public void addListener(AnimatorListener listener) {
149         mListeners.add(listener);
150     }
151 
getObjectAnimatorListeners()152     public ArrayList<AnimatorListener> getObjectAnimatorListeners() {
153         return mObjectAnimator.getListeners();
154     }
155 
156     @Override
getListeners()157     public ArrayList<AnimatorListener> getListeners() {
158         return mListeners;
159     }
160 
161     @Override
removeAllListeners()162     public void removeAllListeners() {
163         mListeners.clear();
164     }
165 
166     @Override
removeListener(AnimatorListener listener)167     public void removeListener(AnimatorListener listener) {
168         mListeners.remove(listener);
169     }
170 
171     @Override
addPauseListener(AnimatorPauseListener listener)172     public void addPauseListener(AnimatorPauseListener listener) {
173         mObjectAnimator.addPauseListener(listener);
174     }
175 
176     @Override
cancel()177     public void cancel() {
178         mObjectAnimator.cancel();
179         mSpring.cancel();
180     }
181 
182     @Override
end()183     public void end() {
184         mObjectAnimator.end();
185     }
186 
187     @Override
getDuration()188     public long getDuration() {
189         return mObjectAnimator.getDuration();
190     }
191 
192     @Override
getInterpolator()193     public TimeInterpolator getInterpolator() {
194         return mObjectAnimator.getInterpolator();
195     }
196 
197     @Override
getStartDelay()198     public long getStartDelay() {
199         return mObjectAnimator.getStartDelay();
200     }
201 
202     @Override
getTotalDuration()203     public long getTotalDuration() {
204         return mObjectAnimator.getTotalDuration();
205     }
206 
207     @Override
isPaused()208     public boolean isPaused() {
209         return mObjectAnimator.isPaused();
210     }
211 
212     @Override
isRunning()213     public boolean isRunning() {
214         return mObjectAnimator.isRunning();
215     }
216 
217     @Override
isStarted()218     public boolean isStarted() {
219         return mObjectAnimator.isStarted();
220     }
221 
222     @Override
pause()223     public void pause() {
224         mObjectAnimator.pause();
225     }
226 
227     @Override
removePauseListener(AnimatorPauseListener listener)228     public void removePauseListener(AnimatorPauseListener listener) {
229         mObjectAnimator.removePauseListener(listener);
230     }
231 
232     @Override
resume()233     public void resume() {
234         mObjectAnimator.resume();
235     }
236 
237     @Override
setDuration(long duration)238     public ValueAnimator setDuration(long duration) {
239         return mObjectAnimator.setDuration(duration);
240     }
241 
242     @Override
setInterpolator(TimeInterpolator value)243     public void setInterpolator(TimeInterpolator value) {
244         mObjectAnimator.setInterpolator(value);
245     }
246 
247     @Override
setStartDelay(long startDelay)248     public void setStartDelay(long startDelay) {
249         mObjectAnimator.setStartDelay(startDelay);
250     }
251 
252     @Override
setTarget(Object target)253     public void setTarget(Object target) {
254         mObjectAnimator.setTarget(target);
255     }
256 
257     @Override
start()258     public void start() {
259         mObjectAnimator.start();
260     }
261 
262     @Override
setCurrentFraction(float fraction)263     public void setCurrentFraction(float fraction) {
264         mObjectAnimator.setCurrentFraction(fraction);
265     }
266 
267     @Override
setCurrentPlayTime(long playTime)268     public void setCurrentPlayTime(long playTime) {
269         mObjectAnimator.setCurrentPlayTime(playTime);
270     }
271 
272     public static class SpringProperty<T> extends FloatProperty<T> {
273 
274         boolean useSpring = false;
275         final FloatProperty<T> mProperty;
276         final SpringAnimation mSpring;
277 
SpringProperty(FloatProperty<T> property, SpringAnimation spring)278         public SpringProperty(FloatProperty<T> property, SpringAnimation spring) {
279             super(property.getName());
280             mProperty = property;
281             mSpring = spring;
282         }
283 
switchToSpring()284         public void switchToSpring() {
285             useSpring = true;
286         }
287 
288         @Override
get(T object)289         public Float get(T object) {
290             return mProperty.get(object);
291         }
292 
293         @Override
setValue(T object, float progress)294         public void setValue(T object, float progress) {
295             if (useSpring) {
296                 mSpring.animateToFinalPosition(progress);
297             } else {
298                 mProperty.setValue(object, progress);
299             }
300         }
301     }
302 }
303