• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package com.android.server.power;
18 
19 import android.animation.ValueAnimator;
20 import android.util.IntProperty;
21 import android.view.Choreographer;
22 
23 /**
24  * A custom animator that progressively updates a property value at
25  * a given variable rate until it reaches a particular target value.
26  */
27 final class RampAnimator<T> {
28     private final T mObject;
29     private final IntProperty<T> mProperty;
30     private final Choreographer mChoreographer;
31 
32     private int mCurrentValue;
33     private int mTargetValue;
34     private int mRate;
35 
36     private boolean mAnimating;
37     private float mAnimatedValue; // higher precision copy of mCurrentValue
38     private long mLastFrameTimeNanos;
39 
40     private boolean mFirstTime = true;
41 
RampAnimator(T object, IntProperty<T> property)42     public RampAnimator(T object, IntProperty<T> property) {
43         mObject = object;
44         mProperty = property;
45         mChoreographer = Choreographer.getInstance();
46     }
47 
48     /**
49      * Starts animating towards the specified value.
50      *
51      * If this is the first time the property is being set, the value jumps
52      * directly to the target.
53      *
54      * @param target The target value.
55      * @param rate The convergence rate, in units per second.
56      * @return True if the target differs from the previous target.
57      */
animateTo(int target, int rate)58     public boolean animateTo(int target, int rate) {
59         // Immediately jump to the target the first time.
60         if (mFirstTime) {
61             mFirstTime = false;
62             mProperty.setValue(mObject, target);
63             mCurrentValue = target;
64             return true;
65         }
66 
67         // Adjust the rate based on the closest target.
68         // If a faster rate is specified, then use the new rate so that we converge
69         // more rapidly based on the new request.
70         // If a slower rate is specified, then use the new rate only if the current
71         // value is somewhere in between the new and the old target meaning that
72         // we will be ramping in a different direction to get there.
73         // Otherwise, continue at the previous rate.
74         if (!mAnimating
75                 || rate > mRate
76                 || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
77                 || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
78             mRate = rate;
79         }
80 
81         final boolean changed = (mTargetValue != target);
82         mTargetValue = target;
83 
84         // Start animating.
85         if (!mAnimating && target != mCurrentValue) {
86             mAnimating = true;
87             mAnimatedValue = mCurrentValue;
88             mLastFrameTimeNanos = System.nanoTime();
89             postCallback();
90         }
91 
92         return changed;
93     }
94 
postCallback()95     private void postCallback() {
96         mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null);
97     }
98 
99     private final Runnable mCallback = new Runnable() {
100         @Override // Choreographer callback
101         public void run() {
102             final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
103             final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
104                     * 0.000000001f;
105             mLastFrameTimeNanos = frameTimeNanos;
106 
107             // Advance the animated value towards the target at the specified rate
108             // and clamp to the target. This gives us the new current value but
109             // we keep the animated value around to allow for fractional increments
110             // towards the target.
111             final float scale = ValueAnimator.getDurationScale();
112             if (scale == 0) {
113                 // Animation off.
114                 mAnimatedValue = mTargetValue;
115             } else {
116                 final float amount = timeDelta * mRate / scale;
117                 if (mTargetValue > mCurrentValue) {
118                     mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
119                 } else {
120                     mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
121                 }
122             }
123             final int oldCurrentValue = mCurrentValue;
124             mCurrentValue = Math.round(mAnimatedValue);
125 
126             if (oldCurrentValue != mCurrentValue) {
127                 mProperty.setValue(mObject, mCurrentValue);
128             }
129 
130             if (mTargetValue != mCurrentValue) {
131                 postCallback();
132             } else {
133                 mAnimating = false;
134             }
135         }
136     };
137 }
138