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 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 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 64 public void startAnimation2d(View[][] objects, final Runnable finishListener) { 65 startAnimation2d(objects, finishListener, this); 66 } 67 68 public void startAnimation(View[] objects, final Runnable finishListener) { 69 startAnimation(objects, finishListener, this); 70 } 71 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 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 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 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 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 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 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 175 public Interpolator getInterpolator() { 176 return mInterpolator; 177 } 178 179 public float getStartTranslation() { 180 return mStartTranslation; 181 } 182 183 @Override 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 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 { 250 float getRowTranslationScale(int row, int numRows); 251 } 252 } 253