• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ObjectAnimator;
21 import android.util.FloatProperty;
22 
23 /**
24  * A mutable float which allows animating the value
25  */
26 public class AnimatedFloat {
27 
28     public static final FloatProperty<AnimatedFloat> VALUE =
29             new FloatProperty<AnimatedFloat>("value") {
30                 @Override
31                 public void setValue(AnimatedFloat obj, float v) {
32                     obj.updateValue(v);
33                 }
34 
35                 @Override
36                 public Float get(AnimatedFloat obj) {
37                     return obj.value;
38                 }
39             };
40 
41     private static final Runnable NO_OP = () -> { };
42 
43     private final Runnable mUpdateCallback;
44     private ObjectAnimator mValueAnimator;
45     // Only non-null when an animation is playing to this value.
46     private Float mEndValue;
47 
48     public float value;
49 
AnimatedFloat()50     public AnimatedFloat() {
51         this(NO_OP);
52     }
53 
AnimatedFloat(Runnable updateCallback)54     public AnimatedFloat(Runnable updateCallback) {
55         mUpdateCallback = updateCallback;
56     }
57 
AnimatedFloat(Runnable updateCallback, float initialValue)58     public AnimatedFloat(Runnable updateCallback, float initialValue) {
59         this(updateCallback);
60         value = initialValue;
61     }
62 
63     /**
64      * Returns an animation from the current value to the given value.
65      */
animateToValue(float end)66     public ObjectAnimator animateToValue(float end) {
67         return animateToValue(value, end);
68     }
69 
70     /**
71      * Returns an animation from the given start value to the given end value.
72      */
animateToValue(float start, float end)73     public ObjectAnimator animateToValue(float start, float end) {
74         cancelAnimation();
75         mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
76         mValueAnimator.addListener(new AnimatorListenerAdapter() {
77             @Override
78             public void onAnimationStart(Animator animator) {
79                 if (mValueAnimator == animator) {
80                     mEndValue = end;
81                 }
82             }
83 
84             @Override
85             public void onAnimationEnd(Animator animator) {
86                 if (mValueAnimator == animator) {
87                     mValueAnimator = null;
88                     mEndValue = null;
89                 }
90             }
91         });
92         return mValueAnimator;
93     }
94 
95     /**
96      * Changes the value and calls the callback.
97      * Note that the value can be directly accessed as well to avoid notifying the callback.
98      */
updateValue(float v)99     public void updateValue(float v) {
100         if (Float.compare(v, value) != 0) {
101             value = v;
102             mUpdateCallback.run();
103         }
104     }
105 
106     /**
107      * Cancels the animation.
108      */
cancelAnimation()109     public void cancelAnimation() {
110         if (mValueAnimator != null) {
111             mValueAnimator.cancel();
112         }
113     }
114 
115     /**
116      * Ends the animation.
117      */
finishAnimation()118     public void finishAnimation() {
119         if (mValueAnimator != null && mValueAnimator.isRunning()) {
120             mValueAnimator.end();
121         }
122     }
123 
isAnimating()124     public boolean isAnimating() {
125         return mValueAnimator != null;
126     }
127 
128     /**
129      * Returns whether we are currently animating, and the animation's end value matches the given.
130      */
isAnimatingToValue(float endValue)131     public boolean isAnimatingToValue(float endValue) {
132         return isAnimating() && mEndValue != null && mEndValue == endValue;
133     }
134 
135     /**
136      * Returns the remaining time of the existing animation (if any).
137      */
getRemainingTime()138     public long getRemainingTime() {
139         return isAnimating() && mValueAnimator.isRunning()
140                 ? Math.max(0, mValueAnimator.getDuration() - mValueAnimator.getCurrentPlayTime())
141                 : 0;
142     }
143 
144     /**
145      * Returns whether we are currently not animating, and the animation's value matches the given.
146      */
isSettledOnValue(float endValue)147     public boolean isSettledOnValue(float endValue) {
148         return !isAnimating() && value == endValue;
149     }
150 }
151