• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.qs;
16 
17 import android.util.FloatProperty;
18 import android.util.MathUtils;
19 import android.util.Property;
20 import android.view.View;
21 import android.view.animation.Interpolator;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 
26 /**
27  * Helper class, that handles similar properties as animators (delay, interpolators)
28  * but can have a float input as to the amount they should be in effect.  This allows
29  * easier animation that tracks input.
30  *
31  * All "delays" and "times" are as fractions from 0-1.
32  */
33 public class TouchAnimator {
34 
35     private final Object[] mTargets;
36     private final KeyframeSet[] mKeyframeSets;
37     private final float mStartDelay;
38     private final float mEndDelay;
39     private final float mSpan;
40     private final Interpolator mInterpolator;
41     private final Listener mListener;
42     private float mLastT = -1;
43 
TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets, float startDelay, float endDelay, Interpolator interpolator, Listener listener)44     private TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets,
45             float startDelay, float endDelay, Interpolator interpolator, Listener listener) {
46         mTargets = targets;
47         mKeyframeSets = keyframeSets;
48         mStartDelay = startDelay;
49         mEndDelay = endDelay;
50         mSpan = (1 - mEndDelay - mStartDelay);
51         mInterpolator = interpolator;
52         mListener = listener;
53     }
54 
setPosition(float fraction)55     public void setPosition(float fraction) {
56         if (Float.isNaN(fraction)) return;
57         float t = MathUtils.constrain((fraction - mStartDelay) / mSpan, 0, 1);
58         if (mInterpolator != null) {
59             t = mInterpolator.getInterpolation(t);
60         }
61         if (t == mLastT) {
62             return;
63         }
64         if (mListener != null) {
65             if (t == 1) {
66                 mListener.onAnimationAtEnd();
67             } else if (t == 0) {
68                 mListener.onAnimationAtStart();
69             } else if (mLastT <= 0 || mLastT == 1) {
70                 mListener.onAnimationStarted();
71             }
72             mLastT = t;
73         }
74         for (int i = 0; i < mTargets.length; i++) {
75             mKeyframeSets[i].setValue(t, mTargets[i]);
76         }
77     }
78 
79     private static final FloatProperty<TouchAnimator> POSITION =
80             new FloatProperty<TouchAnimator>("position") {
81         @Override
82         public void setValue(TouchAnimator touchAnimator, float value) {
83             touchAnimator.setPosition(value);
84         }
85 
86         @Override
87         public Float get(TouchAnimator touchAnimator) {
88             return touchAnimator.mLastT;
89         }
90     };
91 
92     public static class ListenerAdapter implements Listener {
93         @Override
onAnimationAtStart()94         public void onAnimationAtStart() { }
95 
96         @Override
onAnimationAtEnd()97         public void onAnimationAtEnd() { }
98 
99         @Override
onAnimationStarted()100         public void onAnimationStarted() { }
101     }
102 
103     public interface Listener {
104         /**
105          * Called when the animator moves into a position of "0". Start and end delays are
106          * taken into account, so this position may cover a range of fractional inputs.
107          */
onAnimationAtStart()108         void onAnimationAtStart();
109 
110         /**
111          * Called when the animator moves into a position of "1". Start and end delays are
112          * taken into account, so this position may cover a range of fractional inputs.
113          */
onAnimationAtEnd()114         void onAnimationAtEnd();
115 
116         /**
117          * Called when the animator moves out of the start or end position and is in a transient
118          * state.
119          */
onAnimationStarted()120         void onAnimationStarted();
121     }
122 
123     public static class Builder {
124         private List<Object> mTargets = new ArrayList<>();
125         private List<KeyframeSet> mValues = new ArrayList<>();
126 
127         private float mStartDelay;
128         private float mEndDelay;
129         private Interpolator mInterpolator;
130         private Listener mListener;
131 
addFloat(Object target, String property, float... values)132         public Builder addFloat(Object target, String property, float... values) {
133             add(target, KeyframeSet.ofFloat(getProperty(target, property, float.class), values));
134             return this;
135         }
136 
addInt(Object target, String property, int... values)137         public Builder addInt(Object target, String property, int... values) {
138             add(target, KeyframeSet.ofInt(getProperty(target, property, int.class), values));
139             return this;
140         }
141 
add(Object target, KeyframeSet keyframeSet)142         private void add(Object target, KeyframeSet keyframeSet) {
143             mTargets.add(target);
144             mValues.add(keyframeSet);
145         }
146 
getProperty(Object target, String property, Class<?> cls)147         private static Property getProperty(Object target, String property, Class<?> cls) {
148             if (target instanceof View) {
149                 switch (property) {
150                     case "translationX":
151                         return View.TRANSLATION_X;
152                     case "translationY":
153                         return View.TRANSLATION_Y;
154                     case "translationZ":
155                         return View.TRANSLATION_Z;
156                     case "alpha":
157                         return View.ALPHA;
158                     case "rotation":
159                         return View.ROTATION;
160                     case "x":
161                         return View.X;
162                     case "y":
163                         return View.Y;
164                     case "scaleX":
165                         return View.SCALE_X;
166                     case "scaleY":
167                         return View.SCALE_Y;
168                 }
169             }
170             if (target instanceof TouchAnimator && "position".equals(property)) {
171                 return POSITION;
172             }
173             return Property.of(target.getClass(), cls, property);
174         }
175 
setStartDelay(float startDelay)176         public Builder setStartDelay(float startDelay) {
177             mStartDelay = startDelay;
178             return this;
179         }
180 
setEndDelay(float endDelay)181         public Builder setEndDelay(float endDelay) {
182             mEndDelay = endDelay;
183             return this;
184         }
185 
setInterpolator(Interpolator intepolator)186         public Builder setInterpolator(Interpolator intepolator) {
187             mInterpolator = intepolator;
188             return this;
189         }
190 
setListener(Listener listener)191         public Builder setListener(Listener listener) {
192             mListener = listener;
193             return this;
194         }
195 
build()196         public TouchAnimator build() {
197             return new TouchAnimator(mTargets.toArray(new Object[mTargets.size()]),
198                     mValues.toArray(new KeyframeSet[mValues.size()]),
199                     mStartDelay, mEndDelay, mInterpolator, mListener);
200         }
201     }
202 
203     private static abstract class KeyframeSet {
204         private final float mFrameWidth;
205         private final int mSize;
206 
KeyframeSet(int size)207         public KeyframeSet(int size) {
208             mSize = size;
209             mFrameWidth = 1 / (float) (size - 1);
210         }
211 
setValue(float fraction, Object target)212         void setValue(float fraction, Object target) {
213             int i = MathUtils.constrain((int) Math.ceil(fraction / mFrameWidth), 1, mSize - 1);
214             float amount = (fraction - mFrameWidth * (i - 1)) / mFrameWidth;
215             interpolate(i, amount, target);
216         }
217 
interpolate(int index, float amount, Object target)218         protected abstract void interpolate(int index, float amount, Object target);
219 
ofInt(Property property, int... values)220         public static KeyframeSet ofInt(Property property, int... values) {
221             return new IntKeyframeSet((Property<?, Integer>) property, values);
222         }
223 
ofFloat(Property property, float... values)224         public static KeyframeSet ofFloat(Property property, float... values) {
225             return new FloatKeyframeSet((Property<?, Float>) property, values);
226         }
227     }
228 
229     private static class FloatKeyframeSet<T> extends KeyframeSet {
230         private final float[] mValues;
231         private final Property<T, Float> mProperty;
232 
FloatKeyframeSet(Property<T, Float> property, float[] values)233         public FloatKeyframeSet(Property<T, Float> property, float[] values) {
234             super(values.length);
235             mProperty = property;
236             mValues = values;
237         }
238 
239         @Override
interpolate(int index, float amount, Object target)240         protected void interpolate(int index, float amount, Object target) {
241             float firstFloat = mValues[index - 1];
242             float secondFloat = mValues[index];
243             mProperty.set((T) target, firstFloat + (secondFloat - firstFloat) * amount);
244         }
245     }
246 
247     private static class IntKeyframeSet<T> extends KeyframeSet {
248         private final int[] mValues;
249         private final Property<T, Integer> mProperty;
250 
IntKeyframeSet(Property<T, Integer> property, int[] values)251         public IntKeyframeSet(Property<T, Integer> property, int[] values) {
252             super(values.length);
253             mProperty = property;
254             mValues = values;
255         }
256 
257         @Override
interpolate(int index, float amount, Object target)258         protected void interpolate(int index, float amount, Object target) {
259             int firstFloat = mValues[index - 1];
260             int secondFloat = mValues[index];
261             mProperty.set((T) target, (int) (firstFloat + (secondFloat - firstFloat) * amount));
262         }
263     }
264 }
265