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) -> { 100 mSpringEnded = false; 101 mEnded = false; 102 }); 103 mSpring.addEndListener((animation, canceled, value, velocity) -> { 104 mSpringEnded = true; 105 tryEnding(); 106 }); 107 } 108 tryEnding()109 private void tryEnding() { 110 if (DEBUG) { 111 Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded=" 112 + mSpringEnded + ", mEnded=" + mEnded); 113 } 114 115 // If springs are disabled, ignore value of mSpringEnded 116 if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) { 117 for (AnimatorListener l : mListeners) { 118 l.onAnimationEnd(this); 119 } 120 mEnded = true; 121 } 122 } 123 getSpring()124 public SpringAnimation getSpring() { 125 return mSpring; 126 } 127 128 /** 129 * Initializes and sets up the spring to take over controlling the object. 130 */ startSpring(float end, float velocity, OnAnimationEndListener endListener)131 public void startSpring(float end, float velocity, OnAnimationEndListener endListener) { 132 // Cancel the spring so we can set new start velocity and final position. We need to remove 133 // the listener since the spring is not actually ending. 134 mSpring.removeEndListener(endListener); 135 mSpring.cancel(); 136 mSpring.addEndListener(endListener); 137 138 mProperty.switchToSpring(); 139 140 mSpring.setStartVelocity(velocity); 141 142 float startValue = end == 0 ? mValues[1] : mValues[0]; 143 float endValue = end == 0 ? mValues[0] : mValues[1]; 144 mSpring.setStartValue(startValue); 145 new Handler(Looper.getMainLooper()).postDelayed(() -> { 146 mSpring.animateToFinalPosition(endValue); 147 }, getStartDelay()); 148 } 149 150 @Override addListener(AnimatorListener listener)151 public void addListener(AnimatorListener listener) { 152 mListeners.add(listener); 153 } 154 getObjectAnimatorListeners()155 public ArrayList<AnimatorListener> getObjectAnimatorListeners() { 156 return mObjectAnimator.getListeners(); 157 } 158 159 @Override getListeners()160 public ArrayList<AnimatorListener> getListeners() { 161 return mListeners; 162 } 163 164 @Override removeAllListeners()165 public void removeAllListeners() { 166 mListeners.clear(); 167 } 168 169 @Override removeListener(AnimatorListener listener)170 public void removeListener(AnimatorListener listener) { 171 mListeners.remove(listener); 172 } 173 174 @Override addPauseListener(AnimatorPauseListener listener)175 public void addPauseListener(AnimatorPauseListener listener) { 176 mObjectAnimator.addPauseListener(listener); 177 } 178 179 @Override cancel()180 public void cancel() { 181 mObjectAnimator.cancel(); 182 mSpring.cancel(); 183 } 184 185 @Override end()186 public void end() { 187 mObjectAnimator.end(); 188 } 189 190 @Override getDuration()191 public long getDuration() { 192 return mObjectAnimator.getDuration(); 193 } 194 195 @Override getInterpolator()196 public TimeInterpolator getInterpolator() { 197 return mObjectAnimator.getInterpolator(); 198 } 199 200 @Override getStartDelay()201 public long getStartDelay() { 202 return mObjectAnimator.getStartDelay(); 203 } 204 205 @Override getTotalDuration()206 public long getTotalDuration() { 207 return mObjectAnimator.getTotalDuration(); 208 } 209 210 @Override isPaused()211 public boolean isPaused() { 212 return mObjectAnimator.isPaused(); 213 } 214 215 @Override isRunning()216 public boolean isRunning() { 217 return mObjectAnimator.isRunning(); 218 } 219 220 @Override isStarted()221 public boolean isStarted() { 222 return mObjectAnimator.isStarted(); 223 } 224 225 @Override pause()226 public void pause() { 227 mObjectAnimator.pause(); 228 } 229 230 @Override removePauseListener(AnimatorPauseListener listener)231 public void removePauseListener(AnimatorPauseListener listener) { 232 mObjectAnimator.removePauseListener(listener); 233 } 234 235 @Override resume()236 public void resume() { 237 mObjectAnimator.resume(); 238 } 239 240 @Override setDuration(long duration)241 public ValueAnimator setDuration(long duration) { 242 return mObjectAnimator.setDuration(duration); 243 } 244 245 @Override setInterpolator(TimeInterpolator value)246 public void setInterpolator(TimeInterpolator value) { 247 mObjectAnimator.setInterpolator(value); 248 } 249 250 @Override setStartDelay(long startDelay)251 public void setStartDelay(long startDelay) { 252 mObjectAnimator.setStartDelay(startDelay); 253 } 254 255 @Override setTarget(Object target)256 public void setTarget(Object target) { 257 mObjectAnimator.setTarget(target); 258 } 259 260 @Override start()261 public void start() { 262 mObjectAnimator.start(); 263 } 264 265 @Override setCurrentFraction(float fraction)266 public void setCurrentFraction(float fraction) { 267 mObjectAnimator.setCurrentFraction(fraction); 268 } 269 270 @Override setCurrentPlayTime(long playTime)271 public void setCurrentPlayTime(long playTime) { 272 mObjectAnimator.setCurrentPlayTime(playTime); 273 } 274 275 public static class SpringProperty<T> extends FloatProperty<T> { 276 277 boolean useSpring = false; 278 final FloatProperty<T> mProperty; 279 final SpringAnimation mSpring; 280 SpringProperty(FloatProperty<T> property, SpringAnimation spring)281 public SpringProperty(FloatProperty<T> property, SpringAnimation spring) { 282 super(property.getName()); 283 mProperty = property; 284 mSpring = spring; 285 } 286 switchToSpring()287 public void switchToSpring() { 288 useSpring = true; 289 } 290 291 @Override get(T object)292 public Float get(T object) { 293 return mProperty.get(object); 294 } 295 296 @Override setValue(T object, float progress)297 public void setValue(T object, float progress) { 298 if (useSpring) { 299 mSpring.animateToFinalPosition(progress); 300 } else { 301 mProperty.setValue(object, progress); 302 } 303 } 304 } 305 } 306