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 float t = MathUtils.constrain((fraction - mStartDelay) / mSpan, 0, 1); 57 if (mInterpolator != null) { 58 t = mInterpolator.getInterpolation(t); 59 } 60 if (t == mLastT) { 61 return; 62 } 63 if (mListener != null) { 64 if (t == 1) { 65 mListener.onAnimationAtEnd(); 66 } else if (t == 0) { 67 mListener.onAnimationAtStart(); 68 } else if (mLastT <= 0 || mLastT == 1) { 69 mListener.onAnimationStarted(); 70 } 71 mLastT = t; 72 } 73 for (int i = 0; i < mTargets.length; i++) { 74 mKeyframeSets[i].setValue(t, mTargets[i]); 75 } 76 } 77 78 private static final FloatProperty<TouchAnimator> POSITION = 79 new FloatProperty<TouchAnimator>("position") { 80 @Override 81 public void setValue(TouchAnimator touchAnimator, float value) { 82 touchAnimator.setPosition(value); 83 } 84 85 @Override 86 public Float get(TouchAnimator touchAnimator) { 87 return touchAnimator.mLastT; 88 } 89 }; 90 91 public static class ListenerAdapter implements Listener { 92 @Override onAnimationAtStart()93 public void onAnimationAtStart() { } 94 95 @Override onAnimationAtEnd()96 public void onAnimationAtEnd() { } 97 98 @Override onAnimationStarted()99 public void onAnimationStarted() { } 100 } 101 102 public interface Listener { 103 /** 104 * Called when the animator moves into a position of "0". Start and end delays are 105 * taken into account, so this position may cover a range of fractional inputs. 106 */ onAnimationAtStart()107 void onAnimationAtStart(); 108 109 /** 110 * Called when the animator moves into a position of "1". Start and end delays are 111 * taken into account, so this position may cover a range of fractional inputs. 112 */ onAnimationAtEnd()113 void onAnimationAtEnd(); 114 115 /** 116 * Called when the animator moves out of the start or end position and is in a transient 117 * state. 118 */ onAnimationStarted()119 void onAnimationStarted(); 120 } 121 122 public static class Builder { 123 private List<Object> mTargets = new ArrayList<>(); 124 private List<KeyframeSet> mValues = new ArrayList<>(); 125 126 private float mStartDelay; 127 private float mEndDelay; 128 private Interpolator mInterpolator; 129 private Listener mListener; 130 addFloat(Object target, String property, float... values)131 public Builder addFloat(Object target, String property, float... values) { 132 add(target, KeyframeSet.ofFloat(getProperty(target, property, float.class), values)); 133 return this; 134 } 135 addInt(Object target, String property, int... values)136 public Builder addInt(Object target, String property, int... values) { 137 add(target, KeyframeSet.ofInt(getProperty(target, property, int.class), values)); 138 return this; 139 } 140 add(Object target, KeyframeSet keyframeSet)141 private void add(Object target, KeyframeSet keyframeSet) { 142 mTargets.add(target); 143 mValues.add(keyframeSet); 144 } 145 getProperty(Object target, String property, Class<?> cls)146 private static Property getProperty(Object target, String property, Class<?> cls) { 147 if (target instanceof View) { 148 switch (property) { 149 case "translationX": 150 return View.TRANSLATION_X; 151 case "translationY": 152 return View.TRANSLATION_Y; 153 case "translationZ": 154 return View.TRANSLATION_Z; 155 case "alpha": 156 return View.ALPHA; 157 case "rotation": 158 return View.ROTATION; 159 case "x": 160 return View.X; 161 case "y": 162 return View.Y; 163 case "scaleX": 164 return View.SCALE_X; 165 case "scaleY": 166 return View.SCALE_Y; 167 } 168 } 169 if (target instanceof TouchAnimator && "position".equals(property)) { 170 return POSITION; 171 } 172 return Property.of(target.getClass(), cls, property); 173 } 174 setStartDelay(float startDelay)175 public Builder setStartDelay(float startDelay) { 176 mStartDelay = startDelay; 177 return this; 178 } 179 setEndDelay(float endDelay)180 public Builder setEndDelay(float endDelay) { 181 mEndDelay = endDelay; 182 return this; 183 } 184 setInterpolator(Interpolator intepolator)185 public Builder setInterpolator(Interpolator intepolator) { 186 mInterpolator = intepolator; 187 return this; 188 } 189 setListener(Listener listener)190 public Builder setListener(Listener listener) { 191 mListener = listener; 192 return this; 193 } 194 build()195 public TouchAnimator build() { 196 return new TouchAnimator(mTargets.toArray(new Object[mTargets.size()]), 197 mValues.toArray(new KeyframeSet[mValues.size()]), 198 mStartDelay, mEndDelay, mInterpolator, mListener); 199 } 200 } 201 202 private static abstract class KeyframeSet { 203 private final float mFrameWidth; 204 private final int mSize; 205 KeyframeSet(int size)206 public KeyframeSet(int size) { 207 mSize = size; 208 mFrameWidth = 1 / (float) (size - 1); 209 } 210 setValue(float fraction, Object target)211 void setValue(float fraction, Object target) { 212 int i = MathUtils.constrain((int) Math.ceil(fraction / mFrameWidth), 1, mSize - 1); 213 float amount = (fraction - mFrameWidth * (i - 1)) / mFrameWidth; 214 interpolate(i, amount, target); 215 } 216 interpolate(int index, float amount, Object target)217 protected abstract void interpolate(int index, float amount, Object target); 218 ofInt(Property property, int... values)219 public static KeyframeSet ofInt(Property property, int... values) { 220 return new IntKeyframeSet((Property<?, Integer>) property, values); 221 } 222 ofFloat(Property property, float... values)223 public static KeyframeSet ofFloat(Property property, float... values) { 224 return new FloatKeyframeSet((Property<?, Float>) property, values); 225 } 226 } 227 228 private static class FloatKeyframeSet<T> extends KeyframeSet { 229 private final float[] mValues; 230 private final Property<T, Float> mProperty; 231 FloatKeyframeSet(Property<T, Float> property, float[] values)232 public FloatKeyframeSet(Property<T, Float> property, float[] values) { 233 super(values.length); 234 mProperty = property; 235 mValues = values; 236 } 237 238 @Override interpolate(int index, float amount, Object target)239 protected void interpolate(int index, float amount, Object target) { 240 float firstFloat = mValues[index - 1]; 241 float secondFloat = mValues[index]; 242 mProperty.set((T) target, firstFloat + (secondFloat - firstFloat) * amount); 243 } 244 } 245 246 private static class IntKeyframeSet<T> extends KeyframeSet { 247 private final int[] mValues; 248 private final Property<T, Integer> mProperty; 249 IntKeyframeSet(Property<T, Integer> property, int[] values)250 public IntKeyframeSet(Property<T, Integer> property, int[] values) { 251 super(values.length); 252 mProperty = property; 253 mValues = values; 254 } 255 256 @Override interpolate(int index, float amount, Object target)257 protected void interpolate(int index, float amount, Object target) { 258 int firstFloat = mValues[index - 1]; 259 int secondFloat = mValues[index]; 260 mProperty.set((T) target, (int) (firstFloat + (secondFloat - firstFloat) * amount)); 261 } 262 } 263 } 264