1 /*
2  * Copyright (C) 2015 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.settingslib.animation;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ObjectAnimator;
22 import android.animation.ValueAnimator;
23 import android.content.Context;
24 import android.view.RenderNodeAnimator;
25 import android.view.View;
26 import android.view.ViewPropertyAnimator;
27 import android.view.animation.AnimationUtils;
28 import android.view.animation.Interpolator;
29 
30 import com.android.internal.widget.LockPatternView;
31 import com.android.settingslib.R;
32 
33 /**
34  * A class to make nice appear transitions for views in a tabular layout.
35  */
36 public class AppearAnimationUtils implements AppearAnimationCreator<View> {
37 
38     public static final long DEFAULT_APPEAR_DURATION = 220;
39 
40     private final Interpolator mInterpolator;
41     private final float mStartTranslation;
42     private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
43     protected final float mDelayScale;
44     private final long mDuration;
45     protected RowTranslationScaler mRowTranslationScaler;
46     protected boolean mAppearing;
47 
AppearAnimationUtils(Context ctx)48     public AppearAnimationUtils(Context ctx) {
49         this(ctx, DEFAULT_APPEAR_DURATION,
50                 1.0f, 1.0f,
51                 AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
52     }
53 
AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, float delayScaleFactor, Interpolator interpolator)54     public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
55             float delayScaleFactor, Interpolator interpolator) {
56         mInterpolator = interpolator;
57         mStartTranslation = ctx.getResources().getDimensionPixelOffset(
58                 R.dimen.appear_y_translation_start) * translationScaleFactor;
59         mDelayScale = delayScaleFactor;
60         mDuration = duration;
61         mAppearing = true;
62     }
63 
startAnimation2d(View[][] objects, final Runnable finishListener)64     public void startAnimation2d(View[][] objects, final Runnable finishListener) {
65         startAnimation2d(objects, finishListener, this);
66     }
67 
startAnimation(View[] objects, final Runnable finishListener)68     public void startAnimation(View[] objects, final Runnable finishListener) {
69         startAnimation(objects, finishListener, this);
70     }
71 
startAnimation2d(T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)72     public <T> void startAnimation2d(T[][] objects, final Runnable finishListener,
73             AppearAnimationCreator<T> creator) {
74         AppearAnimationProperties properties = getDelays(objects);
75         startAnimations(properties, objects, finishListener, creator);
76     }
77 
startAnimation(T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)78     public <T> void startAnimation(T[] objects, final Runnable finishListener,
79             AppearAnimationCreator<T> creator) {
80         AppearAnimationProperties properties = getDelays(objects);
81         startAnimations(properties, objects, finishListener, creator);
82     }
83 
startAnimations(AppearAnimationProperties properties, T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)84     private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
85             final Runnable finishListener, AppearAnimationCreator<T> creator) {
86         if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
87             finishListener.run();
88             return;
89         }
90         for (int row = 0; row < properties.delays.length; row++) {
91             long[] columns = properties.delays[row];
92             long delay = columns[0];
93             Runnable endRunnable = null;
94             if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
95                 endRunnable = finishListener;
96             }
97             float translationScale = mRowTranslationScaler != null
98                     ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
99                     : 1f;
100             float translation = translationScale * mStartTranslation;
101             creator.createAnimation(objects[row], delay, mDuration,
102                     mAppearing ? translation : -translation,
103                     mAppearing, mInterpolator, endRunnable);
104         }
105     }
106 
startAnimations(AppearAnimationProperties properties, T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)107     private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
108             final Runnable finishListener, AppearAnimationCreator<T> creator) {
109         if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
110             finishListener.run();
111             return;
112         }
113         for (int row = 0; row < properties.delays.length; row++) {
114             long[] columns = properties.delays[row];
115             float translationScale = mRowTranslationScaler != null
116                     ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
117                     : 1f;
118             float translation = translationScale * mStartTranslation;
119             for (int col = 0; col < columns.length; col++) {
120                 long delay = columns[col];
121                 Runnable endRunnable = null;
122                 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
123                     endRunnable = finishListener;
124                 }
125                 creator.createAnimation(objects[row][col], delay, mDuration,
126                         mAppearing ? translation : -translation,
127                         mAppearing, mInterpolator, endRunnable);
128             }
129         }
130     }
131 
getDelays(T[] items)132     private <T> AppearAnimationProperties getDelays(T[] items) {
133         long maxDelay = -1;
134         mProperties.maxDelayColIndex = -1;
135         mProperties.maxDelayRowIndex = -1;
136         mProperties.delays = new long[items.length][];
137         for (int row = 0; row < items.length; row++) {
138             mProperties.delays[row] = new long[1];
139             long delay = calculateDelay(row, 0);
140             mProperties.delays[row][0] = delay;
141             if (items[row] != null && delay > maxDelay) {
142                 maxDelay = delay;
143                 mProperties.maxDelayColIndex = 0;
144                 mProperties.maxDelayRowIndex = row;
145             }
146         }
147         return mProperties;
148     }
149 
getDelays(T[][] items)150     private <T> AppearAnimationProperties getDelays(T[][] items) {
151         long maxDelay = -1;
152         mProperties.maxDelayColIndex = -1;
153         mProperties.maxDelayRowIndex = -1;
154         mProperties.delays = new long[items.length][];
155         for (int row = 0; row < items.length; row++) {
156             T[] columns = items[row];
157             mProperties.delays[row] = new long[columns.length];
158             for (int col = 0; col < columns.length; col++) {
159                 long delay = calculateDelay(row, col);
160                 mProperties.delays[row][col] = delay;
161                 if (items[row][col] != null && delay > maxDelay) {
162                     maxDelay = delay;
163                     mProperties.maxDelayColIndex = col;
164                     mProperties.maxDelayRowIndex = row;
165                 }
166             }
167         }
168         return mProperties;
169     }
170 
calculateDelay(int row, int col)171     protected long calculateDelay(int row, int col) {
172         return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
173     }
174 
getInterpolator()175     public Interpolator getInterpolator() {
176         return mInterpolator;
177     }
178 
getStartTranslation()179     public float getStartTranslation() {
180         return mStartTranslation;
181     }
182 
183     @Override
createAnimation(final View view, long delay, long duration, float translationY, boolean appearing, Interpolator interpolator, final Runnable endRunnable)184     public void createAnimation(final View view, long delay, long duration, float translationY,
185             boolean appearing, Interpolator interpolator, final Runnable endRunnable) {
186         if (view != null) {
187             view.setAlpha(appearing ? 0f : 1.0f);
188             view.setTranslationY(appearing ? translationY : 0);
189             Animator alphaAnim;
190             float targetAlpha =  appearing ? 1f : 0f;
191             if (view.isHardwareAccelerated()) {
192                 RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA,
193                         targetAlpha);
194                 alphaAnimRt.setTarget(view);
195                 alphaAnim = alphaAnimRt;
196             } else {
197                 alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha);
198             }
199             alphaAnim.setInterpolator(interpolator);
200             alphaAnim.setDuration(duration);
201             alphaAnim.setStartDelay(delay);
202             if (view.hasOverlappingRendering()) {
203                 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
204                 alphaAnim.addListener(new AnimatorListenerAdapter() {
205                     @Override
206                     public void onAnimationEnd(Animator animation) {
207                         view.setLayerType(View.LAYER_TYPE_NONE, null);
208                     }
209                 });
210             }
211             if (endRunnable != null) {
212                 alphaAnim.addListener(new AnimatorListenerAdapter() {
213                     @Override
214                     public void onAnimationEnd(Animator animation) {
215                         endRunnable.run();
216                     }
217                 });
218             }
219             alphaAnim.start();
220             startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY,
221                     interpolator);
222         }
223     }
224 
startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator)225     public static void startTranslationYAnimation(View view, long delay, long duration,
226             float endTranslationY, Interpolator interpolator) {
227         Animator translationAnim;
228         if (view.isHardwareAccelerated()) {
229             RenderNodeAnimator translationAnimRt = new RenderNodeAnimator(
230                     RenderNodeAnimator.TRANSLATION_Y, endTranslationY);
231             translationAnimRt.setTarget(view);
232             translationAnim = translationAnimRt;
233         } else {
234             translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
235                     view.getTranslationY(), endTranslationY);
236         }
237         translationAnim.setInterpolator(interpolator);
238         translationAnim.setDuration(duration);
239         translationAnim.setStartDelay(delay);
240         translationAnim.start();
241     }
242 
243     public class AppearAnimationProperties {
244         public long[][] delays;
245         public int maxDelayRowIndex;
246         public int maxDelayColIndex;
247     }
248 
249     public interface RowTranslationScaler {
getRowTranslationScale(int row, int numRows)250         float getRowTranslationScale(int row, int numRows);
251     }
252 }
253