blob: efac6bc3572ded4052ff03340e8fa7e3e7d291e3 [file] [log] [blame]
Jorim Jaggi15a77f72014-05-28 16:20:03 +02001/*
Jorim Jaggi56733532015-06-09 15:25:40 -07002 * Copyright (C) 2015 The Android Open Source Project
Jorim Jaggi15a77f72014-05-28 16:20:03 +02003 *
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
Jorim Jaggi56733532015-06-09 15:25:40 -070017package com.android.settingslib.animation;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020018
Jorim Jaggi613f55f2015-07-16 15:30:10 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020022import android.content.Context;
Jorim Jaggi613f55f2015-07-16 15:30:10 -070023import android.view.RenderNodeAnimator;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020024import android.view.View;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020025import android.view.animation.AnimationUtils;
26import android.view.animation.Interpolator;
27
Jorim Jaggi56733532015-06-09 15:25:40 -070028import com.android.settingslib.R;
29
Jorim Jaggi15a77f72014-05-28 16:20:03 +020030/**
31 * A class to make nice appear transitions for views in a tabular layout.
32 */
Selim Cinek30181972014-05-30 03:33:06 +020033public class AppearAnimationUtils implements AppearAnimationCreator<View> {
Jorim Jaggi15a77f72014-05-28 16:20:03 +020034
Jorim Jaggi98f85302014-08-07 17:45:04 +020035 public static final long DEFAULT_APPEAR_DURATION = 220;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020036
Jorim Jaggi98f85302014-08-07 17:45:04 +020037 private final Interpolator mInterpolator;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020038 private final float mStartTranslation;
Selim Cinek30181972014-05-30 03:33:06 +020039 private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
Selim Cinekf9c0e8f2014-11-11 13:41:02 +010040 protected final float mDelayScale;
Jorim Jaggi98f85302014-08-07 17:45:04 +020041 private final long mDuration;
Jorim Jaggi56733532015-06-09 15:25:40 -070042 protected RowTranslationScaler mRowTranslationScaler;
Selim Cinekf9c0e8f2014-11-11 13:41:02 +010043 protected boolean mAppearing;
Jorim Jaggi15a77f72014-05-28 16:20:03 +020044
45 public AppearAnimationUtils(Context ctx) {
Jorim Jaggi98f85302014-08-07 17:45:04 +020046 this(ctx, DEFAULT_APPEAR_DURATION,
Jorim Jaggi76a16232014-08-08 17:00:47 +020047 1.0f, 1.0f,
Jorim Jaggi98f85302014-08-07 17:45:04 +020048 AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
Jorim Jaggi15a77f72014-05-28 16:20:03 +020049 }
50
Jorim Jaggi98f85302014-08-07 17:45:04 +020051 public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
52 float delayScaleFactor, Interpolator interpolator) {
53 mInterpolator = interpolator;
Selim Cinek30181972014-05-30 03:33:06 +020054 mStartTranslation = ctx.getResources().getDimensionPixelOffset(
55 R.dimen.appear_y_translation_start) * translationScaleFactor;
56 mDelayScale = delayScaleFactor;
Jorim Jaggi98f85302014-08-07 17:45:04 +020057 mDuration = duration;
Selim Cinekf9c0e8f2014-11-11 13:41:02 +010058 mAppearing = true;
Selim Cinek30181972014-05-30 03:33:06 +020059 }
60
Jorim Jaggi56733532015-06-09 15:25:40 -070061 public void startAnimation2d(View[][] objects, final Runnable finishListener) {
62 startAnimation2d(objects, finishListener, this);
Selim Cinek30181972014-05-30 03:33:06 +020063 }
64
Selim Cinekf9c0e8f2014-11-11 13:41:02 +010065 public void startAnimation(View[] objects, final Runnable finishListener) {
66 startAnimation(objects, finishListener, this);
Jorim Jaggi98f85302014-08-07 17:45:04 +020067 }
68
Jorim Jaggi56733532015-06-09 15:25:40 -070069 public <T> void startAnimation2d(T[][] objects, final Runnable finishListener,
Selim Cinek30181972014-05-30 03:33:06 +020070 AppearAnimationCreator<T> creator) {
71 AppearAnimationProperties properties = getDelays(objects);
72 startAnimations(properties, objects, finishListener, creator);
73 }
74
Selim Cinekf9c0e8f2014-11-11 13:41:02 +010075 public <T> void startAnimation(T[] objects, final Runnable finishListener,
Jorim Jaggi98f85302014-08-07 17:45:04 +020076 AppearAnimationCreator<T> creator) {
77 AppearAnimationProperties properties = getDelays(objects);
78 startAnimations(properties, objects, finishListener, creator);
79 }
80
81 private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
82 final Runnable finishListener, AppearAnimationCreator<T> creator) {
83 if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
84 finishListener.run();
85 return;
86 }
87 for (int row = 0; row < properties.delays.length; row++) {
88 long[] columns = properties.delays[row];
89 long delay = columns[0];
90 Runnable endRunnable = null;
91 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
92 endRunnable = finishListener;
93 }
Jorim Jaggi56733532015-06-09 15:25:40 -070094 float translationScale = mRowTranslationScaler != null
95 ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
96 : 1f;
97 float translation = translationScale * mStartTranslation;
Jorim Jaggi98f85302014-08-07 17:45:04 +020098 creator.createAnimation(objects[row], delay, mDuration,
Jorim Jaggi56733532015-06-09 15:25:40 -070099 mAppearing ? translation : -translation,
100 mAppearing, mInterpolator, endRunnable);
Jorim Jaggi98f85302014-08-07 17:45:04 +0200101 }
102 }
103
Selim Cinek30181972014-05-30 03:33:06 +0200104 private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
Jorim Jaggi98f85302014-08-07 17:45:04 +0200105 final Runnable finishListener, AppearAnimationCreator<T> creator) {
Selim Cinek30181972014-05-30 03:33:06 +0200106 if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
107 finishListener.run();
108 return;
109 }
110 for (int row = 0; row < properties.delays.length; row++) {
111 long[] columns = properties.delays[row];
Jorim Jaggi56733532015-06-09 15:25:40 -0700112 float translationScale = mRowTranslationScaler != null
113 ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
114 : 1f;
115 float translation = translationScale * mStartTranslation;
Selim Cinek30181972014-05-30 03:33:06 +0200116 for (int col = 0; col < columns.length; col++) {
117 long delay = columns[col];
118 Runnable endRunnable = null;
119 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
120 endRunnable = finishListener;
121 }
Jorim Jaggi98f85302014-08-07 17:45:04 +0200122 creator.createAnimation(objects[row][col], delay, mDuration,
Selim Cinekf9c0e8f2014-11-11 13:41:02 +0100123 mAppearing ? translation : -translation,
124 mAppearing, mInterpolator, endRunnable);
Selim Cinek30181972014-05-30 03:33:06 +0200125 }
126 }
Jorim Jaggi98f85302014-08-07 17:45:04 +0200127 }
Selim Cinek30181972014-05-30 03:33:06 +0200128
Jorim Jaggi98f85302014-08-07 17:45:04 +0200129 private <T> AppearAnimationProperties getDelays(T[] items) {
130 long maxDelay = -1;
131 mProperties.maxDelayColIndex = -1;
132 mProperties.maxDelayRowIndex = -1;
133 mProperties.delays = new long[items.length][];
134 for (int row = 0; row < items.length; row++) {
135 mProperties.delays[row] = new long[1];
136 long delay = calculateDelay(row, 0);
137 mProperties.delays[row][0] = delay;
138 if (items[row] != null && delay > maxDelay) {
139 maxDelay = delay;
140 mProperties.maxDelayColIndex = 0;
141 mProperties.maxDelayRowIndex = row;
142 }
143 }
144 return mProperties;
Selim Cinek30181972014-05-30 03:33:06 +0200145 }
146
147 private <T> AppearAnimationProperties getDelays(T[][] items) {
Jorim Jaggi98f85302014-08-07 17:45:04 +0200148 long maxDelay = -1;
Selim Cinek30181972014-05-30 03:33:06 +0200149 mProperties.maxDelayColIndex = -1;
150 mProperties.maxDelayRowIndex = -1;
151 mProperties.delays = new long[items.length][];
152 for (int row = 0; row < items.length; row++) {
153 T[] columns = items[row];
154 mProperties.delays[row] = new long[columns.length];
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200155 for (int col = 0; col < columns.length; col++) {
156 long delay = calculateDelay(row, col);
Selim Cinek30181972014-05-30 03:33:06 +0200157 mProperties.delays[row][col] = delay;
158 if (items[row][col] != null && delay > maxDelay) {
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200159 maxDelay = delay;
Selim Cinek30181972014-05-30 03:33:06 +0200160 mProperties.maxDelayColIndex = col;
161 mProperties.maxDelayRowIndex = row;
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200162 }
163 }
164 }
Selim Cinek30181972014-05-30 03:33:06 +0200165 return mProperties;
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200166 }
167
Selim Cinekf9c0e8f2014-11-11 13:41:02 +0100168 protected long calculateDelay(int row, int col) {
Selim Cinek30181972014-05-30 03:33:06 +0200169 return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200170 }
171
Selim Cinek30181972014-05-30 03:33:06 +0200172 public Interpolator getInterpolator() {
Jorim Jaggi98f85302014-08-07 17:45:04 +0200173 return mInterpolator;
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200174 }
175
176 public float getStartTranslation() {
177 return mStartTranslation;
178 }
Selim Cinek30181972014-05-30 03:33:06 +0200179
180 @Override
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700181 public void createAnimation(final View view, long delay, long duration, float translationY,
182 boolean appearing, Interpolator interpolator, final Runnable endRunnable) {
Selim Cinek30181972014-05-30 03:33:06 +0200183 if (view != null) {
Selim Cinekf9c0e8f2014-11-11 13:41:02 +0100184 view.setAlpha(appearing ? 0f : 1.0f);
185 view.setTranslationY(appearing ? translationY : 0);
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700186 Animator alphaAnim;
187 float targetAlpha = appearing ? 1f : 0f;
188 if (view.isHardwareAccelerated()) {
189 RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA,
190 targetAlpha);
191 alphaAnimRt.setTarget(view);
192 alphaAnim = alphaAnimRt;
193 } else {
194 alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha);
195 }
196 alphaAnim.setInterpolator(interpolator);
197 alphaAnim.setDuration(duration);
198 alphaAnim.setStartDelay(delay);
Selim Cinek30181972014-05-30 03:33:06 +0200199 if (view.hasOverlappingRendering()) {
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700200 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
201 alphaAnim.addListener(new AnimatorListenerAdapter() {
202 @Override
203 public void onAnimationEnd(Animator animation) {
204 view.setLayerType(View.LAYER_TYPE_NONE, null);
205 }
206 });
Selim Cinek30181972014-05-30 03:33:06 +0200207 }
208 if (endRunnable != null) {
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700209 alphaAnim.addListener(new AnimatorListenerAdapter() {
210 @Override
211 public void onAnimationEnd(Animator animation) {
212 endRunnable.run();
213 }
214 });
Selim Cinek30181972014-05-30 03:33:06 +0200215 }
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700216 alphaAnim.start();
217 startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY,
218 interpolator);
Selim Cinek30181972014-05-30 03:33:06 +0200219 }
220 }
221
Jorim Jaggi613f55f2015-07-16 15:30:10 -0700222 public static void startTranslationYAnimation(View view, long delay, long duration,
223 float endTranslationY, Interpolator interpolator) {
224 Animator translationAnim;
225 if (view.isHardwareAccelerated()) {
226 RenderNodeAnimator translationAnimRt = new RenderNodeAnimator(
227 RenderNodeAnimator.TRANSLATION_Y, endTranslationY);
228 translationAnimRt.setTarget(view);
229 translationAnim = translationAnimRt;
230 } else {
231 translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
232 view.getTranslationY(), endTranslationY);
233 }
234 translationAnim.setInterpolator(interpolator);
235 translationAnim.setDuration(duration);
236 translationAnim.setStartDelay(delay);
237 translationAnim.start();
238 }
239
Selim Cinek30181972014-05-30 03:33:06 +0200240 public class AppearAnimationProperties {
241 public long[][] delays;
242 public int maxDelayRowIndex;
243 public int maxDelayColIndex;
244 }
Jorim Jaggi56733532015-06-09 15:25:40 -0700245
246 public interface RowTranslationScaler {
247 float getRowTranslationScale(int row, int numRows);
248 }
Jorim Jaggi15a77f72014-05-28 16:20:03 +0200249}