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